]>
git.proxmox.com Git - extjs.git/blob - extjs/build/packages/amf/amf-debug.js
3 * @class Ext.data.amf.Encoder
4 * This class serializes data in the Action Message Format (AMF) format.
5 * It can write simple and complex objects, to be used in conjunction with an
6 * AMF-compliant server.
7 * To encode a byte array, first construct an Encoder, optionally setting the format:
9 * var encoder = Ext.create('Ext.data.amf.Encoder', {
13 * Then use the writer methods to out data to the :
15 * encoder.writeObject(1);
17 * And access the data through the #bytes property:
20 * You can also reset the class to start a new byte array:
24 * Current limitations:
25 * AMF3 format (format:3)
26 * - writeObject will write out XML object, not legacy XMLDocument objects. A
27 * writeXmlDocument method is provided for explicitly writing XMLDocument
29 * - Each object is written out explicitly, not using the reference tables
30 * supported by the AMF format. This means the function does NOT support
31 * circular reference objects.
32 * - Array objects: only the numbered indices and data will be written out.
33 * Associative values will be ignored.
34 * - Objects that aren't Arrays, Dates, Strings, Document (XML) or primitive
35 * values will be written out as anonymous objects with dynamic data.
36 * - There's no JavaScript equivalent to the ByteArray type in ActionScript,
37 * hence data will never be searialized as ByteArrays by the writeObject
38 * function. A writeByteArray method is provided for writing out ByteArray objects.
40 * AMF0 format (format:0)
41 * - Each object is written out explicitly, not using the reference tables
42 * supported by the AMF format. This means the function does NOT support
43 * circular reference objects.
44 * - Array objects: the function always writes an associative array (following
45 * the behavior of flex).
46 * - Objects that aren't Arrays, Dates, Strings, Document (XML) or primitive
47 * values will be written out as anonymous objects.
49 * For more information on working with AMF data please refer to the
50 * [AMF Guide](#/guide/amf).
52 Ext
. define ( 'Ext.data.amf.Encoder' , {
53 alias
: 'data.amf.Encoder' ,
58 * @property {Array} bytes
60 * The constructed byte array.
64 * Creates new Encoder.
65 * @param {Object} config Configuration options
67 constructor : function ( config
) {
68 this . initConfig ( config
);
72 * Reset all class states and starts a new empty array for encoding data.
73 * The method generates a new array for encoding, so it's safe to keep a
74 * reference to the old one.
80 * Sets the functions that will correctly serialize for the relevant
82 * @param {Number} protocol_version the protocol version to support
84 applyFormat : function ( protocol_version
) {
87 writeUndefined
: this . write0Undefined
,
88 writeNull
: this . write0Null
,
89 writeBoolean
: this . write0Boolean
,
90 writeNumber
: this . write0Number
,
91 writeString
: this . write0String
,
92 writeXml
: this . write0Xml
,
93 writeDate
: this . write0Date
,
94 writeArray
: this . write0Array
,
95 writeGenericObject
: this . write0GenericObject
98 writeUndefined
: this . write3Undefined
,
99 writeNull
: this . write3Null
,
100 writeBoolean
: this . write3Boolean
,
101 writeNumber
: this . write3Number
,
102 writeString
: this . write3String
,
103 writeXml
: this . write3Xml
,
104 writeDate
: this . write3Date
,
105 writeArray
: this . write3Array
,
106 writeGenericObject
: this . write3GenericObject
110 Ext
. apply ( this , funcs
);
111 return protocol_version
;
114 Ext
. raise ( "Unsupported AMF format: " + protocol_version
+ ". Only '3' (AMF3) is supported at this point." );
121 * Write the appropriate data items to the byte array. Supported types:
125 * - integer (if AMF3 - limited by 29-bit int, otherwise passed as double)
128 * - XML Document (identified by being instaneof Document. Can be generated with: new DOMParser()).parseFromString(xml, "text/xml");
129 * @param {Object} item A primitive or object to write to the stream
131 writeObject : function ( item
) {
132 var t
= typeof ( item
);
133 //Ext.log("Writing " + item + " of type " + t);
134 if ( t
=== "undefined" ) {
135 this . writeUndefined ();
136 } else if ( item
=== null ) {
137 // can't check type since typeof(null) returns "object"
139 } else if ( Ext
. isBoolean ( item
)) {
140 this . writeBoolean ( item
);
141 } else if ( Ext
. isString ( item
)) {
142 this . writeString ( item
);
143 } else if ( t
=== "number" || item
instanceof Number
) {
144 // Can't use Ext.isNumeric since it accepts strings as well
145 this . writeNumber ( item
);
146 } else if ( t
=== "object" ) {
147 // Figure out which object this is
148 if ( item
instanceof Date
) {
149 this . writeDate ( item
);
150 } else if ( Ext
. isArray ( item
)) {
151 // this won't catch associative arrays deserialized by the Packet class!
152 this . writeArray ( item
);
153 } else if ( this . isXmlDocument ( item
)) {
156 // Treat this as a generic object with name/value pairs of data.
157 this . writeGenericObject ( item
);
161 Ext
. log
. warn ( "AMF Encoder: Unknown item type " + t
+ " can't be written to stream: " + item
);
166 * Writes the AMF3 undefined value to the byte array.
169 write3Undefined : function () {
174 * Writes the AMF0 undefined value to the byte array.
177 write0Undefined : function () {
182 * Writes the AMF3 null value to the byte array.
185 write3Null : function () {
190 * Writes the AMF0 null value to the byte array.
193 write0Null : function () {
198 * Writes the appropriate AMF3 boolean value to the byte array.
199 * @param {boolean} item The value to write
202 write3Boolean : function ( item
) {
204 if ( typeof ( item
) !== "boolean" ) {
205 Ext
. log
. warn ( "Encoder: writeBoolean argument is not a boolean. Coercing." );
217 * Writes the appropriate AMF0 boolean value to the byte array.
218 * @param {boolean} item The value to write
221 write0Boolean : function ( item
) {
223 if ( typeof ( item
) !== "boolean" ) {
224 Ext
. log
. warn ( "Encoder: writeBoolean argument is not a boolean. Coercing." );
228 // AMF0 boolean marker
238 * Encodes a U29 int, returning a byte array with the encoded number.
239 * @param item - unsigned int value
242 encode29Int : function ( item
) {
244 // prepare the bytes, then send them to the array
253 // we have a special case if the number is 4-nibbles in U29 encoding
255 // last nibble is an 8-bit value
257 data
. unshift ( nibble
);
260 // get all the 7-bit parts ready
264 data
. unshift ( nibble
);
267 // now we need to mark each MSb of a 7-bit byte with a 1, except the absolute last one which has a 0.
268 // If there's an 8-bit byte, the 7-bit byte before it is marked with 1 as well.
269 for ( i
= 0 ; i
< data
. length
- 1 ; i
++) {
270 data
[ i
] = data
[ i
] | 128 ;
275 * Writes a numberic value to the byte array in AMF3 format
276 * @param item A native numeric value, Number instance or one of Infinity, -Infinity or NaN
279 write3Number : function ( item
) {
281 var maxInt
= 536870911 ,
282 minSignedInt
= - 268435455 ;
284 if ( typeof ( item
) !== "number" && !( item
instanceof Number
)) {
285 Ext
. log
. warn ( "Encoder: writeNumber argument is not numeric. Can't coerce." );
288 // switch to the primitive value for handling:
289 if ( item
instanceof Number
) {
290 item
= item
. valueOf ();
292 // First we need to determine if this is an integer or a float.
293 // AMF3 allows integers between -2^28 < item < 2^29, else they need to be passed as doubles.
294 if ( item
% 1 === 0 && item
>= minSignedInt
&& item
<= maxInt
) {
295 // The number has no decimal point and is within bounds. Let's encode it.
296 item
= item
& maxInt
;
297 // get an unsigned value to work with - we only care about 29 bits.
298 data
= this . encode29Int ( item
);
299 // And , mark it as an integer
301 // AMF3 integer marker
303 this . writeBytes ( data
);
305 data
= this . encodeDouble ( item
);
307 // AMF3 double marker
308 this . writeBytes ( data
);
312 * Writes a numberic value to the byte array in AMF0 format
313 * @param item A native numeric value, Number instance or one of Infinity, -Infinity or NaN
316 write0Number : function ( item
) {
319 if ( typeof ( item
) !== "number" && !( item
instanceof Number
)) {
320 Ext
. log
. warn ( "Encoder: writeNumber argument is not numeric. Can't coerce." );
323 // switch to the primitive value for handling:
324 if ( item
instanceof Number
) {
325 item
= item
. valueOf ();
327 //In AMF0 numbers are always serialized as double-float values.
328 data
= this . encodeDouble ( item
);
330 // AMF0 double marker
331 this . writeBytes ( data
);
334 * Convert a UTF 16 char to a UTF 8 char
335 * @param {Number} c char 16-bit code to convert
336 * @return {Array} byte array with the UTF 8 values
338 encodeUtf8Char : function ( c
) {
344 Ext
. raise ( "UTF 8 char out of bounds" );
352 // Multi-byte UTF8. Figure out how many bytes:
355 } else if ( c
<= 65535 ) {
360 // encode LSBs of value
363 for ( i
= 1 ; i
< b
; i
++) {
364 val
= ( c
& 63 ) | 128 ;
365 // lowest 6 bits of number, plus a flag to mark the byte
369 marker
= ( marker
>> 1 ) | 128 ;
371 // add one more bit for every byte
372 // the final byte is now ready, but we need to mark it to show how many other bytes follow
379 * Accepts a string and returns a byte array encoded in UTF-8
380 * @param {String} str String to encode
381 * @return {Array} byte array with string encoded in UTF-8 format
384 encodeUtf8String : function ( str
) {
387 for ( i
= 0 ; i
< str
. length
; i
++) {
388 var data
= this . encodeUtf8Char ( str
. charCodeAt ( i
));
389 Ext
. Array
. push ( utf8Data
, data
);
393 // quicker conversion, doesn't work in IE:
394 // utf8String = unescape(encodeURIComponent(str)),
397 * Encode the length of a UTF-8 string in AMF3 format.
398 * @param {Array} utf8Data byte array with the encoded data
399 * @return {Array} byte array encoding of length
403 encode3Utf8StringLen : function ( utf8Data
) {
404 var len
= utf8Data
. length
,
406 if ( len
<= 268435455 ) {
407 // String is under max allowed length in AMF3
408 // AMF3 strings use the LSb to mark whether it's a string reference or a string value. For now we only pass values:
412 // push length value to the array
413 data
= this . encode29Int ( len
);
416 Ext
. raise ( "UTF8 encoded string too long to serialize to AMF: " + len
);
422 * Write an AMF3 UTF-8 string to the byte array
423 * @param {String} item The string to write
426 write3String : function ( item
) {
428 if (! Ext
. isString ( item
)) {
429 Ext
. log
. warn ( "Encoder: writString argument is not a string." );
433 // special case for the empty string
435 // AMF3 string marker
437 } else // zero length string
439 // first encode string to UTF-8.
440 var utf8Data
= this . encodeUtf8String ( item
);
441 var lenData
= this . encode3Utf8StringLen ( utf8Data
);
442 // only write encoding once we got here, i.e. length of string is legal
444 // AMF3 string marker, only if we can actually write a string
445 this . writeBytes ( lenData
);
446 this . writeBytes ( utf8Data
);
450 * Encode 16- or 32-bit integers into big-endian (network order) bytes
451 * @param {Number} value the number to encode.
452 * @param {Number} byte_count 2 or 4 byte encoding
453 * @return {Array} byte array with encoded number
455 encodeXInt : function ( value
, byte_count
) {
458 for ( i
= 0 ; i
< byte_count
; i
++) {
459 data
. unshift ( value
& 255 );
465 * Write an AMF0 UTF-8 string to the byte array
466 * @param {String} item The string to write
469 write0String : function ( item
) {
471 if (! Ext
. isString ( item
)) {
472 Ext
. log
. warn ( "Encoder: writString argument is not a string." );
476 // special case for the empty string
478 // AMF0 short string marker
483 } else // zero length string
485 // first encode string to UTF-8.
486 var utf8Data
= this . encodeUtf8String ( item
);
489 if ( utf8Data
. length
<= 65535 ) {
493 lenData
= this . encodeXInt ( utf8Data
. length
, 2 );
498 lenData
= this . encodeXInt ( utf8Data
. length
, 4 );
500 this . writeByte ( encoding
);
501 // Approperiate AMF0 string marker
502 this . writeBytes ( lenData
);
503 this . writeBytes ( utf8Data
);
507 * Writes an XML document in AMF3 format.
508 * @param {Object} xml XML document (type Document typically)
509 * @param {number} amfType Either 0x07 or 0x0B - the AMF3 object type to use
512 write3XmlWithType : function ( xml
, amfType
) {
514 // We accept XML Documents, or strings
515 if ( amfType
!== 7 && amfType
!== 11 ) {
516 Ext
. raise ( "write XML with unknown AMF3 code: " + amfType
);
518 if (! this . isXmlDocument ( xml
)) {
519 Ext
. log
. warn ( "Encoder: write3XmlWithType argument is not an xml document." );
522 var xmlStr
= this . convertXmlToString ( xml
);
524 // special case for the empty string
525 this . writeByte ( amfType
);
528 } else // zero length string
530 // first encode string to UTF-8.
531 var utf8Data
= this . encodeUtf8String ( xmlStr
);
532 var lenData
= this . encode3Utf8StringLen ( utf8Data
);
533 // only write encoding once we got here, i.e. length of string is legal
534 this . writeByte ( amfType
);
535 // AMF3 XML marker, only if we can actually write the string
536 this . writeBytes ( lenData
);
537 this . writeBytes ( utf8Data
);
541 * Writes an Legacy XMLDocument (ActionScript Legacy XML object) in AMF3
542 * format. Must be called explicitly.
543 * The writeObject method will call writeXml and not writeXmlDocument.
544 * @param {Object} xml XML document (type Document typically) to write
546 write3XmlDocument : function ( xml
) {
547 this . write3XmlWithType ( xml
, 7 );
550 * Writes an XML object (ActionScript 3 new XML object) in AMF3 format.
551 * @param {Object} xml XML document (type Document typically) to write
554 write3Xml : function ( xml
) {
555 this . write3XmlWithType ( xml
, 11 );
558 * Writes an XMLDocument in AMF0 format.
559 * @param {Object} xml XML document (type Document typically) to write
562 write0Xml : function ( xml
) {
564 // We accept XML Documents, or strings
565 if (! this . isXmlDocument ( xml
)) {
566 Ext
. log
. warn ( "Encoder: write0Xml argument is not an xml document." );
569 var xmlStr
= this . convertXmlToString ( xml
);
572 // Always encoded as a long string
573 var utf8Data
= this . encodeUtf8String ( xmlStr
);
574 var lenData
= this . encodeXInt ( utf8Data
. length
, 4 );
575 this . writeBytes ( lenData
);
576 this . writeBytes ( utf8Data
);
579 * Writes a date in AMF3 format.
580 * @param {Date} date the date object
583 write3Date : function ( date
) {
585 if (!( date
instanceof Date
)) {
586 Ext
. raise ( "Serializing a non-date object as date: " + date
);
589 // For now, we don't use object references to just encode the date.
592 this . writeBytes ( this . encode29Int ( 1 ));
593 // mark this as a date value - we don't support references yet
594 this . writeBytes ( this . encodeDouble ( new Number ( date
)));
597 * Writes a date in AMF0 format.
598 * @param {Date} date the date object
601 write0Date : function ( date
) {
603 if (!( date
instanceof Date
)) {
604 Ext
. raise ( "Serializing a non-date object as date: " + date
);
607 // For now, we don't use object references to just encode the date.
610 this . writeBytes ( this . encodeDouble ( new Number ( date
)));
616 // placeholder for timezone, standard says to keep 0, flash actually writes data here
618 * Writes an array in AMF3 format. Only the ordered part of the array use handled.
619 * Unordered parts are ignored (e.g. a["hello"] will not be encoded).
620 * @param {Array} arr the array to serialize.
623 write3Array : function ( arr
) {
625 if (! Ext
. isArray ( arr
)) {
626 Ext
. raise ( "Serializing a non-array object as array: " + arr
);
628 if ( arr
. length
> 268435455 ) {
629 Ext
. raise ( "Array size too long to encode in AMF3: " + arr
. length
);
632 // For now, we don't use object references to just encode the array.
635 // encode ordered part of array's length
636 var len
= arr
. length
;
638 // right-most bit marks this as size
641 this . writeBytes ( this . encode29Int ( len
));
642 // The associative part of the array is ignored, so mark it as empty
644 // equivalent to an empty UTF-8 string
645 // now iterate over the array, writing each element
646 Ext
. each ( arr
, function ( x
) {
651 * Writes a key-value pair in AMF0 format.
652 * @param {String} key the name of the property
653 * @param {Object} value to write in AMF0 format
655 write0ObjectProperty : function ( key
, value
) {
656 if (!( key
instanceof String
) && ( typeof ( key
) !== "string" )) {
657 // coerce to a string
660 // first encode the key to a short UTF-8.
661 var utf8Data
= this . encodeUtf8String ( key
);
663 lenData
= this . encodeXInt ( utf8Data
. length
, 2 );
664 this . writeBytes ( lenData
);
665 this . writeBytes ( utf8Data
);
666 // and now write out the object
667 this . writeObject ( value
);
670 * Writes an associative array in AMF0 format.
671 * @param {Array} arr the array to serialize.
674 write0Array : function ( arr
) {
677 if (! Ext
. isArray ( arr
)) {
678 Ext
. raise ( "Serializing a non-array object as array: " + arr
);
681 /* This writes a strict array, but it seems Flex always writes out associative arrays, so mimic behavior
683 // For now, we don't use object references to just encode the array.
684 this.writeByte(0x0A); // AMF0 strict array marker
686 // encode ordered part of array's length
687 var len = arr.length;
688 this.writeBytes(this.encodeXInt(len, 4));
690 // now iterate over the array, writing each element
691 Ext.each(arr, function(x) {this.writeObject(x);}, this);
693 // Use ECMA (associative) array type
695 // AMF0 ECMA-array marker
696 // we need to know the length of the array before we write the serialized data
697 // to the array. Better to traverse it twice than to copy all the byte data afterwards
702 // Now write out the length of the array
703 this . writeBytes ( this . encodeXInt ( total
, 4 ));
704 // then write out the data
706 Ext
. Array
. push ( this . write0ObjectProperty ( key
, arr
[ key
]));
708 // And finally the object end marker
716 * Writes a strict-array in AMF0 format. Unordered parts are ignored (e.g.
717 * a["hello"] will not be encoded). This function is included for
718 * completeness and will never be called by writeObject.
719 * @param {Array} arr the array to serialize.
721 write0StrictArray : function ( arr
) {
723 if (! Ext
. isArray ( arr
)) {
724 Ext
. raise ( "Serializing a non-array object as array: " + arr
);
727 // For now, we don't use object references to just encode the array.
729 // AMF0 strict array marker
730 // encode ordered part of array's length
731 var len
= arr
. length
;
732 this . writeBytes ( this . encodeXInt ( len
, 4 ));
733 // now iterate over the array, writing each element
734 Ext
. each ( arr
, function ( x
) {
739 * Write a byte array in AMF3 format. This function is never called directly
740 * by writeObject since there's no way to distinguish a regular array from a
742 * @param {Array} arr the object to serialize.
744 write3ByteArray : function ( arr
) {
746 if (! Ext
. isArray ( arr
)) {
747 Ext
. raise ( "Serializing a non-array object as array: " + arr
);
749 if ( arr
. length
> 268435455 ) {
750 Ext
. raise ( "Array size too long to encode in AMF3: " + arr
. length
);
755 // for now no support for references, so just dump the length and data
756 // encode array's length
757 var len
= arr
. length
;
759 // right-most bit marks this as size
762 this . writeBytes ( this . encode29Int ( len
));
763 // and finally, dump the byte data
764 this . writeBytes ( arr
);
767 * Write an object to the byte array in AMF3 format.
768 * Since we don't have the class information form Flex, the object
769 * is written as an anonymous object.
770 * @param {Array} obj the object to serialize.
773 write3GenericObject : function ( obj
) {
776 if (! Ext
. isObject ( obj
)) {
777 Ext
. raise ( "Serializing a non-object object: " + obj
);
780 // For now, we don't use object references so just encode the object.
782 // AMF3 object marker
783 // The following 29-int is marked as follows (LSb to MSb) to signify a
785 // 1 - LSb marks an object value (1) or an object reference (0) which
786 // is not yet supported.
787 // 1 - trait values (1) or trait references (0) which are not supported
789 // 0 - AMF3 format (0) or externalizable, i.e. object handles own
790 // serialization (1) which is not supported.
791 // 1 - dynamic (1) or not dynamic (0) object which is not relevant since
792 // we pass all data as dynamic fields.
793 // The reset of the bits signify how many sealed traits the object has.
794 // we pass 0 since all data is passed as dynamic fields
797 this . writeByte ( oType
);
798 // Next we pass the class name, which is the empty string for anonymous
801 // Next are the sealed traits (of which we have none)
802 // And now the name / value pairs of dynamic fields
804 // Ensure that name is actually a string
805 var newName
= new String ( name
). valueOf ();
808 Ext
. raise ( "Can't encode non-string field name: " + name
);
811 var nameData
= ( this . encodeUtf8String ( name
));
812 this . writeBytes ( this . encode3Utf8StringLen ( name
));
813 this . writeBytes ( nameData
);
814 this . writeObject ( obj
[ name
]);
816 // And mark the end of the dynamic field section with the empty string
820 * Write an object to the byte array in AMF0 format.
821 * Since we don't have the class information form Flex, the object
822 * is written as an anonymous object.
823 * @param {Array} obj the object to serialize.
826 write0GenericObject : function ( obj
) {
827 var typed
, amfType
, key
;
829 if (! Ext
. isObject ( obj
)) {
830 Ext
. raise ( "Serializing a non-object object: " + obj
);
833 // For now, we don't use object references so just encode the object.
834 // if the object is typed, the ID changes and we need to send the type ahead of the data
835 typed
= !! obj
.$ flexType
;
836 amfType
= typed
? 16 : 3 ;
837 // typed vs. untyped object
838 this . writeByte ( amfType
);
839 // AMF0 object marker
840 // if typed, send object type
842 this . write0ShortUtf8String ( obj
.$ flexType
);
844 // then write out the data. There's no counter, but other than that it's the same as an ECMA array
846 if ( key
!= "$flexType" ) {
847 Ext
. Array
. push ( this . write0ObjectProperty ( key
, obj
[ key
]));
850 // And finally the object end marker
858 * Writes a byte to the byte array
859 * @param {number} b Byte to write to the array
862 writeByte : function ( b
) {
864 if ( b
< 0 || b
> 255 ) {
865 Ex
. Error
. raise ( 'ERROR: Value being written outside byte range: ' + b
);
868 Ext
. Array
. push ( this . bytes
, b
);
871 * Writes a byte array to the byte array
872 * @param {number} b Byte array to append to the array
875 writeBytes : function ( b
) {
878 if (! Ext
. isArray ( b
)) {
879 Ext
. raise ( "Decoder: writeBytes parameter is not an array: " + b
);
881 for ( i
= 0 ; i
< b
. length
; i
++) {
882 if ( b
[ i
] < 0 || b
[ i
] > 255 || ! Ext
. isNumber ( b
[ i
])) {
883 Ext
. raise ( "ERROR: Value " + i
+ " being written outside byte range: " + b
[ i
]);
887 Ext
. Array
. push ( this . bytes
, b
);
890 * Converts an XML Document object to a string.
891 * @param {Object} xml XML document (type Document typically) to convert
892 * @return {String} A string representing the document
895 convertXmlToString : function ( xml
) {
897 if ( window
. XMLSerializer
) {
898 // this is not IE, so:
899 str
= new window
. XMLSerializer (). serializeToString ( xml
);
901 //no XMLSerializer, might be an old version of IE
907 * Tries to determine if an object is an XML document
908 * @param {Object} item to identify
909 * @return {Boolean} true if it's an XML document, false otherwise
911 isXmlDocument : function ( item
) {
912 // We can't test if Document is defined since IE just throws an exception. Instead rely on the DOMParser object
913 if ( window
. DOMParser
) {
914 if ( Ext
. isDefined ( item
. doctype
)) {
918 // Otherwise, check if it has an XML field
919 if ( Ext
. isString ( item
. xml
)) {
920 // and we can get the xml
926 * The encodeDouble function is derived from code from the typedarray.js library by Linden Research, Inc.
929 Copyright (c) 2010, Linden Research, Inc.
931 Permission is hereby granted, free of charge, to any person obtaining a copy
932 of this software and associated documentation files (the "Software"), to deal
933 in the Software without restriction, including without limitation the rights
934 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
935 copies of the Software, and to permit persons to whom the Software is
936 furnished to do so, subject to the following conditions:
938 The above copyright notice and this permission notice shall be included in
939 all copies or substantial portions of the Software.
941 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
942 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
943 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
944 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
945 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
946 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
950 * Encodes an IEEE-754 double-precision number.
951 * @param {Number} num the number to encode
952 * @return {Array} byte array containing the encoded number
955 encodeDouble : function ( v
) {
959 var bias
= ( 1 << ( ebits
- 1 )) - 1 ,
960 s
, e
, f
, ln
, i
, bits
, str
,
962 // Precalculated values
993 // Compute sign, exponent, fraction
996 } else if ( v
=== Infinity
) {
998 } else if ( v
== - Infinity
) {
1001 // not a special case, so encode
1005 s
= ( 1 / v
=== - Infinity
) ? 1 : 0 ;
1009 if ( v
>= Math
. pow ( 2 , 1 - bias
)) {
1011 ln
= Math
. min ( Math
. floor ( Math
. log ( v
) / Math
. LN2
), bias
);
1013 f
= Math
. round ( v
* Math
. pow ( 2 , fbits
- ln
) - Math
. pow ( 2 , fbits
));
1017 f
= Math
. round ( v
/ Math
. pow ( 2 , 1 - bias
- fbits
));
1020 // Pack sign, exponent, fraction
1022 for ( i
= fbits
; i
; i
-= 1 ) {
1023 bits
. push ( f
% 2 ? 1 : 0 );
1024 f
= Math
. floor ( f
/ 2 );
1026 for ( i
= ebits
; i
; i
-= 1 ) {
1027 bits
. push ( e
% 2 ? 1 : 0 );
1028 e
= Math
. floor ( e
/ 2 );
1030 bits
. push ( s
? 1 : 0 );
1032 str
= bits
. join ( '' );
1035 while ( str
. length
) {
1036 data
. push ( parseInt ( str
. substring ( 0 , 8 ), 2 ));
1037 str
= str
. substring ( 8 );
1043 * Writes a short UTF8 string preceded with a 16-bit length.
1044 * @param {String} str the string to write
1046 write0ShortUtf8String : function ( str
) {
1047 var utf8Data
= this . encodeUtf8String ( str
),
1049 lenData
= this . encodeXInt ( utf8Data
. length
, 2 );
1050 this . writeBytes ( lenData
);
1051 this . writeBytes ( utf8Data
);
1054 * Writes an AMF packet to the byte array
1055 * @param {Array} headers the headers to serialize. Each item in the array
1056 * should be an object with three fields:
1057 * name, mustUnderstand, value
1058 * @param {Array} messages the messages to serialize. Each item in the array
1059 * should be an object with three fields:
1060 * targetUri, responseUri, body
1062 writeAmfPacket : function ( headers
, messages
) {
1065 if ( this . config
. format
!= 0 ) {
1066 Ext
. raise ( "Trying to write a packet on an AMF3 Encoder. Only AMF0 is supported!" );
1068 if (! Ext
. isArray ( headers
)) {
1069 Ext
. raise ( "headers is not an array: " + headers
);
1071 if (! Ext
. isArray ( messages
)) {
1072 Ext
. raise ( "messages is not an array: " + messages
);
1075 // Write Packet marker
1080 // AMF 0 version for this packet.
1081 // Write header count
1082 this . writeBytes ( this . encodeXInt ( headers
. length
, 2 ));
1083 // And actual headers
1084 for ( i
in headers
) {
1085 this . writeAmfHeader ( headers
[ i
]. name
, headers
[ i
]. mustUnderstand
, headers
[ i
]. value
);
1087 // Write message count
1088 this . writeBytes ( this . encodeXInt ( messages
. length
, 2 ));
1089 // And actual messages
1090 for ( i
in messages
) {
1091 this . writeAmfMessage ( messages
[ i
]. targetUri
, messages
[ i
]. responseUri
, messages
[ i
]. body
);
1095 * Write an AMF header to the byte array. AMF headers are always encoded in AMF0.
1096 * @param {String} headerName the header name
1097 * @param {Boolean} mustUnderstand true if the receiver must understand this header or else reject it, false otherwise
1098 * @param {Object} value the value to serialize. Must be an object that can be serialized by AMF
1101 writeAmfHeader : function ( headerName
, mustUnderstand
, value
) {
1103 if ( this . config
. format
!= 0 ) {
1104 Ext
. raise ( "Trying to write a header on an AMF3 Encoder. Only AMF0 is supported!" );
1106 if (! Ext
. isString ( headerName
)) {
1107 Ext
. raise ( "targetURI is not a string: " + targetUri
);
1109 if (( typeof ( mustUnderstand
) !== "boolean" ) && ! Ext
. isBoolean ( mustUnderstand
)) {
1110 Ext
. raise ( "mustUnderstand is not a boolean value: " + mustUnderstand
);
1113 // write header name
1114 this . write0ShortUtf8String ( headerName
);
1115 // write must understand byte
1116 var mu
= mustUnderstand
? 1 : 0 ;
1118 // next write the header length of -1 (undetermined) to the stream
1119 this . writeBytes ( this . encodeXInt (- 1 , 4 ));
1121 this . writeObject ( value
);
1124 * Writes an AMF message to the byte array. AMF messages are always encoded in AMF0.
1125 * @param {String} targetUri the class / method to call
1126 * @param {String} responseUri the response should call here
1127 * @param {Array} body the parameters to pass to the called method, wrapped in an array
1130 writeAmfMessage : function ( targetUri
, responseUri
, body
) {
1132 if ( this . config
. format
!= 0 ) {
1133 Ext
. raise ( "Trying to write a message on an AMF3 Encoder. Only AMF0 is supported!" );
1135 if (! Ext
. isString ( targetUri
)) {
1136 Ext
. raise ( "targetURI is not a string: " + targetUri
);
1138 if (! Ext
. isString ( responseUri
)) {
1139 Ext
. raise ( "targetURI is not a string: " + responseUri
);
1141 if (! Ext
. isArray ( body
)) {
1142 Ext
. raise ( "body is not an array: " + typeof ( body
));
1146 this . write0ShortUtf8String ( targetUri
);
1147 // write response URI
1148 this . write0ShortUtf8String ( responseUri
);
1149 // next write the message length of -1 (undetermined) to the stream
1150 this . writeBytes ( this . encodeXInt (- 1 , 4 ));
1151 // write the paramters
1152 this . write0StrictArray ( body
);
1158 * @class Ext.data.amf.Packet
1159 * This class represents an Action Message Format (AMF) Packet. It contains all
1160 * the logic required to decode an AMF Packet from a byte array.
1161 * To decode a Packet, first construct a Packet:
1163 * var packet = Ext.create('Ext.data.amf.Packet');
1165 * Then use the decode method to decode an AMF byte array:
1167 * packet.decode(bytes);
1169 * where "bytes" is a Uint8Array or an array of numbers representing the binary
1172 * To access the decoded data use the #version, #headers, and #messages properties:
1174 * console.log(packet.version, packet.headers, packet.messages);
1176 * For more information on working with AMF data please refer to the
1177 * [AMF Guide](#/guide/amf).
1179 Ext
. define ( 'Ext.data.amf.Packet' , function () {
1180 var twoPowN52
= Math
. pow ( 2 , - 52 ),
1181 twoPow8
= Math
. pow ( 2 , 8 ),
1183 bytes
, strings
, objects
, traits
;
1186 * @property {Array} headers
1188 * The decoded headers. Each header has the following properties:
1191 * The header name. Typically identifies a remote operation or method to
1192 * be invoked by this context header.
1193 * - `mustUnderstand`: Boolean
1194 * If `true` this flag instructs the endpoint to abort and generate an
1195 * error if the header is not understood.
1196 * - `byteLength`: Number
1197 * If the byte-length of a header is known it can be specified to optimize
1198 * memory allocation at the remote endpoint.
1203 * @property {Array} messages
1205 * The decoded messages. Each message has the following properties:
1207 * - `targetURI`: String
1208 * Describes which operation, function, or method is to be remotely
1210 * - `responseURI`: String
1211 * A unique operation name
1212 * - `byteLength`: Number
1213 * Optional byte-length of the message body
1218 * @property {Number} version
1220 * The AMF version number (0 or 3)
1223 * Mapping of AMF data types to the names of the methods responsible for
1232 2 : 'readAmf0String' ,
1233 3 : 'readAmf0Object' ,
1238 10 : 'readStrictArray' ,
1240 12 : 'readLongString' ,
1241 13 : 'readUnsupported' ,
1243 16 : 'readTypedObject'
1253 6 : 'readAmf3String' ,
1257 10 : 'readAmf3Object' ,
1263 * Decodes an AMF btye array and sets the decoded data as the
1264 * Packet's #version, #headers, and #messages properties
1265 * @param {Array} byteArray A byte array containing the encoded AMF data.
1266 * @return {Ext.data.amf.Packet} this AMF Packet
1268 decode : function ( byteArray
) {
1270 headers
= me
. headers
= [],
1271 messages
= me
. messages
= [],
1272 headerCount
, messageCount
;
1274 bytes
= me
. bytes
= byteArray
;
1275 // The strings array holds references to all of the deserialized
1276 // AMF3 strings for a given header value or message body so that
1277 // repeat instances of the same string can be deserialized by
1279 strings
= me
. strings
= [];
1280 // The objects array holds references to deserialized objects so
1281 // that repeat occurrences of the same object instance in the byte
1282 // array can be deserialized by reference.
1283 // If version is AMF0 this array holds anonymous objects, typed
1284 // objects, arrays, and ecma-arrays.
1285 // If version is AMF3 this array holds instances of Object, Array, XML,
1286 // XMLDocument, ByteArray, Date, and instances of user defined Classes
1287 objects
= me
. objects
= [];
1288 // The traits array holds references to the "traits" (the
1289 // characteristics of objects that define a strong type such as the
1290 // class name and public member names) of deserialized AMF3 objects
1291 // so that if they are repeated they can be deserialized by reference.
1292 traits
= me
. traits
= [];
1293 // The first two bytes of an AMF packet contain the AMF version
1294 // as an unsigned 16 bit integer.
1295 me
. version
= me
. readUInt ( 2 );
1296 // the next 2 bytes contain the header count
1297 for ( headerCount
= me
. readUInt ( 2 ); headerCount
--; ) {
1299 name
: me
. readAmf0String (),
1300 mustUnderstand
: me
. readBoolean (),
1301 byteLength
: me
. readUInt ( 4 ),
1302 value
: me
. readValue ()
1304 // reset references (reference indices are local to each header)
1305 strings
= me
. strings
= [];
1306 objects
= me
. objects
= [];
1307 traits
= me
. traits
= [];
1309 // The 2 bytes immediately after the header contain the message count.
1310 for ( messageCount
= me
. readUInt ( 2 ); messageCount
--; ) {
1312 targetURI
: me
. readAmf0String (),
1313 responseURI
: me
. readAmf0String (),
1314 byteLength
: me
. readUInt ( 4 ),
1315 body
: me
. readValue ()
1317 // reset references (reference indices are local to each message)
1318 strings
= me
. strings
= [];
1319 objects
= me
. objects
= [];
1320 traits
= me
. traits
= [];
1322 // reset the pointer
1324 // null the bytes array and reference arrays to free up memory.
1325 bytes
= strings
= objects
= traits
= me
. bytes
= me
. strings
= me
. objects
= me
. traits
= null ;
1329 * Decodes an AMF3 byte array and that has one value and returns it.
1330 * Note: Destroys previously stored data in this Packet.
1331 * @param {Array} byteArray A byte array containing the encoded AMF data.
1332 * @return {Object} the decoded object
1334 decodeValue : function ( byteArray
) {
1336 bytes
= me
. bytes
= byteArray
;
1337 // reset the pointer
1339 // The first two bytes of an AMF packet contain the AMF version
1340 // as an unsigned 16 bit integer.
1342 // The strings array holds references to all of the deserialized
1343 // AMF3 strings for a given header value or message body so that
1344 // repeat instances of the same string can be deserialized by
1346 strings
= me
. strings
= [];
1347 // The objects array holds references to deserialized objects so
1348 // that repeat occurrences of the same object instance in the byte
1349 // array can be deserialized by reference.
1350 // If version is AMF0 this array holds anonymous objects, typed
1351 // objects, arrays, and ecma-arrays.
1352 // If version is AMF3 this array holds instances of Object, Array, XML,
1353 // XMLDocument, ByteArray, Date, and instances of user defined Classes
1354 objects
= me
. objects
= [];
1355 // The traits array holds references to the "traits" (the
1356 // characteristics of objects that define a strong type such as the
1357 // class name and public member names) of deserialized AMF3 objects
1358 // so that if they are repeated they can be deserialized by reference.
1359 traits
= me
. traits
= [];
1360 // read one value and return it
1361 return me
. readValue ();
1364 * Parses an xml string and returns an xml document
1366 * @param {String} xml
1368 parseXml : function ( xml
) {
1370 if ( window
. DOMParser
) {
1371 doc
= ( new DOMParser ()). parseFromString ( xml
, "text/xml" );
1373 doc
= new ActiveXObject ( "Microsoft.XMLDOM" );
1379 * Reads an AMF0 date from the byte array
1382 readAmf0Date : function () {
1383 var date
= new Date ( this . readDouble ());
1384 // An AMF0 date type ends with a 16 bit integer time-zone, but
1385 // according to the spec time-zone is "reserved, not supported,
1386 // should be set to 0x000".
1388 // discard the time zone
1392 * Reads an AMF0 Object from the byte array
1395 readAmf0Object : function ( obj
) {
1399 // add the object to the objects array so that the AMF0 reference
1400 // type decoder can refer to it by index if needed.
1402 // An AMF0 object consists of a series of string keys and variable-
1403 // type values. The end of the series is marked by an empty string
1404 // followed by the object-end marker (9).
1405 while (( key
= me
. readAmf0String ()) || bytes
[ pos
] !== 9 ) {
1406 obj
[ key
] = me
. readValue ();
1408 // move the pointer past the object-end marker
1413 * Reads an AMF0 string from the byte array
1416 readAmf0String : function () {
1417 // AMF0 strings begin with a 16 bit byte-length header.
1418 return this . readUtf8 ( this . readUInt ( 2 ));
1420 readAmf0Xml : function () {
1421 return this . parseXml ( this . readLongString ());
1423 readAmf3Array : function () {
1425 header
= me
. readUInt29 (),
1426 count
, key
, array
, i
;
1427 // AMF considers Arrays in two parts, the dense portion and the
1428 // associative portion. The binary representation of the associative
1429 // portion consists of name/value pairs (potentially none) terminated
1430 // by an empty string. The binary representation of the dense portion
1431 // is the size of the dense portion (potentially zero) followed by an
1432 // ordered list of values (potentially none).
1434 // If the first (low) bit is a 1 read an array instance. The
1435 // remaining 1-28 bits are used to encode the length of the
1436 // dense portion of the array.
1437 count
= ( header
>> 1 );
1438 // First read the associative portion of the array (if any). If
1439 // there is an associative portion, the array will be read as a
1440 // javascript object, otherwise it will be a javascript array.
1441 key
= me
. readAmf3String ();
1443 // First key is not an empty string - this is an associative
1444 // array. Read keys and values from the byte array until
1445 // we get to an empty string key
1447 objects
. push ( array
);
1449 array
[ key
] = me
. readValue ();
1450 } while (( key
= me
. readAmf3String ()));
1451 // The dense portion of the array is then read into the
1452 // associative object, keyed by ordinal index.
1453 for ( i
= 0 ; i
< count
; i
++) {
1454 array
[ i
] = me
. readValue ();
1457 // First key is an empty string - this is an array with
1460 objects
. push ( array
);
1461 for ( i
= 0 ; i
< count
; i
++) {
1462 array
. push ( me
. readValue ());
1466 // If the first (low) bit is a 0 read an array reference. The
1467 // remaining 1-28 bits are used to encode the reference index
1468 array
= objects
[ header
>> 1 ];
1473 * Reads an AMF3 date from the byte array
1476 readAmf3Date : function () {
1478 header
= me
. readUInt29 (),
1481 // If the first (low) bit is a 1, this is a date instance.
1482 date
= new Date ( me
. readDouble ());
1485 // If the first (low) bit is a 0, this is a date reference.
1486 // The remaining 1-28 bits encode the reference index
1487 date
= objects
[ header
>> 1 ];
1492 * Reads an AMF3 object from the byte array
1495 readAmf3Object : function () {
1497 header
= me
. readUInt29 (),
1499 headerLast3Bits
, memberCount
, className
, dynamic
, objectTraits
, obj
, key
, klass
, i
;
1500 // There are 3 different types of object headers, distinguishable
1501 // by the 1-3 least significant bits. All object instances have
1502 // a 1 in the low bit position, while references have a 0:
1504 // 0 : object reference
1509 // first (low) bit of 1, denotes an encoded object instance
1510 // The next string is the class name.
1511 headerLast3Bits
= ( header
& 7 );
1512 if ( headerLast3Bits
=== 3 ) {
1513 // If the 3 least significant bits of the header are "011"
1514 // then trait information follows.
1515 className
= me
. readAmf3String ();
1516 // A 1 in the header's 4th least significant byte position
1517 // indicates that dynamic members may follow the sealed
1519 dynamic
= !!( header
& 8 );
1520 // Shift off the 4 least significant bits, and the remaining
1521 // 1-25 bits encode the number of sealed member names. Read
1522 // as many strings from the byte array as member names.
1523 memberCount
= ( header
>> 4 );
1524 for ( i
= 0 ; i
< memberCount
; i
++) {
1525 members
. push ( me
. readAmf3String ());
1528 className
: className
,
1532 // An objects traits are cached in the traits array enabling
1533 // the traits for a given class to only be encoded once for
1534 // a series of instances.
1535 traits
. push ( objectTraits
);
1536 } else if (( header
& 3 ) === 1 ) {
1537 // If the 2 least significant bits are "01", then a traits
1538 // reference follows. The remaining 1-27 bits are used
1539 // to encode the trait reference index.
1540 objectTraits
= traits
[ header
>> 2 ];
1541 className
= objectTraits
. className
;
1542 dynamic
= objectTraits
. dynamic
;
1543 members
= objectTraits
. members
;
1544 memberCount
= members
. length
;
1545 } else if ( headerLast3Bits
=== 7 ) {}
1546 // if the 3 lease significant bits are "111" then
1547 // externalizable trait data follows
1548 // TODO: implement externalizable traits
1550 klass
= Ext
. ClassManager
. getByAlias ( 'amf.' + className
);
1551 obj
= klass
? new klass () : {
1552 $ className
: className
1558 // read the sealed member values
1559 for ( i
= 0 ; i
< memberCount
; i
++) {
1560 obj
[ members
[ i
]] = me
. readValue ();
1563 // If the dynamic flag is set, dynamic members may follow
1564 // the sealed members. Read key/value pairs until we
1565 // encounter an empty string key signifying the end of the
1567 while (( key
= me
. readAmf3String ())) {
1568 obj
[ key
] = me
. readValue ();
1571 // finally, check if we need to convert this class
1572 if ((! klass
) && this . converters
[ className
]) {
1573 obj
= this . converters
[ className
]( obj
);
1576 // If the first (low) bit of the header is a 0, this is an
1577 // object reference. The remaining 1-28 significant bits are
1578 // used to encode an object reference index.
1579 obj
= objects
[ header
>> 1 ];
1584 * Reads an AMF3 string from the byte array
1587 readAmf3String : function () {
1589 header
= me
. readUInt29 (),
1592 // If the first (low) bit is a 1, this is a string literal.
1593 // Discard the low bit. The remaining 1-28 bits are used to
1594 // encode the string's byte-length.
1595 value
= me
. readUtf8 ( header
>> 1 );
1597 // the emtpy string is never encoded by reference
1598 strings
. push ( value
);
1602 // If the first (low) bit is a 0, this is a string reference.
1603 // Discard the low bit, then look up and return the reference
1604 // from the strings array using the remaining 1-28 bits as the
1606 return strings
[ header
>> 1 ];
1610 * Reads an AMF3 XMLDocument type or XML type from the byte array
1613 readAmf3Xml : function () {
1615 header
= me
. readUInt29 (),
1618 // If the first (low) bit is a 1, this is an xml instance. The
1619 // remaining 1-28 bits encode the byte-length of the xml string.
1620 doc
= me
. parseXml ( me
. readUtf8 ( header
>> 1 ));
1623 // if the first (low) bit is a 1, this is an xml reference. The
1624 // remaining 1-28 bits encode the reference index.
1625 doc
= objects
[ header
>> 1 ];
1630 * Reads an AMF0 boolean from the byte array
1633 readBoolean : function () {
1634 return !! bytes
[ pos
++];
1637 * Reads an AMF3 ByteArray type from the byte array
1640 readByteArray : function () {
1641 var header
= this . readUInt29 (),
1644 // If the first (low) bit is a 1, this is a ByteArray instance.
1645 // The remaining 1-28 bits encode the ByteArray's byte-length.
1646 end
= pos
+ ( header
>> 1 );
1647 // Depending on the browser, "bytes" may be either a Uint8Array
1648 // or an Array. Uint8Arrays don't have Array methods, so
1649 // we have to use Array.prototype.slice to get the byteArray
1650 byteArray
= Array
. prototype . slice
. call ( bytes
, pos
, end
);
1651 objects
. push ( byteArray
);
1652 // move the pointer to the first byte after the byteArray that
1656 // if the first (low) bit is a 1, this is a ByteArray reference.
1657 // The remaining 1-28 bits encode the reference index.
1658 byteArray
= objects
[ header
>> 1 ];
1663 * Reads a IEEE 754 double-precision binary floating-point number
1666 readDouble : function () {
1667 var byte1
= bytes
[ pos
++],
1668 byte2
= bytes
[ pos
++],
1669 // the first bit of byte1 is the sign (0 = positive, 1 = negative.
1670 // We read this bit by shifting the 7 least significant bits of
1671 // byte1 off to the right.
1672 sign
= ( byte1
>> 7 ) ? - 1 : 1 ,
1673 // the exponent takes up the next 11 bits.
1674 exponent
= // extract the 7 least significant bits from byte1 and then
1675 // shift them left by 4 bits to make room for the 4 remaining
1677 ((( byte1
& 127 ) << 4 ) | // add the 4 most significant bits from byte 2 to complete
1680 // the remaining 52 bits make up the significand. read the 4
1681 // least significant bytes of byte 2 to begin the significand
1682 significand
= ( byte2
& 15 ),
1683 // The most significant bit of the significand is always 1 for
1684 // a normalized number, therefore it is not stored. This bit is
1685 // referred to as the "hidden bit". The true bit width of the
1686 // significand is 53 if you include the hidden bit. An exponent
1687 // of 0 indicates that this is a subnormal number, and subnormal
1688 // numbers always have a 0 hidden bit.
1689 hiddenBit
= exponent
? 1 : 0 ,
1691 // The operands of all bitwise operators in javascript are converted
1692 // to signed 32 bit integers. It is therefore impossible to construct
1693 // the 52 bit significand by repeatedly shifting its bits and then
1694 // bitwise OR-ing the result with the the next byte. To work around
1695 // this issue, we repeatedly multiply the significand by 2^8 which
1696 // produces the same result as (significand << 8), then we add the
1697 // next byte, which has the same result as a bitwise OR.
1699 significand
= ( significand
* twoPow8
) + bytes
[ pos
++];
1703 // if both exponent and significand are 0, the number is 0
1706 // If the exponent is 0, but the significand is not 0, this
1707 // is a subnormal number. Subnormal numbers are encoded with a
1708 // biased exponent of 0, but are interpreted with the value of
1709 // the smallest allowed exponent, which is one greater.
1712 // 0x7FF (2047) is a special exponent value that represents infinity
1713 // if the significand is 0, and NaN if the significand is not 0
1714 if ( exponent
=== 2047 ) {
1715 return significand
? NaN
: ( Infinity
* sign
);
1717 return sign
* // The exponent is encoded using an offset binary
1718 // representation with the zero offset being 0x3FF (1023),
1719 // so we have to subtract 0x3FF to get the true exponent
1720 Math
. pow ( 2 , exponent
- 1023 ) * // convert the significand to its decimal value by multiplying
1721 // it by 2^52 and then add the hidden bit
1722 ( hiddenBit
+ twoPowN52
* significand
);
1725 * Reads an AMF0 ECMA Array from the byte array
1728 readEcmaArray : function () {
1729 // An ecma array type is encoded exactly like an anonymous object
1730 // with the exception that it has a 32 bit "count" at the beginning.
1731 // We handle emca arrays by just throwing away the count and then
1732 // letting the object decoder handle the rest.
1734 return this . readAmf0Object ();
1737 * Returns false. Used for reading the false type
1740 readFalse : function () {
1744 * Reads a long string (longer than 65535 bytes) from the byte array
1747 readLongString : function () {
1748 // long strings begin with a 32 bit byte-length header.
1749 return this . readUtf8 ( this . readUInt ( 4 ));
1752 * Returns null. Used for reading the null type
1755 readNull : function () {
1759 * Reads a reference from the byte array. Reference types are used to
1760 * avoid duplicating data if the same instance of a complex object (which
1761 * is defined in AMF0 as an anonymous object, typed object, array, or
1762 * ecma-array) is included in the data more than once.
1765 readReference : function () {
1766 // a reference type contains a single 16 bit integer that represents
1767 // the index of an already deserialized object in the objects array
1768 return objects
[ this . readUInt ( 2 )];
1771 * Reads an AMF0 strict array (an array with ordinal indices)
1774 readStrictArray : function () {
1776 len
= me
. readUInt ( 4 ),
1780 arr
. push ( me
. readValue ());
1785 * Returns true. Used for reading the true type
1788 readTrue
: Ext
. returnTrue
,
1790 * Reads an AMF0 typed object from the byte array
1793 readTypedObject : function () {
1795 className
= me
. readAmf0String (),
1796 klass
, instance
, modified
;
1797 klass
= Ext
. ClassManager
. getByAlias ( 'amf.' + className
);
1798 instance
= klass
? new klass () : {
1799 $ className
: className
1801 // if there is no klass, mark the classname for easier parsing of returned results
1802 modified
= me
. readAmf0Object ( instance
);
1803 // check if we need to convert this class
1804 if ((! klass
) && this . converters
[ className
]) {
1805 modified
= this . converters
[ className
]( instance
);
1810 * Reads an unsigned integer from the byte array
1812 * @param {Number} byteCount the number of bytes to read, e.g. 2 to read
1813 * a 16 bit integer, 4 to read a 32 bit integer, etc.
1816 readUInt : function ( byteCount
) {
1819 // read the first byte
1820 result
= bytes
[ pos
++];
1821 // if this is a multi-byte int, loop over the remaining bytes
1822 for (; i
< byteCount
; ++ i
) {
1823 // shift the result 8 bits to the left and add the next byte.
1824 result
= ( result
<< 8 ) | bytes
[ pos
++];
1829 * Reads an unsigned 29-bit integer from the byte array.
1830 * AMF 3 makes use of a special compact format for writing integers to
1831 * reduce the number of bytes required for encoding. As with a normal
1832 * 32-bit integer, up to 4 bytes are required to hold the value however
1833 * the high bit of the first 3 bytes are used as flags to determine
1834 * whether the next byte is part of the integer. With up to 3 bits of
1835 * the 32 bits being used as flags, only 29 significant bits remain for
1836 * encoding an integer. This means the largest unsigned integer value
1837 * that can be represented is 2^29-1.
1840 * 0x00000000 - 0x0000007F : 0xxxxxxx
1841 * 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx
1842 * 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx
1843 * 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx
1847 readUInt29 : function () {
1848 var value
= bytes
[ pos
++],
1851 // if the high order bit of the first byte is a 1, the next byte
1852 // is also part of this integer.
1853 nextByte
= bytes
[ pos
++];
1854 // remove the high order bit from both bytes before combining them
1855 value
= (( value
& 127 ) << 7 ) | ( nextByte
& 127 );
1856 if ( nextByte
& 128 ) {
1857 // if the high order byte of the 2nd byte is a 1, then
1858 // there is a 3rd byte
1859 nextByte
= bytes
[ pos
++];
1860 // remove the high order bit from the 3rd byte before
1861 // adding it to the value
1862 value
= ( value
<< 7 ) | ( nextByte
& 127 );
1863 if ( nextByte
& 128 ) {
1864 // 4th byte is also part of the integer
1865 nextByte
= bytes
[ pos
++];
1866 // use all 8 bits of the 4th byte
1867 value
= ( value
<< 8 ) | nextByte
;
1874 * Returns undefined. Used for reading the undefined type
1877 readUndefined
: Ext
. emptyFn
,
1879 * Returns undefined. Used for reading the unsupported type
1882 readUnsupported
: Ext
. emptyFn
,
1884 * Reads a UTF-8 string from the byte array
1886 * @param {Number} byteLength The number of bytes to read
1889 readUtf8 : function ( byteLength
) {
1890 var end
= pos
+ byteLength
,
1891 // the string's end position
1894 maxCharCount
= 65535 ,
1898 charArrays
, byteCount
, charCode
;
1902 // UTF-8 characters may be encoded using 1-4 bytes. The number of
1903 // bytes that a character consumes is determined by reading the
1904 // leading byte. Values 0-127 in the leading byte indicate a single-
1905 // byte ASCII-compatible character. Values 192-223 (bytes with "110"
1906 // in the high-order position) indicate a 2-byte character, values
1907 // 224-239 (bytes with "1110" in the high-order position) indicate a
1908 // 3-byte character, and values 240-247 (bytes with "11110" in the
1909 // high-order position) indicate a 4-byte character. The remaining
1910 // bits of the leading byte hold bits of the encoded character, with
1911 // leading zeros if necessary.
1913 // The continuation bytes all have "10" in the high-order position,
1914 // which means only the 6 least significant bits of continuation
1915 // bytes are available to hold the bits of the encoded character.
1917 // The following table illustrates the binary format of UTF-8
1920 // Bits Byte 1 Byte 2 Byte 3 Byte 4
1921 // -----------------------------------------------------
1923 // 11 110xxxxx 10xxxxxx
1924 // 16 1110xxxx 10xxxxxx 10xxxxxx
1925 // 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1927 // read a byte from the byte array - if the byte's value is less
1928 // than 128 we are dealing with a single byte character
1929 charCode
= bytes
[ pos
++];
1930 if ( charCode
> 127 ) {
1931 // if the byte's value is greater than 127 we are dealing
1932 // with a multi-byte character.
1933 if ( charCode
> 239 ) {
1934 // a leading-byte value greater than 239 means this is a
1937 // Use only the 3 least-significant bits of the leading
1938 // byte of a 4-byte character. This is achieved by
1939 // applying the following bit mask:
1940 // (charCode & 0x07)
1941 // which is equivalent to:
1942 // 11110xxx (the byte)
1943 // AND 00000111 (the mask)
1944 charCode
= ( charCode
& 7 );
1945 } else if ( charCode
> 223 ) {
1946 // a leading-byte value greater than 223 but less than
1947 // 240 means this is a 3-byte character
1949 // Use only the 4 least-significant bits of the leading
1950 // byte of a 3-byte character. This is achieved by
1951 // applying the following bit mask:
1952 // (charCode & 0x0F)
1953 // which is equivalent to:
1954 // 1110xxxx (the byte)
1955 // AND 00001111 (the mask)
1956 charCode
= ( charCode
& 15 );
1958 // a leading-byte value less than 224 but (implicitly)
1959 // greater than 191 means this is a 2-byte character
1961 // Use only the 5 least-significant bits of the first
1962 // byte of a 2-byte character. This is achieved by
1963 // applying the following bit mask:
1964 // (charCode & 0x1F)
1965 // which is equivalent to:
1966 // 110xxxxx (the byte)
1967 // AND 00011111 (the mask)
1968 charCode
= ( charCode
& 31 );
1970 while (-- byteCount
) {
1971 // get one continuation byte. then strip off the leading
1972 // "10" by applying the following bit mask:
1974 // which is equialent to:
1975 // 10xxxxxx (the byte)
1976 // AND 00111111 (the mask)
1977 // That leaves 6 remaining bits on the continuation byte
1978 // which are concatenated onto the character's bits
1979 charCode
= (( charCode
<< 6 ) | ( bytes
[ pos
++] & 63 ));
1982 chars
. push ( charCode
);
1983 if (++ charCount
=== maxCharCount
) {
1984 charArrays
. push ( chars
= []);
1989 // At this point we end up with an array of char arrays, each char
1990 // array being no longer than 65,535 characters, the fastest way to
1991 // turn these char arrays into strings is to pass them as the
1992 // arguments to fromCharCode (fortunately all currently supported
1993 // browsers can handle at least 65,535 function arguments).
1994 for (; i
< charArrayCount
; i
++) {
1995 // create a result array containing the strings converted from
1996 // the individual character arrays.
1997 result
. push ( String
. fromCharCode
. apply ( String
, charArrays
[ i
]));
1999 return result
. join ( '' );
2002 * Reads an AMF "value-type" from the byte array. Automatically detects
2003 * the data type by reading the "type marker" from the first byte after
2007 readValue : function () {
2009 marker
= bytes
[ pos
++];
2010 // With the introduction of AMF3, a special type marker was added to
2011 // AMF0 to signal a switch to AMF3 serialization. This allows a packet
2012 // to start out in AMF 0 and switch to AMF 3 on the first complex type
2013 // to take advantage of the more the efficient encoding of AMF 3.
2014 if ( marker
=== 17 ) {
2015 // change the version to AMF3 when we see a 17 marker
2017 marker
= bytes
[ pos
++];
2019 return me
[ me
. typeMap
[ me
. version
][ marker
]]();
2022 * Converters used in converting specific typed Flex classes to JavaScript usable form.
2026 'flex.messaging.io.ArrayCollection' : function ( obj
) {
2027 return obj
. source
|| [];
2032 // array collections have a source var that contains the actual data
2036 * The AMF Reader is used by an {@link Ext.data.amf.Proxy AMF Proxy} to read
2037 * records from a server response that contains binary data in either AMF0 or
2038 * AMF3 format. AMF Reader constructs an {@link Ext.data.amf.Packet AMF Packet}
2039 * and uses it to decode the binary data into javascript objects, then simply
2040 * allows its superclass ({@link Ext.data.reader.Json}) to handle converting the
2041 * raw javascript objects into {@link Ext.data.Model} instances.
2043 * For a more detailed tutorial see the [AMF Guide](#/guide/amf).
2045 Ext
. define ( 'Ext.data.amf.Reader' , {
2046 extend
: 'Ext.data.reader.Json' ,
2047 alias
: 'reader.amf' ,
2049 'Ext.data.amf.Packet'
2052 * @cfg {Number} messageIndex
2053 * AMF Packets can contain multiple messages. This config specifies the
2054 * 0-based index of the message that contains the record data.
2058 * Reads records from a XMLHttpRequest response object containing a binary
2059 * AMF Packet and returns a ResultSet.
2060 * @param {Object} response The XMLHttpRequest response object
2061 * @return {Ext.data.ResultSet}
2063 read : function ( response
) {
2065 bytes
= response
. responseBytes
,
2066 packet
, messages
, resultSet
;
2068 throw "AMF Reader cannot process the response because it does not contain binary data. Make sure the Proxy's 'binary' config is true." ;
2070 packet
= new Ext
. data
. amf
. Packet ();
2071 packet
. decode ( bytes
);
2072 messages
= packet
. messages
;
2073 if ( messages
. length
) {
2074 resultSet
= me
. readRecords ( messages
[ me
. messageIndex
]. body
);
2076 // no messages, return null result set
2077 resultSet
= me
. nullResultSet
;
2078 if ( packet
. invalid
) {
2079 // packet contains invalid data
2080 resultSet
. success
= false ;
2089 * The AMF Proxy is an {@link Ext.data.proxy.Ajax Ajax Proxy} that requests
2090 * binary data from a remote server and parses it into records using an
2091 * {@link Ext.data.amf.Reader AMF Reader} for use in a
2092 * {@link Ext.data.Store Store}.
2094 * Ext.create('Ext.data.Store', {
2102 * For a detailed tutorial on using AMF data see the [AMF Guide](#/guide/amf).
2104 * **Note: ** _This functionality is only available with the purchase of
2105 * Sencha Complete. For more information about using this class, please visit
2106 * our [Sencha Complete](https://www.sencha.com/products/complete/) product page._
2109 Ext
. define ( 'Ext.data.amf.Proxy' , {
2110 extend
: 'Ext.data.proxy.Ajax' ,
2113 'Ext.data.amf.Reader'
2129 * @class Ext.data.amf.RemotingMessage
2130 * Represents a remote call to be sent to the server.
2132 Ext
. define ( 'Ext.data.amf.RemotingMessage' , {
2133 alias
: 'data.amf.remotingmessage' ,
2135 $ flexType
: 'flex.messaging.messages.RemotingMessage' ,
2137 * @property {Array} body - typically an array of parameters to pass to a method call
2141 * @property {String} clientID - identifies the calling client.
2145 * @property {String} destination - the service destination on the server
2149 * @property {Object} headers - the headers to attach to the message.
2150 * Would typically contain the DSEndpoint and DSId fields.
2154 * @property {String} messageId - message identifier
2158 * @property {String} operation - the method name to call
2162 * @property {Array} source - should be empty for security purposes
2166 * @property {Number} timestamp - when the message was created
2170 * @property {Number} timeToLive - how long the message is still valid for passing
2175 * Creates new message.
2176 * @param {Object} config Configuration options
2178 constructor : function ( config
) {
2179 this . initConfig ( config
);
2182 * Returns an AMFX encoded version of the message.
2184 encodeMessage : function () {
2185 var encoder
= Ext
. create ( 'Ext.data.amf.XmlEncoder' ),
2187 cleanObj
= Ext
. copyTo ({}, this , "$flexType,body,clientId,destination,headers,messageId,operation,source,timestamp,timeToLive" , true );
2188 encoder
. writeObject ( cleanObj
);
2189 return encoder
. body
;
2195 * @class Ext.data.amf.XmlDecoder
2196 * This class parses an XML-based AMFX message and returns the deserialized
2197 * objects. You should not need to use this class directly. It's mostly used by
2198 * the AMFX Direct implementation.
2199 * To decode a message, first construct a Decoder:
2201 * decoder = Ext.create('Ext.data.amf.XmlDecoder');
2203 * Then ask it to read in the message :
2205 * resp = decoder.readAmfxMessage(str);
2207 * For more information on working with AMF data please refer to the
2208 * [AMF Guide](#/guide/amf).
2210 Ext
. define ( 'Ext.data.amf.XmlDecoder' , {
2211 alias
: 'data.amf.xmldecoder' ,
2214 * Parses an xml string and returns an xml document
2216 * @param {String} xml
2218 readXml : function ( xml
) {
2220 if ( window
. DOMParser
) {
2221 doc
= ( new DOMParser ()). parseFromString ( xml
, "text/xml" );
2223 doc
= new ActiveXObject ( "Microsoft.XMLDOM" );
2229 * parses a node containing a byte array in hexadecimal format, returning the reconstructed array.
2230 * @param {HTMLElement/XMLElement} node the node
2231 * @return {Array} a byte array
2233 readByteArray : function ( node
) {
2236 str
= node
. firstChild
. nodeValue
;
2237 for ( i
= 0 ; i
< str
. length
; i
= i
+ 2 ) {
2238 c
= str
. substr ( i
, 2 );
2239 bytes
. push ( parseInt ( c
, 16 ));
2244 * Deserializes an AMF3 binary object from a byte array
2245 * @param {Array} bytes the byte array containing one AMF3-encoded value
2246 * @return {Object} the decoded value
2248 readAMF3Value : function ( bytes
) {
2250 packet
= Ext
. create ( 'Ext.data.amf.Packet' );
2251 return packet
. decodeValue ( bytes
);
2254 * Accepts Flex-style UID and decodes the number in the first four bytes (8 hex digits) of data.
2255 * @param {String} messageId the message ID
2256 * @return {Number} the transaction ID
2258 decodeTidFromFlexUID : function ( messageId
) {
2260 str
= messageId
. substr ( 0 , 8 );
2261 return parseInt ( str
, 16 );
2265 * Creates new encoder.
2266 * @param {Object} config Configuration options
2268 constructor : function ( config
) {
2269 this . initConfig ( config
);
2273 * Clears the accumulated data and reference tables
2276 // reset reference counters
2277 this . objectReferences
= [];
2278 this . traitsReferences
= [];
2279 this . stringReferences
= [];
2282 * Reads and returns a decoded AMFX packet.
2283 * @param {String} xml the xml of the message
2284 * @return {Object} the response object containing the message
2286 readAmfxMessage : function ( xml
) {
2287 var doc
, amfx
, body
, i
,
2291 doc
= Ext
. data
. amf
. XmlDecoder
. readXml ( xml
);
2292 amfx
= doc
. getElementsByTagName ( 'amfx' )[ 0 ];
2295 Ext
. warn
. log ( "No AMFX tag in message" );
2297 if ( amfx
. getAttribute ( 'ver' ) != "3" ) {
2298 Ext
. raise ( "Unsupported AMFX version: " + amfx
. getAttribute ( 'ver' ));
2301 body
= amfx
. getElementsByTagName ( 'body' )[ 0 ];
2302 resp
. targetURI
= body
. getAttribute ( 'targetURI' );
2303 resp
. responseURI
= body
. getAttribute ( 'responseURI' );
2304 // most likely empty string
2305 for ( i
= 0 ; i
< body
. childNodes
. length
; i
++) {
2306 if ( body
. childNodes
. item ( i
). nodeType
!= 1 ) {
2307 // only process element nodes, ignore white space and text nodes
2311 resp
. message
= this . readValue ( body
. childNodes
. item ( i
));
2314 // no need to keep iterating
2318 * Parses an HTML element returning the appropriate JavaScript value from the AMFX data.
2319 * @param {HTMLElement} node The node to parse
2320 * @return {Object} a JavaScript object or value
2322 readValue : function ( node
) {
2324 if ( typeof node
. normalize
=== 'function' ) {
2327 // 2DO: handle references!
2328 if ( node
. tagName
== "null" ) {
2330 } else if ( node
. tagName
== "true" ) {
2332 } else if ( node
. tagName
== "false" ) {
2334 } else if ( node
. tagName
== "string" ) {
2335 return this . readString ( node
);
2336 } else if ( node
. tagName
== "int" ) {
2337 return parseInt ( node
. firstChild
. nodeValue
);
2338 } else if ( node
. tagName
== "double" ) {
2339 return parseFloat ( node
. firstChild
. nodeValue
);
2340 } else if ( node
. tagName
== "date" ) {
2341 val
= new Date ( parseFloat ( node
. firstChild
. nodeValue
));
2342 // record in object reference table
2343 this . objectReferences
. push ( val
);
2345 } else if ( node
. tagName
== "dictionary" ) {
2346 return this . readDictionary ( node
);
2347 } else if ( node
. tagName
== "array" ) {
2348 return this . readArray ( node
);
2349 } else if ( node
. tagName
== "ref" ) {
2350 return this . readObjectRef ( node
);
2351 } else if ( node
. tagName
== "object" ) {
2352 return this . readObject ( node
);
2353 } else if ( node
. tagName
== "xml" ) {
2354 // the CDATA content of the node is a parseable XML document. parse it.
2355 return Ext
. data
. amf
. XmlDecoder
. readXml ( node
. firstChild
. nodeValue
);
2356 } else if ( node
. tagName
== "bytearray" ) {
2357 // a byte array is usually an AMF stream. Parse it to a byte array, then pass through the AMF decoder to get the objects inside
2358 return Ext
. data
. amf
. XmlDecoder
. readAMF3Value ( Ext
. data
. amf
. XmlDecoder
. readByteArray ( node
));
2361 Ext
. raise ( "Unknown tag type: " + node
. tagName
);
2366 * Reads a string or string reference and return the value
2367 * @param {HTMLElement/XMLElement} node the node containing a string object
2368 * @return {String} the parsed string
2370 readString : function ( node
) {
2372 if ( node
. getAttributeNode ( 'id' )) {
2373 return this . stringReferences
[ parseInt ( node
. getAttribute ( 'id' ))];
2375 val
= ( node
. firstChild
? node
. firstChild
. nodeValue
: "" ) || "" ;
2376 this . stringReferences
. push ( val
);
2380 * Parses and returns an ordered list of trait names
2381 * @param {HTMLElement/XMLElement} node the traits node from the XML doc
2382 * @return {Array} an array of ordered trait names or null if it's an externalizable object
2384 readTraits : function ( node
) {
2387 if ( node
=== null ) {
2390 if ( node
. getAttribute ( 'externalizable' ) == "true" ) {
2391 // no traits since it's an externalizable or a null object.
2394 if ( node
. getAttributeNode ( 'id' )) {
2395 // return traits reference
2396 return this . traitsReferences
[ parseInt ( node
. getAttributeNode ( 'id' ). value
)];
2398 /* // empty anonymous objects still seem to get their empty traits in the reference table
2399 if (!node.hasChildNodes()) {
2400 var className = node.parentNode.getElementsByTagName('type');
2401 if (className.length == 0) {
2402 return traits; // special case of an anonymous object with no traits. Does not get reference counted
2406 rawtraits
= node
. childNodes
;
2407 for ( i
= 0 ; i
< rawtraits
. length
; i
++) {
2408 if ( rawtraits
. item ( i
). nodeType
!= 1 ) {
2409 // only process element nodes, ignore white space and text nodes
2413 // this will be a string, but let the readValue function handle it nonetheless
2414 traits
. push ( this . readValue ( rawtraits
. item ( i
)));
2416 // register traits in ref table:
2417 this . traitsReferences
. push ( traits
);
2421 * Parses and return an object / array / dictionary / date from reference
2422 * @param {HTMLElement/XMLElement} node the ref node
2423 * @return {Object} the previously instantiated object referred to by the ref node
2425 readObjectRef : function ( node
) {
2427 id
= parseInt ( node
. getAttribute ( 'id' ));
2428 return this . objectReferences
[ id
];
2431 * Parses and returns an AMFX object.
2432 * @param {HTMLElement/XMLElement} node the `<object>` node to parse
2433 * @return {Object} the deserialized object
2435 readObject : function ( node
) {
2438 traitsNode
, i
, j
, n
, key
, val
,
2441 className
= node
. getAttribute ( 'type' );
2443 klass
= Ext
. ClassManager
. getByAlias ( 'amfx.' + className
);
2445 // check if special case for class
2446 obj
= klass
? new klass () : ( className
? {
2447 $ className
: className
2449 // if there is no klass, mark the classname for easier parsing of returned results
2450 // check if we need special handling for this class
2451 if ((! klass
) && this . converters
[ className
]) {
2452 obj
= this . converters
[ className
]( this , node
);
2456 traitsNode
= node
. getElementsByTagName ( 'traits' )[ 0 ];
2457 traits
= this . readTraits ( traitsNode
);
2459 if ( traits
=== null ) {
2460 Ext
. raise ( "No support for externalizable object: " + className
);
2463 // Register object if ref table, in case there's a cyclical reference coming
2464 this . objectReferences
. push ( obj
);
2465 // Now we expect an item for each trait name we have. We assume it's an ordered list. We'll skip the first (traits) tag
2467 for ( i
= 0 ; i
< node
. childNodes
. length
; i
++) {
2468 n
= node
. childNodes
. item ( i
);
2469 if ( n
. nodeType
!= 1 ) {
2470 // Ignore text nodes and non-element nodes
2474 if ( n
. tagName
== "traits" ) {
2475 // ignore the traits node. We've already covered it.
2480 val
= this . readValue ( n
);
2484 if ( j
> traits
. length
) {
2485 Ext
. raise ( "Too many items for object, not enough traits: " + className
);
2492 * Parses and returns an AMFX array.
2493 * @param {HTMLElement/XMLElement} node the array node
2494 * @return {Array} the deserialized array
2496 readArray : function ( node
) {
2498 n
, i
, j
, l
, name
, val
, len
, childnodes
, cn
;
2499 // register array in object references table before we parse, in case of circular references
2500 this . objectReferences
. push ( arr
);
2501 len
= parseInt ( node
. getAttributeNode ( 'length' ). value
);
2503 // the length only accounts for the ordinal values. For the rest, we'll read them as ECMA key-value pairs
2504 for ( l
= 0 ; l
< node
. childNodes
. length
; l
++) {
2505 n
= node
. childNodes
. item ( l
);
2506 if ( n
. nodeType
!= 1 ) {
2507 // Ignore text nodes and non-element nodes
2511 if ( n
. tagName
== "item" ) {
2513 name
= n
. getAttributeNode ( 'name' ). value
;
2514 childnodes
= n
. childNodes
;
2515 for ( j
= 0 ; j
< childnodes
. length
; j
++) {
2516 cn
= childnodes
. item ( j
);
2517 if ( cn
. nodeType
!= 1 ) {
2518 // Ignore text nodes and non-element nodes
2522 val
= this . readValue ( cn
);
2525 // out of loop. We've found our value
2529 arr
[ i
] = this . readValue ( n
);
2533 Ext
. raise ( "Array has more items than declared length: " + i
+ " > " + len
);
2540 Ext
. raise ( "Array has less items than declared length: " + i
+ " < " + len
);
2546 * Parses and returns an AMFX dictionary.
2547 * @param {HTMLElement/XMLElement} node the `<dictionary>` node
2548 * @return {Object} a javascript object with the dictionary value-pair elements
2550 readDictionary : function ( node
) {
2551 // For now, handle regular objects
2553 key
, val
, i
, j
, n
, len
;
2554 len
= parseInt ( node
. getAttribute ( 'length' ));
2555 // Register dictionary in the ref table, in case there's a cyclical reference coming
2556 this . objectReferences
. push ( dict
);
2557 // now find pairs of keys and values
2561 for ( i
= 0 ; i
< node
. childNodes
. length
; i
++) {
2562 n
= node
. childNodes
. item ( i
);
2563 if ( n
. nodeType
!= 1 ) {
2564 // Ignore text nodes and non-element nodes
2569 key
= this . readValue ( n
);
2573 // next element is the value
2574 val
= this . readValue ( n
);
2582 Ext
. raise ( "Incorrect number of dictionary values: " + j
+ " != " + len
);
2588 * Converts externalizable flex objects with a source array to a regular array.
2591 convertObjectWithSourceField : function ( node
) {
2593 for ( i
= 0 ; i
< node
. childNodes
. length
; i
++) {
2594 n
= node
. childNodes
. item ( i
);
2595 if ( n
. tagName
== "bytearray" ) {
2596 val
= this . readValue ( n
);
2597 this . objectReferences
. push ( val
);
2603 // we shouldn't reach here, but just in case
2605 * Converters used in converting specific typed Flex classes to JavaScript usable form.
2609 'flex.messaging.io.ArrayCollection' : function ( decoder
, node
) {
2610 return decoder
. convertObjectWithSourceField ( node
);
2612 'mx.collections.ArrayList' : function ( decoder
, node
) {
2613 return decoder
. convertObjectWithSourceField ( node
);
2615 'mx.collections.ArrayCollection' : function ( decoder
, node
) {
2616 return decoder
. convertObjectWithSourceField ( node
);
2623 * @class Ext.data.amf.XmlEncoder
2624 * This class serializes data in the Action Message Format XML (AMFX) format.
2625 * It can write simple and complex objects, to be used in conjunction with an
2626 * AMFX-compliant server.
2627 * To create an encoded XMl, first construct an Encoder:
2629 * var encoder = Ext.create('Ext.data.amf.XmlEncoder');
2631 * Then use the writer methods to out data to the :
2633 * encoder.writeObject(1);
2634 * encoder.writeObject({a: "b"});
2636 * And access the data through the #bytes property:
2639 * You can also reset the class to start a new body:
2643 * Current limitations:
2644 * AMF3 format (format:3)
2645 * - Each object is written out explicitly, not using the reference tables
2646 * supported by the AMFX format. This means the function does NOT support
2647 * circular reference objects.
2648 * - Objects that aren't Arrays, Dates, Strings, Document (XML) or primitive
2649 * values will be written out as anonymous objects with dynamic data.
2650 * - If the object has a $flexType field, that field will be used in signifying
2651 * the object-type as an attribute, instead of being passed as data.
2652 * - There's no JavaScript equivalent to the ByteArray type in ActionScript,
2653 * hence data will never be searialized as ByteArrays by the writeObject
2654 * function. A writeByteArray method is provided for writing out ByteArray objects.
2656 * For more information on working with AMF data please refer to the
2657 * [AMF Guide](#/guide/amf).
2659 Ext
. define ( 'Ext.data.amf.XmlEncoder' , {
2660 alias
: 'data.amf.xmlencoder' ,
2662 * @property {String} body - The output string
2667 * Utility function to generate a flex-friendly UID
2668 * @param {Number} id used in the first 8 chars of the id. If not provided, a random number will be used.
2669 * @return {String} a string-encoded opaque UID
2671 generateFlexUID : function ( id
) {
2674 if ( id
=== undefined ) {
2675 id
= Ext
. Number
. randomInt ( 0 , 4.294967295 E9
);
2677 // The format of a UID is XXXXXXXX-XXXX-XXXX-XXXX-YYYYYYYYXXXX
2678 // where each X is a random hex digit and each Y is a hex digit from the least significant part of a time stamp.
2679 t
= ( id
+ 4.294967296 E9
). toString ( 16 ). toUpperCase ();
2681 uid
= t
. substr ( t
. length
- 8 , 8 );
2683 for ( j
= 0 ; j
< 3 ; j
++) {
2686 for ( i
= 0 ; i
< 4 ; i
++) {
2687 uid
+= Ext
. Number
. randomInt ( 0 , 15 ). toString ( 16 ). toUpperCase ();
2692 t
= new Number ( new Date ()). valueOf (). toString ( 16 ). toUpperCase ();
2693 // get the String representation of milliseconds in hex format
2696 // pad with "0" if needed
2697 for ( i
= 0 ; i
< t
. length
- 8 ; i
++) {
2702 // actual timestamp:
2703 uid
+= t
. substr (-( 8 - j
));
2705 // and last 4 random digits
2706 for ( i
= 0 ; i
< 4 ; i
++) {
2707 uid
+= Ext
. Number
. randomInt ( 0 , 15 ). toString ( 16 ). toUpperCase ();
2713 * Creates new encoder.
2714 * @param {Object} config Configuration options
2716 constructor : function ( config
) {
2717 this . initConfig ( config
);
2721 * Clears the accumulated data, starting with an empty string
2727 * Returns the encoding for undefined (which is the same as the encoding for null)
2729 encodeUndefined : function () {
2730 return this . encodeNull ();
2733 * Writes the undefined value to the string
2735 writeUndefined : function () {
2736 this . write ( this . encodeUndefined ());
2739 * Returns the encoding for null
2741 encodeNull : function () {
2745 * Writes the null value to the string
2747 writeNull : function () {
2748 this . write ( this . encodeNull ());
2751 * Returns an encoded boolean
2752 * @param {Boolean} val a boolean value
2754 encodeBoolean : function ( val
) {
2764 * Writes a boolean value to the string
2765 * @param {Boolean} val a boolean value
2767 writeBoolean : function ( val
) {
2768 this . write ( this . encodeBoolean ( val
));
2771 * Returns an encoded string
2772 * @param {String} str the string to encode
2774 encodeString : function ( str
) {
2779 ret
= "<string>" + str
+ "</string>" ;
2784 * Writes a string tag with the string content.
2785 * @param {String} str the string to encode
2787 writeString : function ( str
) {
2788 this . write ( this . encodeString ( str
));
2791 * Returns an encoded int
2792 * @param {Number} num the integer to encode
2794 encodeInt : function ( num
) {
2795 return "<int>" + num
. toString () + "</int>" ;
2798 * Writes a int tag with the content.
2799 * @param {Number} num the integer to encode
2801 writeInt : function ( num
) {
2802 this . write ( this . encodeInt ( num
));
2805 * Returns an encoded double
2806 * @param {Number} num the double to encode
2808 encodeDouble : function ( num
) {
2809 return "<double>" + num
. toString () + "</double>" ;
2812 * Writes a double tag with the content.
2813 * @param {Number} num the double to encode
2815 writeDouble : function ( num
) {
2816 this . write ( this . encodeDouble ( num
));
2819 * Returns an encoded number. Decides wheter to use int or double encoding.
2820 * @param {Number} num the number to encode
2822 encodeNumber : function ( num
) {
2823 var maxInt
= 536870911 ,
2824 minSignedInt
= - 268435455 ;
2826 if ( typeof ( num
) !== "number" && !( num
instanceof Number
)) {
2827 Ext
. log
. warn ( "Encoder: writeNumber argument is not numeric. Can't coerce." );
2830 // switch to the primitive value for handling:
2831 if ( num
instanceof Number
) {
2832 num
= num
. valueOf ();
2834 // Determine if this is an integer or a float.
2835 if ( num
% 1 === 0 && num
>= minSignedInt
&& num
<= maxInt
) {
2836 // The number has no decimal point and is within bounds. Let's encode it.
2837 return this . encodeInt ( num
);
2839 return this . encodeDouble ( num
);
2843 * Writes a number, deciding if to use int or double as the tag
2844 * @param {Number} num the number to encode
2846 writeNumber : function ( num
) {
2847 this . write ( this . encodeNumber ( num
));
2851 * @param {Date} date the date to encode
2853 encodeDate : function ( date
) {
2854 return "<date>" + ( new Number ( date
)). toString () + "</date>" ;
2857 * Write a date to the string
2858 * @param {Date} date the date to encode
2860 writeDate : function ( date
) {
2861 this . write ( this . encodeDate ( date
));
2865 * Encodes one ECMA array element
2866 * @param {String} key the name of the element
2867 * @param {Object} value the value of the element
2868 * @return {String} the encoded key-value pair
2870 encodeEcmaElement : function ( key
, value
) {
2871 var str
= '<item name="' + key
. toString () + '">' + this . encodeObject ( value
) + '</item>' ;
2875 * Encodes an array, marking it as an ECMA array if it has associative (non-ordinal) indices
2876 * @param {Array} array the array to encode
2878 encodeArray : function ( array
) {
2882 length
= array
. length
,
2883 // length is of ordinal section only
2886 if ( Ext
. isNumeric ( i
) && ( i
% 1 == 0 )) {
2887 //this is an integer. Add to ordinals array
2888 ordinals
[ i
] = this . encodeObject ( array
[ i
]);
2890 ecmaElements
. push ( this . encodeEcmaElement ( i
, array
[ i
]));
2893 firstNonOrdinal
= ordinals
. length
;
2894 // now, check if we have consecutive numbers in the ordinals array
2895 for ( i
= 0 ; i
< ordinals
. length
; i
++) {
2896 if ( ordinals
[ i
] === undefined ) {
2897 // we have a gap in the array. Mark it - the rest of the items become ECMA elements
2898 firstNonOrdinal
= i
;
2902 if ( firstNonOrdinal
< ordinals
. length
) {
2903 // transfer some of the elements to the ecma array
2904 for ( i
= firstNonOrdinals
; i
< ordinals
. length
; i
++) {
2905 if ( ordinals
[ i
] !== undefined ) {
2906 ecmaElements
. push ( this . encodeEcmaElement ( i
, ordinals
[ i
]));
2909 ordinals
= ordinals
. slice ( 0 , firstNonOrdinal
);
2911 // finally start constructing the string
2912 str
= '<array length="' + ordinals
. length
+ '"' ;
2913 if ( ecmaElements
. length
> 0 ) {
2914 str
+= ' ecma="true"' ;
2917 // first add the oridnals in consecutive order:
2918 for ( i
= 0 ; i
< ordinals
. length
; i
++) {
2919 // iterate by counting since we need to guarantee the order
2922 // Now add ECMA items
2923 for ( i
in ecmaElements
) {
2924 str
+= ecmaElements
[ i
];
2926 // And close the array:
2931 * Writes an array to the string, marking it as an ECMA array if it has associative (non-ordinal) indices
2932 * @param {Array} array the array to encode
2934 writeArray : function ( array
) {
2935 this . write ( this . encodeArray ( array
));
2938 * Encodes an xml document into a CDATA section
2939 * @param {XMLElement/HTMLElement} xml an XML document or element (Document type in some browsers)
2941 encodeXml : function ( xml
) {
2942 var str
= this . convertXmlToString ( xml
);
2943 return "<xml><![CDATA[" + str
+ "]]></xml>" ;
2946 * Write an XML document to the string
2947 * @param {XMLElement/HTMLElement} xml an XML document or element (Document type in some browsers)
2949 writeXml : function ( xml
) {
2950 this . write ( this . encodeXml ( xml
));
2953 * Encodes a generic object into AMFX format. If a <tt>$flexType</tt> member is defined, list that as the object type.
2954 * @param {Object} obj the object to encode
2955 * @return {String} the encoded text
2957 encodeGenericObject : function ( obj
) {
2963 if ( i
== "$flexType" ) {
2966 traits
. push ( this . encodeString ( new String ( i
)));
2967 values
. push ( this . encodeObject ( obj
[ i
]));
2971 str
= '<object type="' + flexType
+ '">' ;
2975 if ( traits
. length
> 0 ) {
2977 str
+= traits
. join ( "" );
2980 str
+= "<traits />" ;
2982 str
+= values
. join ( "" );
2987 * Writes a generic object to the string. If a <tt>$flexType</tt> member is defined, list that as the object type.
2988 * @param {Object} obj the object to encode
2990 writeGenericObject : function ( obj
) {
2991 this . write ( this . encodeGenericObject ( obj
));
2994 * Encodes a byte arrat in AMFX format
2995 * @param {Array} array the byte array to encode
2997 encodeByteArray : function ( array
) {
2999 if ( array
. length
> 0 ) {
3000 str
= "<bytearray>" ;
3001 for ( i
= 0 ; i
< array
. length
; i
++) {
3003 if (! Ext
. isNumber ( array
[ i
])) {
3004 Ext
. raise ( "Byte array contains a non-number: " + array
[ i
] + " in index: " + i
);
3006 if ( array
[ i
] < 0 || array
[ i
] > 255 ) {
3007 Ext
. raise ( "Byte array value out of bounds: " + array
[ i
]);
3010 h
= array
[ i
]. toString ( 16 ). toUpperCase ();
3011 if ( array
[ i
] < 16 ) {
3016 str
+= "</bytearray>" ;
3018 str
= "<bytearray />" ;
3023 * Writes an AMFX byte array to the string. This is for convenience only and is not called automatically by writeObject.
3024 * @param {Array} array the byte array to encode
3026 writeByteArray : function ( array
) {
3027 this . write ( this . encodeByteArray ( array
));
3030 * encode the appropriate data item. Supported types:
3037 * - XML Document (identified by being instaneof Document. Can be generated with: new DOMParser()).parseFromString(xml, "text/xml");
3041 * @param {Object} item A primitive or object to write to the stream
3042 * @return {String} the encoded object in AMFX format
3044 encodeObject : function ( item
) {
3045 var t
= typeof ( item
);
3046 //Ext.log("Writing " + item + " of type " + t);
3047 if ( t
=== "undefined" ) {
3048 return this . encodeUndefined ();
3049 } else if ( item
=== null ) {
3050 // can't check type since typeof(null) returns "object"
3051 return this . encodeNull ();
3052 } else if ( Ext
. isBoolean ( item
)) {
3053 return this . encodeBoolean ( item
);
3054 } else if ( Ext
. isString ( item
)) {
3055 return this . encodeString ( item
);
3056 } else if ( t
=== "number" || item
instanceof Number
) {
3057 // Can't use Ext.isNumeric since it accepts strings as well
3058 return this . encodeNumber ( item
);
3059 } else if ( t
=== "object" ) {
3060 // Figure out which object this is
3061 if ( item
instanceof Date
) {
3062 return this . encodeDate ( item
);
3063 } else if ( Ext
. isArray ( item
)) {
3064 return this . encodeArray ( item
);
3065 } else if ( this . isXmlDocument ( item
)) {
3066 return this . encodeXml ( item
);
3068 // Treat this as a generic object with name/value pairs of data.
3069 return this . encodeGenericObject ( item
);
3073 Ext
. log
. warn ( "AMFX Encoder: Unknown item type " + t
+ " can't be written to stream: " + item
);
3078 // if we reached here, return null
3080 * Writes the appropriate data item to the string. Supported types:
3087 * - XML Document (identified by being instaneof Document. Can be generated with: new DOMParser()).parseFromString(xml, "text/xml");
3091 * @param {Object} item A primitive or object to write to the stream
3093 writeObject : function ( item
) {
3094 this . write ( this . encodeObject ( item
));
3097 * Encodes an AMFX remoting message with the AMFX envelope.
3098 * @param {Ext.data.amf.RemotingMessage} message the message to pass on to serialize.
3100 encodeAmfxRemotingPacket : function ( message
) {
3102 str
= '<amfx ver="3" xmlns="http://www.macromedia.com/2005/amfx"><body>' ;
3103 str
+= message
. encodeMessage ();
3104 str
+= '</body></amfx>' ;
3108 * Writes an AMFX remoting message with the AMFX envelope to the string.
3109 * @param {Ext.data.amf.RemotingMessage} message the message to pass on to serialize.
3111 writeAmfxRemotingPacket : function ( params
) {
3112 this . write ( this . encodeAmfxRemotingPacket ( params
));
3115 * Converts an XML Document object to a string.
3116 * @param {Object} xml XML document to convert (typically Document object)
3117 * @return {String} A string representing the document
3120 convertXmlToString : function ( xml
) {
3122 if ( window
. XMLSerializer
) {
3123 // this is not IE, so:
3124 str
= new window
. XMLSerializer (). serializeToString ( xml
);
3126 //no XMLSerializer, might be an old version of IE
3132 * Tries to determine if an object is an XML document
3133 * @param {Object} item to identify
3134 * @return {Boolean} true if it's an XML document, false otherwise
3136 isXmlDocument : function ( item
) {
3137 // We can't test if Document is defined since IE just throws an exception. Instead rely on the DOMParser object
3138 if ( window
. DOMParser
) {
3139 if ( Ext
. isDefined ( item
. doctype
)) {
3143 // Otherwise, check if it has an XML field
3144 if ( Ext
. isString ( item
. xml
)) {
3145 // and we can get the xml
3151 * Appends a string to the body of the message
3152 * @param {String} str the string to append
3155 write : function ( str
) {
3162 * @class Ext.direct.AmfRemotingProvider
3164 * <p>The {@link Ext.direct.AmfRemotingProvider AmfRemotingProvider}
3165 * allows making RPC calls to a Java object on a BlazeDS or ColdFusion using either the AMFX or the AMF protocols.</p>
3167 * <p>The default protocol is AMFX which works on all browsers. If you choose AMF, a flash plugin might be loaded in certain browsers that do not support posting binary data to the server, e.g. Internet Explorer version 9 or less. To choose AMF, set the {@link Ext.direct.AmfRemotingProvider#binary binary} property to true.</p>
3168 * <p>For AMFX, the server must be configured to expose the desired services via an HTTPEndpoint. For example, the following configuration snippet adds an HTTPEndpoint (AMFX endpoint) to the BlazeDS services-config.xml file:</p>
3170 <channel-definition id="my-http" class="mx.messaging.channels.HTTPChannel">
3171 <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/http" class="flex.messaging.endpoints.HTTPEndpoint"/>
3172 </channel-definition>
3175 * <p>Once the HTTPEndpoint is configured, make sure the service is exposed via the channel by adding the channel (e.g. my-http) to your remoting-services.xml file.
3176 * For example this allows services to be accessed remotely by both AMF and AMFX:</p>
3178 <default-channels>
3179 <channel ref="my-amf"/>
3180 <channel ref="my-http"/>
3181 </default-channels>
3184 * <p>In order to make a call, you first need to declare the API to Ext direct. The following example defines local methods to the services provided by the sample Products application provided by Adobe as part of the BlazeDS 4.x binary turnkey distribution's testdrive (Sample 5: Updating Data):</p>
3186 Ext.direct.Manager.addProvider({
3187 "url":"/samples/messagebroker/http", // URL for the HTTPEndpoint
3188 "type":"amfremoting",
3189 "endpoint": "my-http", // the name of the HTTPEndpoint channel as defined in the server's services-config.xml
3191 "product":[{ // name of the destination as defined in remoting-config.xml on the server
3192 "name":"getProducts", // method name of the method to call
3193 "len":0 // number of parameters
3205 * <p>You can now call the service as follows:</p>
3207 product.getProducts((function(provider, response) {
3208 // do something with the response
3209 console.log("Got " + response.data.length + " objects");
3213 * Note that in case server methods require parameters of a specific class (e.g. flex.samples.product.Product), you should make sure the passed parameter has a field called $flexType set to the class name (in this case flex.Samples.product.Product). This is similar to the remote class alias definition in ActionScript.
3216 * <p>The following example shows how to define a binary AMF-based call:</p>
3218 Ext.direct.Manager.addProvider({
3219 "url":"/samples/messagebroker/amf", // URL for the AMFEndpoint
3220 "type":"amfremoting",
3221 "endpoint": "my-amf", // the name of the AMFEndpoint channel as defined in the server's services-config.xml
3222 "binary": true, // chooses AMF encoding
3224 "product":[{ // name of the destination as defined in remoting-config.xml on the server
3225 "name":"getProducts", // method name of the method to call
3226 "len":0 // number of parameters
3238 * <p>Calling the server is done the same way as for the AMFX-based definition.</p>
3241 Ext
. define ( 'Ext.direct.AmfRemotingProvider' , {
3242 /* Begin Definitions */
3243 alias
: 'direct.amfremotingprovider' ,
3244 extend
: 'Ext.direct.Provider' ,
3246 'Ext.util.MixedCollection' ,
3247 'Ext.util.DelayedTask' ,
3248 'Ext.direct.Transaction' ,
3249 'Ext.direct.RemotingMethod' ,
3250 'Ext.data.amf.XmlEncoder' ,
3251 'Ext.data.amf.XmlDecoder' ,
3252 'Ext.data.amf.Encoder' ,
3253 'Ext.data.amf.Packet' ,
3254 'Ext.data.amf.RemotingMessage' ,
3255 'Ext.direct.ExceptionEvent'
3257 /* End Definitions */
3259 * @cfg {Object} actions
3260 * Object literal defining the server side actions and methods. For example, if
3261 * the Provider is configured with:
3263 "actions":{ // each property within the 'actions' object represents a server side Class
3264 "TestAction":[ // array of methods within each server side Class to be
3265 { // stubbed out on client
3269 "name":"multiply",// name of method
3270 "len":2 // The number of parameters that will be used to create an
3271 // array of data to send to the server side function.
3272 // Ensure the server sends back a Number, not a String.
3275 "formHandler":true, // direct the client to use specialized form handling method
3280 * <p>Note that a Store is not required, a server method can be called at any time.
3281 * In the following example a <b>client side</b> handler is used to call the
3282 * server side method "multiply" in the server-side "TestAction" Class:</p>
3284 TestAction.multiply(
3285 2, 4, // pass two arguments to server, so specify len=2
3286 // callback function after the server is called
3287 // result: the result returned by the server
3288 // e: Ext.direct.RemotingEvent object
3289 function(result, e) {
3290 var t = e.getTransaction();
3291 var action = t.action; // server side Class called
3292 var method = t.method; // server side method called
3294 var answer = Ext.encode(result); // 8
3297 var msg = e.message; // failure message
3302 * In the example above, the server side "multiply" function will be passed two
3303 * arguments (2 and 4). The "multiply" method should return the value 8 which will be
3304 * available as the <tt>result</tt> in the example above.
3307 * @cfg {String/Object} namespace
3308 * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
3309 * Explicitly specify the namespace Object, or specify a String to have a
3310 * {@link Ext#namespace namespace created} implicitly.
3314 * <b>Required</b>. The URL to connect to the Flex remoting server (LCDS, BlazeDS, etc).
3315 * This should include the /messagebroker/amf suffix as defined in the services-config.xml and remoting-config.xml files.
3318 * @cfg {String} endpoint
3319 * <b>Requred</b>. This is the channel id defined in services-config.xml on the server (e.g. my-amf or my-http).
3322 * @cfg {String} enableUrlEncode
3323 * Specify which param will hold the arguments for the method.
3324 * Defaults to <tt>'data'</tt>.
3327 * @cfg {String} binary
3328 * If true, use AMF binary encoding instead of AMFX XML-based encoding. Note that on some browsers, this will load a flash plugin to handle binary communication with the server. Important: If using binary encoding with older browsers, see notes in {@link Ext.data.flash.BinaryXhr BinaryXhr} regarding packaging the Flash plugin for use in older browsers.
3332 * @cfg {Number} maxRetries
3333 * Number of times to re-attempt delivery on failure of a call.
3337 * @cfg {Number} timeout
3338 * The timeout to use for each request.
3343 * Fires immediately before the client-side sends off the RPC call.
3344 * By returning false from an event handler you can prevent the call from
3346 * @param {Ext.direct.AmfRemotingProvider} provider
3347 * @param {Ext.direct.Transaction} transaction
3348 * @param {Object} meta The meta data
3352 * Fires immediately after the request to the server-side is sent. This does
3353 * NOT fire after the response has come back from the call.
3354 * @param {Ext.direct.AmfRemotingProvider} provider
3355 * @param {Ext.direct.Transaction} transaction
3356 * @param {Object} meta The meta data
3358 constructor : function ( config
) {
3360 me
. callParent ( arguments
);
3361 me
. namespace = ( Ext
. isString ( me
. namespace )) ? Ext
. ns ( me
. namespace ) : me
. namespace || window
;
3362 me
. transactions
= new Ext
. util
. MixedCollection ();
3366 * Initialize the API
3369 initAPI : function () {
3370 var actions
= this . actions
,
3371 namespace = this . namespace ,
3372 action
, cls
, methods
, i
, len
, method
;
3373 for ( action
in actions
) {
3374 if ( actions
. hasOwnProperty ( action
)) {
3375 cls
= namespace [ action
];
3377 cls
= namespace [ action
] = {};
3379 methods
= actions
[ action
];
3380 for ( i
= 0 , len
= methods
. length
; i
< len
; ++ i
) {
3381 method
= new Ext
. direct
. RemotingMethod ( methods
[ i
]);
3382 cls
[ method
. name
] = this . createHandler ( action
, method
);
3388 * Create a handler function for a direct call.
3390 * @param {String} action The action the call is for
3391 * @param {Object} method The details of the method
3392 * @return {Function} A JS function that will kick off the call
3394 createHandler : function ( action
, method
) {
3397 if (! method
. formHandler
) {
3398 handler = function () {
3399 me
. configureRequest ( action
, method
, Array
. prototype . slice
. call ( arguments
, 0 ));
3402 handler = function ( form
, callback
, scope
) {
3403 me
. configureFormRequest ( action
, method
, form
, callback
, scope
);
3406 handler
. directCfg
= {
3412 isConnected : function () {
3413 return !! this . connected
;
3415 connect : function () {
3418 // Generate a unique ID for this client
3419 me
. clientId
= Ext
. data
. amf
. XmlEncoder
. generateFlexUID ();
3421 me
. connected
= true ;
3422 me
. fireEvent ( 'connect' , me
);
3424 } else if (! me
. url
) {
3426 Ext
. raise ( 'Error initializing RemotingProvider, no url configured.' );
3430 disconnect : function () {
3433 me
. connected
= false ;
3434 me
. fireEvent ( 'disconnect' , me
);
3438 * Run any callbacks related to the transaction.
3440 * @param {Ext.direct.Transaction} transaction The transaction
3441 * @param {Ext.direct.Event} event The event
3443 runCallback : function ( transaction
, event
) {
3444 var success
= !! event
. status
,
3445 funcName
= success
? 'success' : 'failure' ,
3447 if ( transaction
&& transaction
. callback
) {
3448 callback
= transaction
. callback
;
3449 result
= Ext
. isDefined ( event
. result
) ? event
. result
: event
. data
;
3450 if ( Ext
. isFunction ( callback
)) {
3451 callback ( result
, event
, success
);
3453 Ext
. callback ( callback
[ funcName
], callback
. scope
, [
3458 Ext
. callback ( callback
. callback
, callback
. scope
, [
3467 * React to the ajax request being completed
3470 onData : function ( options
, success
, response
) {
3473 len
, events
, event
, transaction
, transactions
;
3475 events
= me
. createEvents ( response
);
3476 for ( len
= events
. length
; i
< len
; ++ i
) {
3478 transaction
= me
. getTransaction ( event
);
3479 me
. fireEvent ( 'data' , me
, event
);
3481 me
. runCallback ( transaction
, event
, true );
3482 Ext
. direct
. Manager
. removeTransaction ( transaction
);
3486 transactions
= []. concat ( options
. transaction
);
3487 for ( len
= transactions
. length
; i
< len
; ++ i
) {
3488 transaction
= me
. getTransaction ( transactions
[ i
]);
3489 if ( transaction
&& transaction
. retryCount
< me
. maxRetries
) {
3490 transaction
. retry ();
3492 event
= new Ext
. direct
. ExceptionEvent ({
3494 transaction
: transaction
,
3495 code
: Ext
. direct
. Manager
. exceptions
. TRANSPORT
,
3496 message
: 'Unable to connect to the server.' ,
3499 me
. fireEvent ( 'data' , me
, event
);
3501 me
. runCallback ( transaction
, event
, false );
3502 Ext
. direct
. Manager
. removeTransaction ( transaction
);
3509 * Get transaction from XHR options
3511 * @param {Object} options The options sent to the Ajax request
3512 * @return {Ext.direct.Transaction} The transaction, null if not found
3514 getTransaction : function ( options
) {
3515 return options
&& options
. tid
? Ext
. direct
. Manager
. getTransaction ( options
. tid
) : null ;
3518 * Configure a direct request
3520 * @param {String} action The action being executed
3521 * @param {Object} method The method being executed
3523 configureRequest : function ( action
, method
, args
) {
3525 callData
= method
. getCallData ( args
),
3526 data
= callData
. data
,
3527 callback
= callData
. callback
,
3528 scope
= callData
. scope
,
3530 transaction
= new Ext
. direct
. Transaction ({
3534 method
: method
. name
,
3536 callback
: scope
&& Ext
. isFunction ( callback
) ? Ext
. Function
. bind ( callback
, scope
) : callback
3538 if ( me
. fireEvent ( 'beforecall' , me
, transaction
, method
) !== false ) {
3539 Ext
. direct
. Manager
. addTransaction ( transaction
);
3540 me
. queueTransaction ( transaction
);
3541 me
. fireEvent ( 'call' , me
, transaction
, method
);
3545 * Gets the Flex remoting message info for a transaction
3547 * @param {Ext.direct.Transaction} transaction The transaction
3548 * @return {Object} The Flex remoting message structure ready to encode in an AMFX RemoteMessage
3550 getCallData : function ( transaction
) {
3553 targetUri
: transaction
. action
+ "." + transaction
. method
,
3554 responseUri
: '/' + transaction
. id
,
3555 body
: transaction
. data
|| []
3558 return new Ext
. data
. amf
. RemotingMessage ({
3559 body
: transaction
. data
|| [],
3560 clientId
: this . clientId
,
3561 destination
: transaction
. action
,
3563 DSEndpoint
: this . endpoint
,
3564 DSId
: this . DSId
|| "nil"
3566 // if unknown yet, use "nil"
3567 messageId
: Ext
. data
. amf
. XmlEncoder
. generateFlexUID ( transaction
. id
),
3568 // encode as first 4 bytes of UID
3569 operation
: transaction
. method
,
3577 action: transaction.action,
3578 method: transaction.method,
3579 data: transaction.data,
3585 * Sends a request to the server
3587 * @param {Object/Array} data The data to send
3589 sendRequest : function ( data
) {
3593 callback
: me
. onData
,
3600 len
, params
, encoder
,
3603 // prepare AMFX messages
3604 if ( Ext
. isArray ( data
)) {
3607 Ext
. raise ( "Mutltiple messages in the same call are not supported in AMFX" );
3610 for ( len
= data
. length
; i
< len
; ++ i
) {
3611 amfMessages
. push ( me
. getCallData ( data
[ i
]));
3614 amfMessages
. push ( me
. getCallData ( data
));
3617 encoder
= new Ext
. data
. amf
. Encoder ({
3620 // AMF message sending always uses AMF0
3622 encoder
. writeAmfPacket ( amfHeaders
, amfMessages
);
3623 request
. binaryData
= encoder
. bytes
;
3624 request
. binary
= true ;
3627 'Content-Type' : 'application/x-amf'
3630 encoder
= new Ext
. data
. amf
. XmlEncoder ();
3632 encoder
. writeAmfxRemotingPacket ( amfMessages
[ 0 ]);
3633 request
. xmlData
= encoder
. body
;
3635 // prepare Ajax request
3636 Ext
. Ajax
. request ( request
);
3639 * Add a new transaction to the queue
3641 * @param {Ext.direct.Transaction} transaction The transaction
3643 queueTransaction : function ( transaction
) {
3645 enableBuffer
= false ;
3646 // no queueing for AMFX
3647 if ( transaction
. form
) {
3648 me
. sendFormRequest ( transaction
);
3651 me
. callBuffer
. push ( transaction
);
3654 me
. callTask
= new Ext
. util
. DelayedTask ( me
. combineAndSend
, me
);
3656 me
. callTask
. delay ( Ext
. isNumber ( enableBuffer
) ? enableBuffer
: 10 );
3658 me
. combineAndSend ();
3662 * Combine any buffered requests and send them off
3665 combineAndSend : function () {
3666 var buffer
= this . callBuffer
,
3667 len
= buffer
. length
;
3669 this . sendRequest ( len
== 1 ? buffer
[ 0 ] : buffer
);
3670 this . callBuffer
= [];
3674 * Configure a form submission request
3676 * @param {String} action The action being executed
3677 * @param {Object} method The method being executed
3678 * @param {HTMLElement} form The form being submitted
3679 * @param {Function} callback (optional) A callback to run after the form submits
3680 * @param {Object} scope (optional) A scope to execute the callback in
3682 configureFormRequest : function ( action
, method
, form
, callback
, scope
) {
3684 Ext
. raise ( "Form requests are not supported for AmfRemoting" );
3689 transaction = new Ext.direct.Transaction({
3692 method: method.name,
3693 args: [form, callback, scope],
3694 callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback,
3700 if (me.fireEvent('beforecall', me, transaction, method) !== false) {
3701 Ext.direct.Manager.addTransaction(transaction);
3702 isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data';
3705 extTID: transaction.id,
3707 extMethod: method.name,
3709 extUpload: String(isUpload)
3712 // change made from typeof callback check to callback.params
3713 // to support addl param passing in DirectSubmit EAC 6/2
3714 Ext.apply(transaction, {
3715 form: Ext.getDom(form),
3717 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
3719 me.fireEvent('call', me, transaction, method);
3720 me.sendFormRequest(transaction);
3724 * Sends a form request
3726 * @param {Ext.direct.Transaction} transaction The transaction to send
3728 sendFormRequest : function ( transaction
) {
3730 Ext
. raise ( "Form requests are not supported for AmfRemoting" );
3736 params: transaction.params,
3737 callback: this.onData,
3739 form: transaction.form,
3740 isUpload: transaction.isUpload,
3741 transaction: transaction
3745 * Creates a set of events based on the XHR response
3747 * @param {Object} response The XHR response
3748 * @return {Ext.direct.Event[]} An array of Ext.direct.Event
3750 createEvents : function ( response
) {
3759 decoder
= new Ext
. data
. amf
. Packet ();
3760 data
= decoder
. decode ( response
. responseBytes
);
3762 decoder
= new Ext
. data
. amf
. XmlDecoder ();
3763 data
= decoder
. readAmfxMessage ( response
. responseText
);
3766 // This won't be sent back unless we use a ping message, so ignore for now
3767 // if we don't have the server ID yet, check for it here
3769 if (data.message.headers && data.message.headers.DSId) {
3770 this.DSId = data.message.headers.DSId;
3775 event
= new Ext
. direct
. ExceptionEvent ({
3778 code
: Ext
. direct
. Manager
. exceptions
. PARSE
,
3779 message
: 'Error parsing AMF response: \n\n ' + data
3786 for ( i
= 0 ; i
< data
. messages
. length
; i
++) {
3787 events
. push ( this . createEvent ( data
. messages
[ i
]));
3790 // AMFX messages have one response per message
3791 events
. push ( this . createEvent ( data
));
3796 * Create an event from an AMFX response object
3797 * @param {Object} response The AMFX response object
3798 * @return {Ext.direct.Event} The event
3800 createEvent : function ( response
) {
3801 // Check targetUri to identify transaction ID and status
3802 var status
= response
. targetURI
. split ( "/" ),
3803 tid
, event
, data
, statusIndex
,
3809 tid
= Ext
. data
. amf
. XmlDecoder
. decodeTidFromFlexUID ( response
. message
. correlationId
);
3812 // construct data structure
3813 if ( status
[ statusIndex
] == "onStatus" ) {
3817 data
: ( me
. binary
? response
. body
: response
. message
)
3819 event
= Ext
. create ( 'direct.exception' , data
);
3820 } else if ( status
[ statusIndex
] == "onResult" ) {
3824 data
: ( me
. binary
? response
. body
: response
. message
. body
)
3826 event
= Ext
. create ( 'direct.rpc' , data
);
3829 Ext
. raise ( "Unknown AMF return status: " + status
[ statusIndex
]);