amf-debug.js 141 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836
  1. // @tag enterprise
  2. /**
  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:
  8. *
  9. * var encoder = Ext.create('Ext.data.amf.Encoder', {
  10. * format: 3
  11. * });
  12. *
  13. * Then use the writer methods to out data to the :
  14. *
  15. * encoder.writeObject(1);
  16. *
  17. * And access the data through the #bytes property:
  18. * encoder.bytes;
  19. *
  20. * You can also reset the class to start a new byte array:
  21. *
  22. * encoder.clear();
  23. *
  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
  28. * objects.
  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.
  39. *
  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.
  48. *
  49. * For more information on working with AMF data please refer to the
  50. * [AMF Guide](../guides/backend_connectors/amf.html).
  51. */
  52. Ext.define('Ext.data.amf.Encoder', {
  53. alias: 'data.amf.Encoder',
  54. config: {
  55. format: 3
  56. },
  57. /**
  58. * @property {Array} bytes
  59. * @readonly
  60. * The constructed byte array.
  61. */
  62. bytes: [],
  63. /**
  64. * Creates new Encoder.
  65. * @param {Object} config Configuration options
  66. */
  67. constructor: function(config) {
  68. this.initConfig(config);
  69. this.clear();
  70. },
  71. /**
  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.
  75. */
  76. clear: function() {
  77. this.bytes = [];
  78. },
  79. /**
  80. * Sets the functions that will correctly serialize for the relevant
  81. * protocol version.
  82. * @param {Number} protocol_version the protocol version to support
  83. */
  84. applyFormat: function(protocol_version) {
  85. var funcs = {
  86. 0: {
  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
  96. },
  97. 3: {
  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
  107. }
  108. }[protocol_version];
  109. if (funcs) {
  110. Ext.apply(this, funcs);
  111. return protocol_version;
  112. } else {
  113. //<debug>
  114. Ext.raise("Unsupported AMF format: " + protocol_version + ". Only '3' (AMF3) is supported at this point.");
  115. //</debug>
  116. return;
  117. }
  118. },
  119. // return nothing
  120. /**
  121. * Write the appropriate data items to the byte array. Supported types:
  122. * - undefined
  123. * - null
  124. * - boolean
  125. * - integer (if AMF3 - limited by 29-bit int, otherwise passed as double)
  126. * - double
  127. * - UTF-8 string
  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
  130. */
  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"
  138. this.writeNull();
  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)) {
  154. this.writeXml(item);
  155. } else {
  156. // Treat this as a generic object with name/value pairs of data.
  157. this.writeGenericObject(item);
  158. }
  159. } else {
  160. //<debug>
  161. Ext.log.warn("AMF Encoder: Unknown item type " + t + " can't be written to stream: " + item);
  162. }
  163. },
  164. //</debug>
  165. /**
  166. * Writes the AMF3 undefined value to the byte array.
  167. * @private
  168. */
  169. write3Undefined: function() {
  170. this.writeByte(0);
  171. },
  172. // AMF3 undefined
  173. /**
  174. * Writes the AMF0 undefined value to the byte array.
  175. * @private
  176. */
  177. write0Undefined: function() {
  178. this.writeByte(6);
  179. },
  180. // AMF0 undefined
  181. /**
  182. * Writes the AMF3 null value to the byte array.
  183. * @private
  184. */
  185. write3Null: function() {
  186. this.writeByte(1);
  187. },
  188. // AMF3 null
  189. /**
  190. * Writes the AMF0 null value to the byte array.
  191. * @private
  192. */
  193. write0Null: function() {
  194. this.writeByte(5);
  195. },
  196. // AMF0 null
  197. /**
  198. * Writes the appropriate AMF3 boolean value to the byte array.
  199. * @param {boolean} item The value to write
  200. * @private
  201. */
  202. write3Boolean: function(item) {
  203. //<debug>
  204. if (typeof (item) !== "boolean") {
  205. Ext.log.warn("Encoder: writeBoolean argument is not a boolean. Coercing.");
  206. }
  207. // </debug>
  208. if (item) {
  209. this.writeByte(3);
  210. } else // AMF3 true
  211. {
  212. this.writeByte(2);
  213. }
  214. },
  215. // AMF3 false
  216. /**
  217. * Writes the appropriate AMF0 boolean value to the byte array.
  218. * @param {boolean} item The value to write
  219. * @private
  220. */
  221. write0Boolean: function(item) {
  222. //<debug>
  223. if (typeof (item) !== "boolean") {
  224. Ext.log.warn("Encoder: writeBoolean argument is not a boolean. Coercing.");
  225. }
  226. // </debug>
  227. this.writeByte(1);
  228. // AMF0 boolean marker
  229. if (item) {
  230. this.writeByte(1);
  231. } else // AMF0 true
  232. {
  233. this.writeByte(0);
  234. }
  235. },
  236. // AMF0 false
  237. /**
  238. * Encodes a U29 int, returning a byte array with the encoded number.
  239. * @param item - unsigned int value
  240. * @private
  241. */
  242. encode29Int: function(item) {
  243. var data = [],
  244. // prepare the bytes, then send them to the array
  245. num = item,
  246. nibble, i;
  247. if (num == 0) {
  248. return [
  249. 0
  250. ];
  251. }
  252. // no other data
  253. // we have a special case if the number is 4-nibbles in U29 encoding
  254. if (num > 2097151) {
  255. // last nibble is an 8-bit value
  256. nibble = num & 255;
  257. data.unshift(nibble);
  258. num = num >> 8;
  259. }
  260. // get all the 7-bit parts ready
  261. while (num > 0) {
  262. nibble = num & 127;
  263. // 7 bits
  264. data.unshift(nibble);
  265. num = num >> 7;
  266. }
  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;
  271. }
  272. return data;
  273. },
  274. /**
  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
  277. * @private
  278. */
  279. write3Number: function(item) {
  280. var data;
  281. var maxInt = 536870911,
  282. minSignedInt = -268435455;
  283. //<debug>
  284. if (typeof (item) !== "number" && !(item instanceof Number)) {
  285. Ext.log.warn("Encoder: writeNumber argument is not numeric. Can't coerce.");
  286. }
  287. // </debug>
  288. // switch to the primitive value for handling:
  289. if (item instanceof Number) {
  290. item = item.valueOf();
  291. }
  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
  300. data.unshift(4);
  301. // AMF3 integer marker
  302. // write it!
  303. this.writeBytes(data);
  304. } else {
  305. data = this.encodeDouble(item);
  306. data.unshift(5);
  307. // AMF3 double marker
  308. this.writeBytes(data);
  309. }
  310. },
  311. /**
  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
  314. * @private
  315. */
  316. write0Number: function(item) {
  317. var data;
  318. //<debug>
  319. if (typeof (item) !== "number" && !(item instanceof Number)) {
  320. Ext.log.warn("Encoder: writeNumber argument is not numeric. Can't coerce.");
  321. }
  322. // </debug>
  323. // switch to the primitive value for handling:
  324. if (item instanceof Number) {
  325. item = item.valueOf();
  326. }
  327. //In AMF0 numbers are always serialized as double-float values.
  328. data = this.encodeDouble(item);
  329. data.unshift(0);
  330. // AMF0 double marker
  331. this.writeBytes(data);
  332. },
  333. /**
  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
  337. */
  338. encodeUtf8Char: function(c) {
  339. var data = [],
  340. val, b, i, marker;
  341. //<debug>
  342. if (c > 1114111) {
  343. //<debug>
  344. Ext.raise("UTF 8 char out of bounds");
  345. }
  346. //</debug>
  347. //</debug>
  348. if (c <= 127) {
  349. // One byte UTF8
  350. data.push(c);
  351. } else {
  352. // Multi-byte UTF8. Figure out how many bytes:
  353. if (c <= 2047) {
  354. b = 2;
  355. } else if (c <= 65535) {
  356. b = 3;
  357. } else {
  358. b = 4;
  359. }
  360. // encode LSBs of value
  361. marker = 128;
  362. // MSB marker
  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
  366. data.unshift(val);
  367. c = c >> 6;
  368. // drop 6 LSbs
  369. marker = (marker >> 1) | 128;
  370. }
  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
  373. val = c | marker;
  374. data.unshift(val);
  375. }
  376. return data;
  377. },
  378. /**
  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
  382. * @private
  383. */
  384. encodeUtf8String: function(str) {
  385. var i,
  386. utf8Data = [];
  387. for (i = 0; i < str.length; i++) {
  388. var data = this.encodeUtf8Char(str.charCodeAt(i));
  389. Ext.Array.push(utf8Data, data);
  390. }
  391. return utf8Data;
  392. },
  393. // quicker conversion, doesn't work in IE:
  394. // utf8String = unescape(encodeURIComponent(str)),
  395. // utf8Data = [];
  396. /**
  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
  400. *
  401. * @private
  402. */
  403. encode3Utf8StringLen: function(utf8Data) {
  404. var len = utf8Data.length,
  405. data = [];
  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:
  409. len = len << 1;
  410. len = len | 1;
  411. // mark as value
  412. // push length value to the array
  413. data = this.encode29Int(len);
  414. } else {
  415. //<debug>
  416. Ext.raise("UTF8 encoded string too long to serialize to AMF: " + len);
  417. }
  418. //</debug>
  419. return data;
  420. },
  421. /**
  422. * Write an AMF3 UTF-8 string to the byte array
  423. * @param {String} item The string to write
  424. * @private
  425. */
  426. write3String: function(item) {
  427. //<debug>
  428. if (!Ext.isString(item)) {
  429. Ext.log.warn("Encoder: writString argument is not a string.");
  430. }
  431. // </debug>
  432. if (item == "") {
  433. // special case for the empty string
  434. this.writeByte(6);
  435. // AMF3 string marker
  436. this.writeByte(1);
  437. } else // zero length string
  438. {
  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
  443. this.writeByte(6);
  444. // AMF3 string marker, only if we can actually write a string
  445. this.writeBytes(lenData);
  446. this.writeBytes(utf8Data);
  447. }
  448. },
  449. /**
  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
  454. */
  455. encodeXInt: function(value, byte_count) {
  456. var data = [],
  457. i;
  458. for (i = 0; i < byte_count; i++) {
  459. data.unshift(value & 255);
  460. value = value >> 8;
  461. }
  462. return data;
  463. },
  464. /**
  465. * Write an AMF0 UTF-8 string to the byte array
  466. * @param {String} item The string to write
  467. * @private
  468. */
  469. write0String: function(item) {
  470. //<debug>
  471. if (!Ext.isString(item)) {
  472. Ext.log.warn("Encoder: writString argument is not a string.");
  473. }
  474. // </debug>
  475. if (item == "") {
  476. // special case for the empty string
  477. this.writeByte(2);
  478. // AMF0 short string marker
  479. this.writeBytes([
  480. 0,
  481. 0
  482. ]);
  483. } else // zero length string
  484. {
  485. // first encode string to UTF-8.
  486. var utf8Data = this.encodeUtf8String(item);
  487. var encoding;
  488. var lenData;
  489. if (utf8Data.length <= 65535) {
  490. // short string
  491. encoding = 2;
  492. // short string
  493. lenData = this.encodeXInt(utf8Data.length, 2);
  494. } else {
  495. // long string
  496. encoding = 12;
  497. // long string
  498. lenData = this.encodeXInt(utf8Data.length, 4);
  499. }
  500. this.writeByte(encoding);
  501. // Approperiate AMF0 string marker
  502. this.writeBytes(lenData);
  503. this.writeBytes(utf8Data);
  504. }
  505. },
  506. /**
  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
  510. * @private
  511. */
  512. write3XmlWithType: function(xml, amfType) {
  513. //<debug>
  514. // We accept XML Documents, or strings
  515. if (amfType !== 7 && amfType !== 11) {
  516. Ext.raise("write XML with unknown AMF3 code: " + amfType);
  517. }
  518. if (!this.isXmlDocument(xml)) {
  519. Ext.log.warn("Encoder: write3XmlWithType argument is not an xml document.");
  520. }
  521. // </debug>
  522. var xmlStr = this.convertXmlToString(xml);
  523. if (xmlStr == "") {
  524. // special case for the empty string
  525. this.writeByte(amfType);
  526. // AMF3 XML marker
  527. this.writeByte(1);
  528. } else // zero length string
  529. {
  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);
  538. }
  539. },
  540. /**
  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
  545. */
  546. write3XmlDocument: function(xml) {
  547. this.write3XmlWithType(xml, 7);
  548. },
  549. /**
  550. * Writes an XML object (ActionScript 3 new XML object) in AMF3 format.
  551. * @param {Object} xml XML document (type Document typically) to write
  552. * @private
  553. */
  554. write3Xml: function(xml) {
  555. this.write3XmlWithType(xml, 11);
  556. },
  557. /**
  558. * Writes an XMLDocument in AMF0 format.
  559. * @param {Object} xml XML document (type Document typically) to write
  560. * @private
  561. */
  562. write0Xml: function(xml) {
  563. //<debug>
  564. // We accept XML Documents, or strings
  565. if (!this.isXmlDocument(xml)) {
  566. Ext.log.warn("Encoder: write0Xml argument is not an xml document.");
  567. }
  568. // </debug>
  569. var xmlStr = this.convertXmlToString(xml);
  570. this.writeByte(15);
  571. // AMF0 XML marker
  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);
  577. },
  578. /**
  579. * Writes a date in AMF3 format.
  580. * @param {Date} date the date object
  581. * @private
  582. */
  583. write3Date: function(date) {
  584. //<debug>
  585. if (!(date instanceof Date)) {
  586. Ext.raise("Serializing a non-date object as date: " + date);
  587. }
  588. //</debug>
  589. // For now, we don't use object references to just encode the date.
  590. this.writeByte(8);
  591. // AMF3 date marker
  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)));
  595. },
  596. /**
  597. * Writes a date in AMF0 format.
  598. * @param {Date} date the date object
  599. * @private
  600. */
  601. write0Date: function(date) {
  602. //<debug>
  603. if (!(date instanceof Date)) {
  604. Ext.raise("Serializing a non-date object as date: " + date);
  605. }
  606. //</debug>
  607. // For now, we don't use object references to just encode the date.
  608. this.writeByte(11);
  609. // AMF0 date marker
  610. this.writeBytes(this.encodeDouble(new Number(date)));
  611. this.writeBytes([
  612. 0,
  613. 0
  614. ]);
  615. },
  616. // placeholder for timezone, standard says to keep 0, flash actually writes data here
  617. /**
  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.
  621. * @private
  622. */
  623. write3Array: function(arr) {
  624. //<debug>
  625. if (!Ext.isArray(arr)) {
  626. Ext.raise("Serializing a non-array object as array: " + arr);
  627. }
  628. if (arr.length > 268435455) {
  629. Ext.raise("Array size too long to encode in AMF3: " + arr.length);
  630. }
  631. //</debug>
  632. // For now, we don't use object references to just encode the array.
  633. this.writeByte(9);
  634. // AMF3 array marker
  635. // encode ordered part of array's length
  636. var len = arr.length;
  637. len = len << 1;
  638. // right-most bit marks this as size
  639. len = len | 1;
  640. // mark it a size
  641. this.writeBytes(this.encode29Int(len));
  642. // The associative part of the array is ignored, so mark it as empty
  643. this.writeByte(1);
  644. // equivalent to an empty UTF-8 string
  645. // now iterate over the array, writing each element
  646. Ext.each(arr, function(x) {
  647. this.writeObject(x);
  648. }, this);
  649. },
  650. /**
  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
  654. */
  655. write0ObjectProperty: function(key, value) {
  656. if (!(key instanceof String) && (typeof (key) !== "string")) {
  657. // coerce to a string
  658. key = key + "";
  659. }
  660. // first encode the key to a short UTF-8.
  661. var utf8Data = this.encodeUtf8String(key);
  662. var lenData;
  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);
  668. },
  669. /**
  670. * Writes an associative array in AMF0 format.
  671. * @param {Array} arr the array to serialize.
  672. * @private
  673. */
  674. write0Array: function(arr) {
  675. var key;
  676. //<debug>
  677. if (!Ext.isArray(arr)) {
  678. Ext.raise("Serializing a non-array object as array: " + arr);
  679. }
  680. //</debug>
  681. /* This writes a strict array, but it seems Flex always writes out associative arrays, so mimic behavior
  682. // For now, we don't use object references to just encode the array.
  683. this.writeByte(0x0A); // AMF0 strict array marker
  684. // encode ordered part of array's length
  685. var len = arr.length;
  686. this.writeBytes(this.encodeXInt(len, 4));
  687. // now iterate over the array, writing each element
  688. Ext.each(arr, function(x) {this.writeObject(x);}, this);
  689. */
  690. // Use ECMA (associative) array type
  691. this.writeByte(8);
  692. // AMF0 ECMA-array marker
  693. // we need to know the length of the array before we write the serialized data
  694. // to the array. Better to traverse it twice than to copy all the byte data afterwards
  695. var total = 0;
  696. for (key in arr) {
  697. total++;
  698. }
  699. // Now write out the length of the array
  700. this.writeBytes(this.encodeXInt(total, 4));
  701. // then write out the data
  702. for (key in arr) {
  703. Ext.Array.push(this.write0ObjectProperty(key, arr[key]));
  704. }
  705. // And finally the object end marker
  706. this.writeBytes([
  707. 0,
  708. 0,
  709. 9
  710. ]);
  711. },
  712. /**
  713. * Writes a strict-array in AMF0 format. Unordered parts are ignored (e.g.
  714. * a["hello"] will not be encoded). This function is included for
  715. * completeness and will never be called by writeObject.
  716. * @param {Array} arr the array to serialize.
  717. */
  718. write0StrictArray: function(arr) {
  719. //<debug>
  720. if (!Ext.isArray(arr)) {
  721. Ext.raise("Serializing a non-array object as array: " + arr);
  722. }
  723. //</debug>
  724. // For now, we don't use object references to just encode the array.
  725. this.writeByte(10);
  726. // AMF0 strict array marker
  727. // encode ordered part of array's length
  728. var len = arr.length;
  729. this.writeBytes(this.encodeXInt(len, 4));
  730. // now iterate over the array, writing each element
  731. Ext.each(arr, function(x) {
  732. this.writeObject(x);
  733. }, this);
  734. },
  735. /**
  736. * Write a byte array in AMF3 format. This function is never called directly
  737. * by writeObject since there's no way to distinguish a regular array from a
  738. * byte array.
  739. * @param {Array} arr the object to serialize.
  740. */
  741. write3ByteArray: function(arr) {
  742. //<debug>
  743. if (!Ext.isArray(arr)) {
  744. Ext.raise("Serializing a non-array object as array: " + arr);
  745. }
  746. if (arr.length > 268435455) {
  747. Ext.raise("Array size too long to encode in AMF3: " + arr.length);
  748. }
  749. //</debug>
  750. this.writeByte(12);
  751. // Byte array marker
  752. // for now no support for references, so just dump the length and data
  753. // encode array's length
  754. var len = arr.length;
  755. len = len << 1;
  756. // right-most bit marks this as size
  757. len = len | 1;
  758. // mark it a size
  759. this.writeBytes(this.encode29Int(len));
  760. // and finally, dump the byte data
  761. this.writeBytes(arr);
  762. },
  763. /**
  764. * Write an object to the byte array in AMF3 format.
  765. * Since we don't have the class information form Flex, the object
  766. * is written as an anonymous object.
  767. * @param {Array} obj the object to serialize.
  768. * @private
  769. */
  770. write3GenericObject: function(obj) {
  771. var name;
  772. //<debug>
  773. if (!Ext.isObject(obj)) {
  774. Ext.raise("Serializing a non-object object: " + obj);
  775. }
  776. //</debug>
  777. // For now, we don't use object references so just encode the object.
  778. this.writeByte(10);
  779. // AMF3 object marker
  780. // The following 29-int is marked as follows (LSb to MSb) to signify a
  781. // "U29O-traits":
  782. // 1 - LSb marks an object value (1) or an object reference (0) which
  783. // is not yet supported.
  784. // 1 - trait values (1) or trait references (0) which are not supported
  785. // yet.
  786. // 0 - AMF3 format (0) or externalizable, i.e. object handles own
  787. // serialization (1) which is not supported.
  788. // 1 - dynamic (1) or not dynamic (0) object which is not relevant since
  789. // we pass all data as dynamic fields.
  790. // The reset of the bits signify how many sealed traits the object has.
  791. // we pass 0 since all data is passed as dynamic fields
  792. var oType = 11;
  793. // binary 1011
  794. this.writeByte(oType);
  795. // Next we pass the class name, which is the empty string for anonymous
  796. // objects
  797. this.writeByte(1);
  798. // Next are the sealed traits (of which we have none)
  799. // And now the name / value pairs of dynamic fields
  800. for (name in obj) {
  801. // Ensure that name is actually a string
  802. var newName = new String(name).valueOf();
  803. if (newName == "") {
  804. //<debug>
  805. Ext.raise("Can't encode non-string field name: " + name);
  806. }
  807. //</debug>
  808. var nameData = (this.encodeUtf8String(name));
  809. this.writeBytes(this.encode3Utf8StringLen(name));
  810. this.writeBytes(nameData);
  811. this.writeObject(obj[name]);
  812. }
  813. // And mark the end of the dynamic field section with the empty string
  814. this.writeByte(1);
  815. },
  816. /**
  817. * Write an object to the byte array in AMF0 format.
  818. * Since we don't have the class information form Flex, the object
  819. * is written as an anonymous object.
  820. * @param {Array} obj the object to serialize.
  821. * @private
  822. */
  823. write0GenericObject: function(obj) {
  824. var typed, amfType, key;
  825. //<debug>
  826. if (!Ext.isObject(obj)) {
  827. Ext.raise("Serializing a non-object object: " + obj);
  828. }
  829. //</debug>
  830. // For now, we don't use object references so just encode the object.
  831. // if the object is typed, the ID changes and we need to send the type ahead of the data
  832. typed = !!obj.$flexType;
  833. amfType = typed ? 16 : 3;
  834. // typed vs. untyped object
  835. this.writeByte(amfType);
  836. // AMF0 object marker
  837. // if typed, send object type
  838. if (typed) {
  839. this.write0ShortUtf8String(obj.$flexType);
  840. }
  841. // then write out the data. There's no counter, but other than that it's the same as an ECMA array
  842. for (key in obj) {
  843. if (key != "$flexType") {
  844. Ext.Array.push(this.write0ObjectProperty(key, obj[key]));
  845. }
  846. }
  847. // And finally the object end marker
  848. this.writeBytes([
  849. 0,
  850. 0,
  851. 9
  852. ]);
  853. },
  854. /**
  855. * Writes a byte to the byte array
  856. * @param {number} b Byte to write to the array
  857. * @private
  858. */
  859. writeByte: function(b) {
  860. //<debug>
  861. if (b < 0 || b > 255) {
  862. Ex.Error.raise('ERROR: Value being written outside byte range: ' + b);
  863. }
  864. //</debug>
  865. Ext.Array.push(this.bytes, b);
  866. },
  867. /**
  868. * Writes a byte array to the byte array
  869. * @param {number} b Byte array to append to the array
  870. * @private
  871. */
  872. writeBytes: function(b) {
  873. var i;
  874. //<debug>
  875. if (!Ext.isArray(b)) {
  876. Ext.raise("Decoder: writeBytes parameter is not an array: " + b);
  877. }
  878. for (i = 0; i < b.length; i++) {
  879. if (b[i] < 0 || b[i] > 255 || !Ext.isNumber(b[i])) {
  880. Ext.raise("ERROR: Value " + i + " being written outside byte range: " + b[i]);
  881. }
  882. }
  883. //</debug>
  884. Ext.Array.push(this.bytes, b);
  885. },
  886. /**
  887. * Converts an XML Document object to a string.
  888. * @param {Object} xml XML document (type Document typically) to convert
  889. * @return {String} A string representing the document
  890. * @private
  891. */
  892. convertXmlToString: function(xml) {
  893. var str;
  894. if (window.XMLSerializer) {
  895. // this is not IE, so:
  896. str = new window.XMLSerializer().serializeToString(xml);
  897. } else {
  898. //no XMLSerializer, might be an old version of IE
  899. str = xml.xml;
  900. }
  901. return str;
  902. },
  903. /**
  904. * Tries to determine if an object is an XML document
  905. * @param {Object} item to identify
  906. * @return {Boolean} true if it's an XML document, false otherwise
  907. */
  908. isXmlDocument: function(item) {
  909. // We can't test if Document is defined since IE just throws an exception. Instead rely on the DOMParser object
  910. if (window.DOMParser) {
  911. if (Ext.isDefined(item.doctype)) {
  912. return true;
  913. }
  914. }
  915. // Otherwise, check if it has an XML field
  916. if (Ext.isString(item.xml)) {
  917. // and we can get the xml
  918. return true;
  919. }
  920. return false;
  921. },
  922. /*
  923. * The encodeDouble function is derived from code from the typedarray.js library by Linden Research, Inc.
  924. *
  925. Copyright (c) 2010, Linden Research, Inc.
  926. Permission is hereby granted, free of charge, to any person obtaining a copy
  927. of this software and associated documentation files (the "Software"), to deal
  928. in the Software without restriction, including without limitation the rights
  929. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  930. copies of the Software, and to permit persons to whom the Software is
  931. furnished to do so, subject to the following conditions:
  932. The above copyright notice and this permission notice shall be included in
  933. all copies or substantial portions of the Software.
  934. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  935. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  936. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  937. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  938. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  939. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  940. THE SOFTWARE.
  941. */
  942. /**
  943. * Encodes an IEEE-754 double-precision number.
  944. * @param {Number} num the number to encode
  945. * @return {Array} byte array containing the encoded number
  946. * @private
  947. */
  948. encodeDouble: function(num) {
  949. var ebits = 11,
  950. fbits = 52;
  951. // double
  952. var bias = (1 << (ebits - 1)) - 1,
  953. s, e, f, ln, i, bits, str,
  954. data = [];
  955. // Precalculated values
  956. var K_INFINITY = [
  957. 127,
  958. 240,
  959. 0,
  960. 0,
  961. 0,
  962. 0,
  963. 0,
  964. 0
  965. ],
  966. K_NINFINITY = [
  967. 255,
  968. 240,
  969. 0,
  970. 0,
  971. 0,
  972. 0,
  973. 0,
  974. 0
  975. ],
  976. K_NAN = [
  977. 255,
  978. 248,
  979. 0,
  980. 0,
  981. 0,
  982. 0,
  983. 0,
  984. 0
  985. ];
  986. // Compute sign, exponent, fraction
  987. if (isNaN(num)) {
  988. data = K_NAN;
  989. } else if (num === Infinity) {
  990. data = K_INFINITY;
  991. } else if (num == -Infinity) {
  992. data = K_NINFINITY;
  993. } else {
  994. // not a special case, so encode
  995. if (num === 0) {
  996. e = 0;
  997. f = 0;
  998. s = (1 / num === -Infinity) ? 1 : 0;
  999. } else {
  1000. s = num < 0;
  1001. num = Math.abs(num);
  1002. if (num >= Math.pow(2, 1 - bias)) {
  1003. // Normalized
  1004. ln = Math.min(Math.floor(Math.log(num) / Math.LN2), bias);
  1005. e = ln + bias;
  1006. f = Math.round(num * Math.pow(2, fbits - ln) - Math.pow(2, fbits));
  1007. } else {
  1008. // Denormalized
  1009. e = 0;
  1010. f = Math.round(num / Math.pow(2, 1 - bias - fbits));
  1011. }
  1012. }
  1013. // Pack sign, exponent, fraction
  1014. bits = [];
  1015. for (i = fbits; i; i -= 1) {
  1016. bits.push(f % 2 ? 1 : 0);
  1017. f = Math.floor(f / 2);
  1018. }
  1019. for (i = ebits; i; i -= 1) {
  1020. bits.push(e % 2 ? 1 : 0);
  1021. e = Math.floor(e / 2);
  1022. }
  1023. bits.push(s ? 1 : 0);
  1024. bits.reverse();
  1025. str = bits.join('');
  1026. // Bits to bytes
  1027. data = [];
  1028. while (str.length) {
  1029. data.push(parseInt(str.substring(0, 8), 2));
  1030. str = str.substring(8);
  1031. }
  1032. }
  1033. return data;
  1034. },
  1035. /**
  1036. * Writes a short UTF8 string preceded with a 16-bit length.
  1037. * @param {String} str the string to write
  1038. */
  1039. write0ShortUtf8String: function(str) {
  1040. var utf8Data = this.encodeUtf8String(str),
  1041. lenData;
  1042. lenData = this.encodeXInt(utf8Data.length, 2);
  1043. this.writeBytes(lenData);
  1044. this.writeBytes(utf8Data);
  1045. },
  1046. /**
  1047. * Writes an AMF packet to the byte array
  1048. * @param {Array} headers the headers to serialize. Each item in the array
  1049. * should be an object with three fields:
  1050. * name, mustUnderstand, value
  1051. * @param {Array} messages the messages to serialize. Each item in the array
  1052. * should be an object with three fields:
  1053. * targetUri, responseUri, body
  1054. */
  1055. writeAmfPacket: function(headers, messages) {
  1056. var i;
  1057. //<debug>
  1058. if (this.config.format != 0) {
  1059. Ext.raise("Trying to write a packet on an AMF3 Encoder. Only AMF0 is supported!");
  1060. }
  1061. if (!Ext.isArray(headers)) {
  1062. Ext.raise("headers is not an array: " + headers);
  1063. }
  1064. if (!Ext.isArray(messages)) {
  1065. Ext.raise("messages is not an array: " + messages);
  1066. }
  1067. //</debug>
  1068. // Write Packet marker
  1069. this.writeBytes([
  1070. 0,
  1071. 0
  1072. ]);
  1073. // AMF 0 version for this packet.
  1074. // Write header count
  1075. this.writeBytes(this.encodeXInt(headers.length, 2));
  1076. // And actual headers
  1077. for (i in headers) {
  1078. this.writeAmfHeader(headers[i].name, headers[i].mustUnderstand, headers[i].value);
  1079. }
  1080. // Write message count
  1081. this.writeBytes(this.encodeXInt(messages.length, 2));
  1082. // And actual messages
  1083. for (i in messages) {
  1084. this.writeAmfMessage(messages[i].targetUri, messages[i].responseUri, messages[i].body);
  1085. }
  1086. },
  1087. /**
  1088. * Write an AMF header to the byte array. AMF headers are always encoded in AMF0.
  1089. * @param {String} headerName the header name
  1090. * @param {Boolean} mustUnderstand true if the receiver must understand this header or else reject it, false otherwise
  1091. * @param {Object} value the value to serialize. Must be an object that can be serialized by AMF
  1092. * @private
  1093. */
  1094. writeAmfHeader: function(headerName, mustUnderstand, value) {
  1095. //<debug>
  1096. if (this.config.format != 0) {
  1097. Ext.raise("Trying to write a header on an AMF3 Encoder. Only AMF0 is supported!");
  1098. }
  1099. if (!Ext.isString(headerName)) {
  1100. Ext.raise("targetURI is not a string: " + targetUri);
  1101. }
  1102. if ((typeof (mustUnderstand) !== "boolean") && !Ext.isBoolean(mustUnderstand)) {
  1103. Ext.raise("mustUnderstand is not a boolean value: " + mustUnderstand);
  1104. }
  1105. //</debug>
  1106. // write header name
  1107. this.write0ShortUtf8String(headerName);
  1108. // write must understand byte
  1109. var mu = mustUnderstand ? 1 : 0;
  1110. this.writeByte(mu);
  1111. // next write the header length of -1 (undetermined) to the stream
  1112. this.writeBytes(this.encodeXInt(-1, 4));
  1113. // write value
  1114. this.writeObject(value);
  1115. },
  1116. /**
  1117. * Writes an AMF message to the byte array. AMF messages are always encoded in AMF0.
  1118. * @param {String} targetUri the class / method to call
  1119. * @param {String} responseUri the response should call here
  1120. * @param {Array} body the parameters to pass to the called method, wrapped in an array
  1121. * @private
  1122. */
  1123. writeAmfMessage: function(targetUri, responseUri, body) {
  1124. //<debug>
  1125. if (this.config.format != 0) {
  1126. Ext.raise("Trying to write a message on an AMF3 Encoder. Only AMF0 is supported!");
  1127. }
  1128. if (!Ext.isString(targetUri)) {
  1129. Ext.raise("targetURI is not a string: " + targetUri);
  1130. }
  1131. if (!Ext.isString(responseUri)) {
  1132. Ext.raise("targetURI is not a string: " + responseUri);
  1133. }
  1134. if (!Ext.isArray(body)) {
  1135. Ext.raise("body is not an array: " + typeof (body));
  1136. }
  1137. //</debug>
  1138. // write target URI
  1139. this.write0ShortUtf8String(targetUri);
  1140. // write response URI
  1141. this.write0ShortUtf8String(responseUri);
  1142. // next write the message length of -1 (undetermined) to the stream
  1143. this.writeBytes(this.encodeXInt(-1, 4));
  1144. // write the paramters
  1145. this.write0StrictArray(body);
  1146. }
  1147. });
  1148. // @tag enterprise
  1149. /**
  1150. * @class Ext.data.amf.Packet
  1151. * This class represents an Action Message Format (AMF) Packet. It contains all
  1152. * the logic required to decode an AMF Packet from a byte array.
  1153. * To decode a Packet, first construct a Packet:
  1154. *
  1155. * var packet = Ext.create('Ext.data.amf.Packet');
  1156. *
  1157. * Then use the decode method to decode an AMF byte array:
  1158. *
  1159. * packet.decode(bytes);
  1160. *
  1161. * where "bytes" is a Uint8Array or an array of numbers representing the binary
  1162. * AMF data.
  1163. *
  1164. * To access the decoded data use the #version, #headers, and #messages properties:
  1165. *
  1166. * console.log(packet.version, packet.headers, packet.messages);
  1167. *
  1168. * For more information on working with AMF data please refer to the
  1169. * [AMF Guide](../guides/backend_connectors/amf.html).
  1170. */
  1171. Ext.define('Ext.data.amf.Packet', function() {
  1172. var twoPowN52 = Math.pow(2, -52),
  1173. twoPow8 = Math.pow(2, 8),
  1174. pos = 0,
  1175. bytes, strings, objects, traits;
  1176. return {
  1177. /**
  1178. * @property {Array} headers
  1179. * @readonly
  1180. * The decoded headers. Each header has the following properties:
  1181. *
  1182. * - `name`: String
  1183. * The header name. Typically identifies a remote operation or method to
  1184. * be invoked by this context header.
  1185. * - `mustUnderstand`: Boolean
  1186. * If `true` this flag instructs the endpoint to abort and generate an
  1187. * error if the header is not understood.
  1188. * - `byteLength`: Number
  1189. * If the byte-length of a header is known it can be specified to optimize
  1190. * memory allocation at the remote endpoint.
  1191. * - `value`: Mixed
  1192. * The header value
  1193. */
  1194. /**
  1195. * @property {Array} messages
  1196. * @readonly
  1197. * The decoded messages. Each message has the following properties:
  1198. *
  1199. * - `targetURI`: String
  1200. * Describes which operation, function, or method is to be remotely
  1201. * invoked.
  1202. * - `responseURI`: String
  1203. * A unique operation name
  1204. * - `byteLength`: Number
  1205. * Optional byte-length of the message body
  1206. * - `body`: Mixed
  1207. * The message body
  1208. */
  1209. /**
  1210. * @property {Number} version
  1211. * @readonly
  1212. * The AMF version number (0 or 3)
  1213. */
  1214. /**
  1215. * Mapping of AMF data types to the names of the methods responsible for
  1216. * reading them.
  1217. * @private
  1218. */
  1219. typeMap: {
  1220. // AMF0 mapping
  1221. 0: {
  1222. 0: 'readDouble',
  1223. 1: 'readBoolean',
  1224. 2: 'readAmf0String',
  1225. 3: 'readAmf0Object',
  1226. 5: 'readNull',
  1227. 6: 'readUndefined',
  1228. 7: 'readReference',
  1229. 8: 'readEcmaArray',
  1230. 10: 'readStrictArray',
  1231. 11: 'readAmf0Date',
  1232. 12: 'readLongString',
  1233. 13: 'readUnsupported',
  1234. 15: 'readAmf0Xml',
  1235. 16: 'readTypedObject'
  1236. },
  1237. // AMF3 mapping
  1238. 3: {
  1239. 0: 'readUndefined',
  1240. 1: 'readNull',
  1241. 2: 'readFalse',
  1242. 3: 'readTrue',
  1243. 4: 'readUInt29',
  1244. 5: 'readDouble',
  1245. 6: 'readAmf3String',
  1246. 7: 'readAmf3Xml',
  1247. 8: 'readAmf3Date',
  1248. 9: 'readAmf3Array',
  1249. 10: 'readAmf3Object',
  1250. 11: 'readAmf3Xml',
  1251. 12: 'readByteArray'
  1252. }
  1253. },
  1254. /**
  1255. * Decodes an AMF btye array and sets the decoded data as the
  1256. * Packet's #version, #headers, and #messages properties
  1257. * @param {Array} byteArray A byte array containing the encoded AMF data.
  1258. * @return {Ext.data.amf.Packet} this AMF Packet
  1259. */
  1260. decode: function(byteArray) {
  1261. var me = this,
  1262. headers = me.headers = [],
  1263. messages = me.messages = [],
  1264. headerCount, messageCount;
  1265. pos = 0;
  1266. bytes = me.bytes = byteArray;
  1267. // The strings array holds references to all of the deserialized
  1268. // AMF3 strings for a given header value or message body so that
  1269. // repeat instances of the same string can be deserialized by
  1270. // reference
  1271. strings = me.strings = [];
  1272. // The objects array holds references to deserialized objects so
  1273. // that repeat occurrences of the same object instance in the byte
  1274. // array can be deserialized by reference.
  1275. // If version is AMF0 this array holds anonymous objects, typed
  1276. // objects, arrays, and ecma-arrays.
  1277. // If version is AMF3 this array holds instances of Object, Array, XML,
  1278. // XMLDocument, ByteArray, Date, and instances of user defined Classes
  1279. objects = me.objects = [];
  1280. // The traits array holds references to the "traits" (the
  1281. // characteristics of objects that define a strong type such as the
  1282. // class name and public member names) of deserialized AMF3 objects
  1283. // so that if they are repeated they can be deserialized by reference.
  1284. traits = me.traits = [];
  1285. // The first two bytes of an AMF packet contain the AMF version
  1286. // as an unsigned 16 bit integer.
  1287. me.version = me.readUInt(2);
  1288. // the next 2 bytes contain the header count
  1289. for (headerCount = me.readUInt(2); headerCount--; ) {
  1290. headers.push({
  1291. name: me.readAmf0String(),
  1292. mustUnderstand: me.readBoolean(),
  1293. byteLength: me.readUInt(4),
  1294. value: me.readValue()
  1295. });
  1296. // reset references (reference indices are local to each header)
  1297. strings = me.strings = [];
  1298. objects = me.objects = [];
  1299. traits = me.traits = [];
  1300. }
  1301. // The 2 bytes immediately after the header contain the message count.
  1302. for (messageCount = me.readUInt(2); messageCount--; ) {
  1303. messages.push({
  1304. targetURI: me.readAmf0String(),
  1305. responseURI: me.readAmf0String(),
  1306. byteLength: me.readUInt(4),
  1307. body: me.readValue()
  1308. });
  1309. // reset references (reference indices are local to each message)
  1310. strings = me.strings = [];
  1311. objects = me.objects = [];
  1312. traits = me.traits = [];
  1313. }
  1314. // reset the pointer
  1315. pos = 0;
  1316. // null the bytes array and reference arrays to free up memory.
  1317. bytes = strings = objects = traits = me.bytes = me.strings = me.objects = me.traits = null;
  1318. return me;
  1319. },
  1320. /**
  1321. * Decodes an AMF3 byte array and that has one value and returns it.
  1322. * Note: Destroys previously stored data in this Packet.
  1323. * @param {Array} byteArray A byte array containing the encoded AMF data.
  1324. * @return {Object} the decoded object
  1325. */
  1326. decodeValue: function(byteArray) {
  1327. var me = this;
  1328. bytes = me.bytes = byteArray;
  1329. // reset the pointer
  1330. pos = 0;
  1331. // The first two bytes of an AMF packet contain the AMF version
  1332. // as an unsigned 16 bit integer.
  1333. me.version = 3;
  1334. // The strings array holds references to all of the deserialized
  1335. // AMF3 strings for a given header value or message body so that
  1336. // repeat instances of the same string can be deserialized by
  1337. // reference
  1338. strings = me.strings = [];
  1339. // The objects array holds references to deserialized objects so
  1340. // that repeat occurrences of the same object instance in the byte
  1341. // array can be deserialized by reference.
  1342. // If version is AMF0 this array holds anonymous objects, typed
  1343. // objects, arrays, and ecma-arrays.
  1344. // If version is AMF3 this array holds instances of Object, Array, XML,
  1345. // XMLDocument, ByteArray, Date, and instances of user defined Classes
  1346. objects = me.objects = [];
  1347. // The traits array holds references to the "traits" (the
  1348. // characteristics of objects that define a strong type such as the
  1349. // class name and public member names) of deserialized AMF3 objects
  1350. // so that if they are repeated they can be deserialized by reference.
  1351. traits = me.traits = [];
  1352. // read one value and return it
  1353. return me.readValue();
  1354. },
  1355. /**
  1356. * Parses an xml string and returns an xml document
  1357. * @private
  1358. * @param {String} xml
  1359. */
  1360. parseXml: function(xml) {
  1361. var doc;
  1362. if (window.DOMParser) {
  1363. doc = (new DOMParser()).parseFromString(xml, "text/xml");
  1364. } else {
  1365. doc = new ActiveXObject("Microsoft.XMLDOM");
  1366. doc.loadXML(xml);
  1367. }
  1368. return doc;
  1369. },
  1370. /**
  1371. * Reads an AMF0 date from the byte array
  1372. * @private
  1373. */
  1374. readAmf0Date: function() {
  1375. var date = new Date(this.readDouble());
  1376. // An AMF0 date type ends with a 16 bit integer time-zone, but
  1377. // according to the spec time-zone is "reserved, not supported,
  1378. // should be set to 0x000".
  1379. pos += 2;
  1380. // discard the time zone
  1381. return date;
  1382. },
  1383. /**
  1384. * Reads an AMF0 Object from the byte array
  1385. * @private
  1386. */
  1387. readAmf0Object: function(obj) {
  1388. var me = this,
  1389. key;
  1390. obj = obj || {};
  1391. // add the object to the objects array so that the AMF0 reference
  1392. // type decoder can refer to it by index if needed.
  1393. objects.push(obj);
  1394. // An AMF0 object consists of a series of string keys and variable-
  1395. // type values. The end of the series is marked by an empty string
  1396. // followed by the object-end marker (9).
  1397. while ((key = me.readAmf0String()) || bytes[pos] !== 9) {
  1398. obj[key] = me.readValue();
  1399. }
  1400. // move the pointer past the object-end marker
  1401. pos++;
  1402. return obj;
  1403. },
  1404. /**
  1405. * Reads an AMF0 string from the byte array
  1406. * @private
  1407. */
  1408. readAmf0String: function() {
  1409. // AMF0 strings begin with a 16 bit byte-length header.
  1410. return this.readUtf8(this.readUInt(2));
  1411. },
  1412. readAmf0Xml: function() {
  1413. return this.parseXml(this.readLongString());
  1414. },
  1415. readAmf3Array: function() {
  1416. var me = this,
  1417. header = me.readUInt29(),
  1418. count, key, array, i;
  1419. // AMF considers Arrays in two parts, the dense portion and the
  1420. // associative portion. The binary representation of the associative
  1421. // portion consists of name/value pairs (potentially none) terminated
  1422. // by an empty string. The binary representation of the dense portion
  1423. // is the size of the dense portion (potentially zero) followed by an
  1424. // ordered list of values (potentially none).
  1425. if (header & 1) {
  1426. // If the first (low) bit is a 1 read an array instance. The
  1427. // remaining 1-28 bits are used to encode the length of the
  1428. // dense portion of the array.
  1429. count = (header >> 1);
  1430. // First read the associative portion of the array (if any). If
  1431. // there is an associative portion, the array will be read as a
  1432. // javascript object, otherwise it will be a javascript array.
  1433. key = me.readAmf3String();
  1434. if (key) {
  1435. // First key is not an empty string - this is an associative
  1436. // array. Read keys and values from the byte array until
  1437. // we get to an empty string key
  1438. array = {};
  1439. objects.push(array);
  1440. do {
  1441. array[key] = me.readValue();
  1442. } while ((key = me.readAmf3String()));
  1443. // The dense portion of the array is then read into the
  1444. // associative object, keyed by ordinal index.
  1445. for (i = 0; i < count; i++) {
  1446. array[i] = me.readValue();
  1447. }
  1448. } else {
  1449. // First key is an empty string - this is an array with
  1450. // ordinal indices.
  1451. array = [];
  1452. objects.push(array);
  1453. for (i = 0; i < count; i++) {
  1454. array.push(me.readValue());
  1455. }
  1456. }
  1457. } else {
  1458. // If the first (low) bit is a 0 read an array reference. The
  1459. // remaining 1-28 bits are used to encode the reference index
  1460. array = objects[header >> 1];
  1461. }
  1462. return array;
  1463. },
  1464. /**
  1465. * Reads an AMF3 date from the byte array
  1466. * @private
  1467. */
  1468. readAmf3Date: function() {
  1469. var me = this,
  1470. header = me.readUInt29(),
  1471. date;
  1472. if (header & 1) {
  1473. // If the first (low) bit is a 1, this is a date instance.
  1474. date = new Date(me.readDouble());
  1475. objects.push(date);
  1476. } else {
  1477. // If the first (low) bit is a 0, this is a date reference.
  1478. // The remaining 1-28 bits encode the reference index
  1479. date = objects[header >> 1];
  1480. }
  1481. return date;
  1482. },
  1483. /**
  1484. * Reads an AMF3 object from the byte array
  1485. * @private
  1486. */
  1487. readAmf3Object: function() {
  1488. var me = this,
  1489. header = me.readUInt29(),
  1490. members = [],
  1491. headerLast3Bits, memberCount, className, dynamic, objectTraits, obj, key, klass, i;
  1492. // There are 3 different types of object headers, distinguishable
  1493. // by the 1-3 least significant bits. All object instances have
  1494. // a 1 in the low bit position, while references have a 0:
  1495. //
  1496. // 0 : object reference
  1497. // 011 : traits
  1498. // 01 : traits-ref
  1499. // 111 : traits-ext
  1500. if (header & 1) {
  1501. // first (low) bit of 1, denotes an encoded object instance
  1502. // The next string is the class name.
  1503. headerLast3Bits = (header & 7);
  1504. if (headerLast3Bits === 3) {
  1505. // If the 3 least significant bits of the header are "011"
  1506. // then trait information follows.
  1507. className = me.readAmf3String();
  1508. // A 1 in the header's 4th least significant byte position
  1509. // indicates that dynamic members may follow the sealed
  1510. // members.
  1511. dynamic = !!(header & 8);
  1512. // Shift off the 4 least significant bits, and the remaining
  1513. // 1-25 bits encode the number of sealed member names. Read
  1514. // as many strings from the byte array as member names.
  1515. memberCount = (header >> 4);
  1516. for (i = 0; i < memberCount; i++) {
  1517. members.push(me.readAmf3String());
  1518. }
  1519. objectTraits = {
  1520. className: className,
  1521. dynamic: dynamic,
  1522. members: members
  1523. };
  1524. // An objects traits are cached in the traits array enabling
  1525. // the traits for a given class to only be encoded once for
  1526. // a series of instances.
  1527. traits.push(objectTraits);
  1528. } else if ((header & 3) === 1) {
  1529. // If the 2 least significant bits are "01", then a traits
  1530. // reference follows. The remaining 1-27 bits are used
  1531. // to encode the trait reference index.
  1532. objectTraits = traits[header >> 2];
  1533. className = objectTraits.className;
  1534. dynamic = objectTraits.dynamic;
  1535. members = objectTraits.members;
  1536. memberCount = members.length;
  1537. } else if (headerLast3Bits === 7) {}
  1538. // if the 3 lease significant bits are "111" then
  1539. // externalizable trait data follows
  1540. // TODO: implement externalizable traits
  1541. if (className) {
  1542. klass = Ext.ClassManager.getByAlias('amf.' + className);
  1543. obj = klass ? new klass() : {
  1544. $className: className
  1545. };
  1546. } else {
  1547. obj = {};
  1548. }
  1549. objects.push(obj);
  1550. // read the sealed member values
  1551. for (i = 0; i < memberCount; i++) {
  1552. obj[members[i]] = me.readValue();
  1553. }
  1554. if (dynamic) {
  1555. // If the dynamic flag is set, dynamic members may follow
  1556. // the sealed members. Read key/value pairs until we
  1557. // encounter an empty string key signifying the end of the
  1558. // dynamic members.
  1559. while ((key = me.readAmf3String())) {
  1560. obj[key] = me.readValue();
  1561. }
  1562. }
  1563. // finally, check if we need to convert this class
  1564. if ((!klass) && this.converters[className]) {
  1565. obj = this.converters[className](obj);
  1566. }
  1567. } else {
  1568. // If the first (low) bit of the header is a 0, this is an
  1569. // object reference. The remaining 1-28 significant bits are
  1570. // used to encode an object reference index.
  1571. obj = objects[header >> 1];
  1572. }
  1573. return obj;
  1574. },
  1575. /**
  1576. * Reads an AMF3 string from the byte array
  1577. * @private
  1578. */
  1579. readAmf3String: function() {
  1580. var me = this,
  1581. header = me.readUInt29(),
  1582. value;
  1583. if (header & 1) {
  1584. // If the first (low) bit is a 1, this is a string literal.
  1585. // Discard the low bit. The remaining 1-28 bits are used to
  1586. // encode the string's byte-length.
  1587. value = me.readUtf8(header >> 1);
  1588. if (value) {
  1589. // the emtpy string is never encoded by reference
  1590. strings.push(value);
  1591. }
  1592. return value;
  1593. } else {
  1594. // If the first (low) bit is a 0, this is a string reference.
  1595. // Discard the low bit, then look up and return the reference
  1596. // from the strings array using the remaining 1-28 bits as the
  1597. // index.
  1598. return strings[header >> 1];
  1599. }
  1600. },
  1601. /**
  1602. * Reads an AMF3 XMLDocument type or XML type from the byte array
  1603. * @private
  1604. */
  1605. readAmf3Xml: function() {
  1606. var me = this,
  1607. header = me.readUInt29(),
  1608. doc;
  1609. if (header & 1) {
  1610. // If the first (low) bit is a 1, this is an xml instance. The
  1611. // remaining 1-28 bits encode the byte-length of the xml string.
  1612. doc = me.parseXml(me.readUtf8(header >> 1));
  1613. objects.push(doc);
  1614. } else {
  1615. // if the first (low) bit is a 1, this is an xml reference. The
  1616. // remaining 1-28 bits encode the reference index.
  1617. doc = objects[header >> 1];
  1618. }
  1619. return doc;
  1620. },
  1621. /**
  1622. * Reads an AMF0 boolean from the byte array
  1623. * @private
  1624. */
  1625. readBoolean: function() {
  1626. return !!bytes[pos++];
  1627. },
  1628. /**
  1629. * Reads an AMF3 ByteArray type from the byte array
  1630. * @private
  1631. */
  1632. readByteArray: function() {
  1633. var header = this.readUInt29(),
  1634. byteArray, end;
  1635. if (header & 1) {
  1636. // If the first (low) bit is a 1, this is a ByteArray instance.
  1637. // The remaining 1-28 bits encode the ByteArray's byte-length.
  1638. end = pos + (header >> 1);
  1639. // Depending on the browser, "bytes" may be either a Uint8Array
  1640. // or an Array. Uint8Arrays don't have Array methods, so
  1641. // we have to use Array.prototype.slice to get the byteArray
  1642. byteArray = Array.prototype.slice.call(bytes, pos, end);
  1643. objects.push(byteArray);
  1644. // move the pointer to the first byte after the byteArray that
  1645. // was just read
  1646. pos = end;
  1647. } else {
  1648. // if the first (low) bit is a 1, this is a ByteArray reference.
  1649. // The remaining 1-28 bits encode the reference index.
  1650. byteArray = objects[header >> 1];
  1651. }
  1652. return byteArray;
  1653. },
  1654. /**
  1655. * Reads a IEEE 754 double-precision binary floating-point number
  1656. * @private
  1657. */
  1658. readDouble: function() {
  1659. var byte1 = bytes[pos++],
  1660. byte2 = bytes[pos++],
  1661. // the first bit of byte1 is the sign (0 = positive, 1 = negative.
  1662. // We read this bit by shifting the 7 least significant bits of
  1663. // byte1 off to the right.
  1664. sign = (byte1 >> 7) ? -1 : 1,
  1665. // the exponent takes up the next 11 bits.
  1666. exponent = (// extract the 7 least significant bits from byte1 and then
  1667. // shift them left by 4 bits to make room for the 4 remaining
  1668. // bits from byte 2
  1669. ((byte1 & 127) << 4) | (// add the 4 most significant bits from byte 2 to complete
  1670. // the exponent
  1671. byte2 >> 4)),
  1672. // the remaining 52 bits make up the significand. read the 4
  1673. // least significant bytes of byte 2 to begin the significand
  1674. significand = (byte2 & 15),
  1675. // The most significant bit of the significand is always 1 for
  1676. // a normalized number, therefore it is not stored. This bit is
  1677. // referred to as the "hidden bit". The true bit width of the
  1678. // significand is 53 if you include the hidden bit. An exponent
  1679. // of 0 indicates that this is a subnormal number, and subnormal
  1680. // numbers always have a 0 hidden bit.
  1681. hiddenBit = exponent ? 1 : 0,
  1682. i = 6;
  1683. // The operands of all bitwise operators in javascript are converted
  1684. // to signed 32 bit integers. It is therefore impossible to construct
  1685. // the 52 bit significand by repeatedly shifting its bits and then
  1686. // bitwise OR-ing the result with the the next byte. To work around
  1687. // this issue, we repeatedly multiply the significand by 2^8 which
  1688. // produces the same result as (significand << 8), then we add the
  1689. // next byte, which has the same result as a bitwise OR.
  1690. while (i--) {
  1691. significand = (significand * twoPow8) + bytes[pos++];
  1692. }
  1693. if (!exponent) {
  1694. if (!significand) {
  1695. // if both exponent and significand are 0, the number is 0
  1696. return 0;
  1697. }
  1698. // If the exponent is 0, but the significand is not 0, this
  1699. // is a subnormal number. Subnormal numbers are encoded with a
  1700. // biased exponent of 0, but are interpreted with the value of
  1701. // the smallest allowed exponent, which is one greater.
  1702. exponent = 1;
  1703. }
  1704. // 0x7FF (2047) is a special exponent value that represents infinity
  1705. // if the significand is 0, and NaN if the significand is not 0
  1706. if (exponent === 2047) {
  1707. return significand ? NaN : (Infinity * sign);
  1708. }
  1709. return sign * // The exponent is encoded using an offset binary
  1710. // representation with the zero offset being 0x3FF (1023),
  1711. // so we have to subtract 0x3FF to get the true exponent
  1712. Math.pow(2, exponent - 1023) * (// convert the significand to its decimal value by multiplying
  1713. // it by 2^52 and then add the hidden bit
  1714. hiddenBit + twoPowN52 * significand);
  1715. },
  1716. /**
  1717. * Reads an AMF0 ECMA Array from the byte array
  1718. * @private
  1719. */
  1720. readEcmaArray: function() {
  1721. // An ecma array type is encoded exactly like an anonymous object
  1722. // with the exception that it has a 32 bit "count" at the beginning.
  1723. // We handle emca arrays by just throwing away the count and then
  1724. // letting the object decoder handle the rest.
  1725. pos += 4;
  1726. return this.readAmf0Object();
  1727. },
  1728. /**
  1729. * Returns false. Used for reading the false type
  1730. * @private
  1731. */
  1732. readFalse: function() {
  1733. return false;
  1734. },
  1735. /**
  1736. * Reads a long string (longer than 65535 bytes) from the byte array
  1737. * @private
  1738. */
  1739. readLongString: function() {
  1740. // long strings begin with a 32 bit byte-length header.
  1741. return this.readUtf8(this.readUInt(4));
  1742. },
  1743. /**
  1744. * Returns null. Used for reading the null type
  1745. * @private
  1746. */
  1747. readNull: function() {
  1748. return null;
  1749. },
  1750. /**
  1751. * Reads a reference from the byte array. Reference types are used to
  1752. * avoid duplicating data if the same instance of a complex object (which
  1753. * is defined in AMF0 as an anonymous object, typed object, array, or
  1754. * ecma-array) is included in the data more than once.
  1755. * @private
  1756. */
  1757. readReference: function() {
  1758. // a reference type contains a single 16 bit integer that represents
  1759. // the index of an already deserialized object in the objects array
  1760. return objects[this.readUInt(2)];
  1761. },
  1762. /**
  1763. * Reads an AMF0 strict array (an array with ordinal indices)
  1764. * @private
  1765. */
  1766. readStrictArray: function() {
  1767. var me = this,
  1768. len = me.readUInt(4),
  1769. arr = [];
  1770. objects.push(arr);
  1771. while (len--) {
  1772. arr.push(me.readValue());
  1773. }
  1774. return arr;
  1775. },
  1776. /**
  1777. * Returns true. Used for reading the true type
  1778. * @private
  1779. */
  1780. readTrue: Ext.returnTrue,
  1781. /**
  1782. * Reads an AMF0 typed object from the byte array
  1783. * @private
  1784. */
  1785. readTypedObject: function() {
  1786. var me = this,
  1787. className = me.readAmf0String(),
  1788. klass, instance, modified;
  1789. klass = Ext.ClassManager.getByAlias('amf.' + className);
  1790. instance = klass ? new klass() : {
  1791. $className: className
  1792. };
  1793. // if there is no klass, mark the classname for easier parsing of returned results
  1794. modified = me.readAmf0Object(instance);
  1795. // check if we need to convert this class
  1796. if ((!klass) && this.converters[className]) {
  1797. modified = this.converters[className](instance);
  1798. }
  1799. return modified;
  1800. },
  1801. /**
  1802. * Reads an unsigned integer from the byte array
  1803. * @private
  1804. * @param {Number} byteCount the number of bytes to read, e.g. 2 to read
  1805. * a 16 bit integer, 4 to read a 32 bit integer, etc.
  1806. * @return {Number}
  1807. */
  1808. readUInt: function(byteCount) {
  1809. var i = 1,
  1810. result;
  1811. // read the first byte
  1812. result = bytes[pos++];
  1813. // if this is a multi-byte int, loop over the remaining bytes
  1814. for (; i < byteCount; ++i) {
  1815. // shift the result 8 bits to the left and add the next byte.
  1816. result = (result << 8) | bytes[pos++];
  1817. }
  1818. return result;
  1819. },
  1820. /**
  1821. * Reads an unsigned 29-bit integer from the byte array.
  1822. * AMF 3 makes use of a special compact format for writing integers to
  1823. * reduce the number of bytes required for encoding. As with a normal
  1824. * 32-bit integer, up to 4 bytes are required to hold the value however
  1825. * the high bit of the first 3 bytes are used as flags to determine
  1826. * whether the next byte is part of the integer. With up to 3 bits of
  1827. * the 32 bits being used as flags, only 29 significant bits remain for
  1828. * encoding an integer. This means the largest unsigned integer value
  1829. * that can be represented is 2^29-1.
  1830. *
  1831. * (hex) : (binary)
  1832. * 0x00000000 - 0x0000007F : 0xxxxxxx
  1833. * 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx
  1834. * 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx
  1835. * 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx
  1836. * @private
  1837. * @return {Number}
  1838. */
  1839. readUInt29: function() {
  1840. var value = bytes[pos++],
  1841. nextByte;
  1842. if (value & 128) {
  1843. // if the high order bit of the first byte is a 1, the next byte
  1844. // is also part of this integer.
  1845. nextByte = bytes[pos++];
  1846. // remove the high order bit from both bytes before combining them
  1847. value = ((value & 127) << 7) | (nextByte & 127);
  1848. if (nextByte & 128) {
  1849. // if the high order byte of the 2nd byte is a 1, then
  1850. // there is a 3rd byte
  1851. nextByte = bytes[pos++];
  1852. // remove the high order bit from the 3rd byte before
  1853. // adding it to the value
  1854. value = (value << 7) | (nextByte & 127);
  1855. if (nextByte & 128) {
  1856. // 4th byte is also part of the integer
  1857. nextByte = bytes[pos++];
  1858. // use all 8 bits of the 4th byte
  1859. value = (value << 8) | nextByte;
  1860. }
  1861. }
  1862. }
  1863. return value;
  1864. },
  1865. /**
  1866. * @method
  1867. * Returns undefined. Used for reading the undefined type
  1868. * @private
  1869. */
  1870. readUndefined: Ext.emptyFn,
  1871. /**
  1872. * @method
  1873. * Returns undefined. Used for reading the unsupported type
  1874. * @private
  1875. */
  1876. readUnsupported: Ext.emptyFn,
  1877. /**
  1878. * Reads a UTF-8 string from the byte array
  1879. * @private
  1880. * @param {Number} byteLength The number of bytes to read
  1881. * @return {String}
  1882. */
  1883. readUtf8: function(byteLength) {
  1884. var end = pos + byteLength,
  1885. // the string's end position
  1886. chars = [],
  1887. charCount = 0,
  1888. maxCharCount = 65535,
  1889. charArrayCount = 1,
  1890. result = [],
  1891. i = 0,
  1892. charArrays, byteCount, charCode;
  1893. charArrays = [
  1894. chars
  1895. ];
  1896. // UTF-8 characters may be encoded using 1-4 bytes. The number of
  1897. // bytes that a character consumes is determined by reading the
  1898. // leading byte. Values 0-127 in the leading byte indicate a single-
  1899. // byte ASCII-compatible character. Values 192-223 (bytes with "110"
  1900. // in the high-order position) indicate a 2-byte character, values
  1901. // 224-239 (bytes with "1110" in the high-order position) indicate a
  1902. // 3-byte character, and values 240-247 (bytes with "11110" in the
  1903. // high-order position) indicate a 4-byte character. The remaining
  1904. // bits of the leading byte hold bits of the encoded character, with
  1905. // leading zeros if necessary.
  1906. //
  1907. // The continuation bytes all have "10" in the high-order position,
  1908. // which means only the 6 least significant bits of continuation
  1909. // bytes are available to hold the bits of the encoded character.
  1910. //
  1911. // The following table illustrates the binary format of UTF-8
  1912. // characters:
  1913. //
  1914. // Bits Byte 1 Byte 2 Byte 3 Byte 4
  1915. // -----------------------------------------------------
  1916. // 7 0xxxxxxx
  1917. // 11 110xxxxx 10xxxxxx
  1918. // 16 1110xxxx 10xxxxxx 10xxxxxx
  1919. // 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  1920. while (pos < end) {
  1921. // read a byte from the byte array - if the byte's value is less
  1922. // than 128 we are dealing with a single byte character
  1923. charCode = bytes[pos++];
  1924. if (charCode > 127) {
  1925. // if the byte's value is greater than 127 we are dealing
  1926. // with a multi-byte character.
  1927. if (charCode > 239) {
  1928. // a leading-byte value greater than 239 means this is a
  1929. // 4-byte character
  1930. byteCount = 4;
  1931. // Use only the 3 least-significant bits of the leading
  1932. // byte of a 4-byte character. This is achieved by
  1933. // applying the following bit mask:
  1934. // (charCode & 0x07)
  1935. // which is equivalent to:
  1936. // 11110xxx (the byte)
  1937. // AND 00000111 (the mask)
  1938. charCode = (charCode & 7);
  1939. } else if (charCode > 223) {
  1940. // a leading-byte value greater than 223 but less than
  1941. // 240 means this is a 3-byte character
  1942. byteCount = 3;
  1943. // Use only the 4 least-significant bits of the leading
  1944. // byte of a 3-byte character. This is achieved by
  1945. // applying the following bit mask:
  1946. // (charCode & 0x0F)
  1947. // which is equivalent to:
  1948. // 1110xxxx (the byte)
  1949. // AND 00001111 (the mask)
  1950. charCode = (charCode & 15);
  1951. } else {
  1952. // a leading-byte value less than 224 but (implicitly)
  1953. // greater than 191 means this is a 2-byte character
  1954. byteCount = 2;
  1955. // Use only the 5 least-significant bits of the first
  1956. // byte of a 2-byte character. This is achieved by
  1957. // applying the following bit mask:
  1958. // (charCode & 0x1F)
  1959. // which is equivalent to:
  1960. // 110xxxxx (the byte)
  1961. // AND 00011111 (the mask)
  1962. charCode = (charCode & 31);
  1963. }
  1964. while (--byteCount) {
  1965. // get one continuation byte. then strip off the leading
  1966. // "10" by applying the following bit mask:
  1967. // (b & 0x3F)
  1968. // which is equialent to:
  1969. // 10xxxxxx (the byte)
  1970. // AND 00111111 (the mask)
  1971. // That leaves 6 remaining bits on the continuation byte
  1972. // which are concatenated onto the character's bits
  1973. charCode = ((charCode << 6) | (bytes[pos++] & 63));
  1974. }
  1975. }
  1976. chars.push(charCode);
  1977. if (++charCount === maxCharCount) {
  1978. charArrays.push(chars = []);
  1979. charCount = 0;
  1980. charArrayCount++;
  1981. }
  1982. }
  1983. // At this point we end up with an array of char arrays, each char
  1984. // array being no longer than 65,535 characters, the fastest way to
  1985. // turn these char arrays into strings is to pass them as the
  1986. // arguments to fromCharCode (fortunately all currently supported
  1987. // browsers can handle at least 65,535 function arguments).
  1988. for (; i < charArrayCount; i++) {
  1989. // create a result array containing the strings converted from
  1990. // the individual character arrays.
  1991. result.push(String.fromCharCode.apply(String, charArrays[i]));
  1992. }
  1993. return result.join('');
  1994. },
  1995. /**
  1996. * Reads an AMF "value-type" from the byte array. Automatically detects
  1997. * the data type by reading the "type marker" from the first byte after
  1998. * the pointer.
  1999. * @private
  2000. */
  2001. readValue: function() {
  2002. var me = this,
  2003. marker = bytes[pos++];
  2004. // With the introduction of AMF3, a special type marker was added to
  2005. // AMF0 to signal a switch to AMF3 serialization. This allows a packet
  2006. // to start out in AMF 0 and switch to AMF 3 on the first complex type
  2007. // to take advantage of the more the efficient encoding of AMF 3.
  2008. if (marker === 17) {
  2009. // change the version to AMF3 when we see a 17 marker
  2010. me.version = 3;
  2011. marker = bytes[pos++];
  2012. }
  2013. return me[me.typeMap[me.version][marker]]();
  2014. },
  2015. /**
  2016. * Converters used in converting specific typed Flex classes to JavaScript usable form.
  2017. * @private
  2018. */
  2019. converters: {
  2020. 'flex.messaging.io.ArrayCollection': function(obj) {
  2021. return obj.source || [];
  2022. }
  2023. }
  2024. };
  2025. });
  2026. // array collections have a source var that contains the actual data
  2027. // @tag enterprise
  2028. /**
  2029. * The AMF Reader is used by an {@link Ext.data.amf.Proxy AMF Proxy} to read
  2030. * records from a server response that contains binary data in either AMF0 or
  2031. * AMF3 format. AMF Reader constructs an {@link Ext.data.amf.Packet AMF Packet}
  2032. * and uses it to decode the binary data into javascript objects, then simply
  2033. * allows its superclass ({@link Ext.data.reader.Json}) to handle converting the
  2034. * raw javascript objects into {@link Ext.data.Model} instances.
  2035. *
  2036. * For a more detailed tutorial see the
  2037. * [AMF Guide](../guides/backend_connectors/amf.html).
  2038. */
  2039. Ext.define('Ext.data.amf.Reader', {
  2040. extend: 'Ext.data.reader.Json',
  2041. alias: 'reader.amf',
  2042. requires: [
  2043. 'Ext.data.amf.Packet'
  2044. ],
  2045. /**
  2046. * @cfg {Number} messageIndex
  2047. * AMF Packets can contain multiple messages. This config specifies the
  2048. * 0-based index of the message that contains the record data.
  2049. */
  2050. messageIndex: 0,
  2051. responseType: 'arraybuffer',
  2052. /**
  2053. * Reads records from a XMLHttpRequest response object containing a binary
  2054. * AMF Packet and returns a ResultSet.
  2055. * @param {Object} response The XMLHttpRequest response object
  2056. * @return {Ext.data.ResultSet}
  2057. */
  2058. read: function(response) {
  2059. var me = this,
  2060. bytes = response.responseBytes,
  2061. packet, messages, resultSet;
  2062. if (!bytes) {
  2063. throw "AMF Reader cannot process the response because it does not contain binary data. Make sure the Proxy's 'binary' config is true.";
  2064. }
  2065. packet = new Ext.data.amf.Packet();
  2066. packet.decode(bytes);
  2067. messages = packet.messages;
  2068. if (messages.length) {
  2069. resultSet = me.readRecords(messages[me.messageIndex].body);
  2070. } else {
  2071. // no messages, return null result set
  2072. resultSet = me.nullResultSet;
  2073. if (packet.invalid) {
  2074. // packet contains invalid data
  2075. resultSet.success = false;
  2076. }
  2077. }
  2078. return resultSet;
  2079. }
  2080. });
  2081. // @tag enterprise
  2082. /**
  2083. * The AMF Proxy is an {@link Ext.data.proxy.Ajax Ajax Proxy} that requests
  2084. * binary data from a remote server and parses it into records using an
  2085. * {@link Ext.data.amf.Reader AMF Reader} for use in a
  2086. * {@link Ext.data.Store Store}.
  2087. *
  2088. * Ext.create('Ext.data.Store', {
  2089. * model: 'Foo',
  2090. * proxy: {
  2091. * type: 'amf',
  2092. * url: 'some/url'
  2093. * }
  2094. * });
  2095. *
  2096. * For a detailed tutorial on using AMF data see the
  2097. * [AMF Guide](../guides/backend_connectors/amf.html).
  2098. */
  2099. Ext.define('Ext.data.amf.Proxy', {
  2100. extend: 'Ext.data.proxy.Ajax',
  2101. alias: 'proxy.amf',
  2102. requires: [
  2103. 'Ext.data.amf.Reader'
  2104. ],
  2105. /**
  2106. * @cfg binary
  2107. * @inheritdoc
  2108. */
  2109. binary: true,
  2110. /**
  2111. * @cfg reader
  2112. * @inheritdoc
  2113. */
  2114. reader: 'amf'
  2115. });
  2116. // @tag enterprise
  2117. /**
  2118. * @class Ext.data.amf.RemotingMessage
  2119. * Represents a remote call to be sent to the server.
  2120. */
  2121. Ext.define('Ext.data.amf.RemotingMessage', {
  2122. alias: 'data.amf.remotingmessage',
  2123. config: {
  2124. $flexType: 'flex.messaging.messages.RemotingMessage',
  2125. /**
  2126. * @property {Array} body - typically an array of parameters to pass to a method call
  2127. */
  2128. body: [],
  2129. /**
  2130. * @property {String} clientID - identifies the calling client.
  2131. */
  2132. clientId: "",
  2133. /**
  2134. * @property {String} destination - the service destination on the server
  2135. */
  2136. destination: "",
  2137. /**
  2138. * @property {Object} headers - the headers to attach to the message.
  2139. * Would typically contain the DSEndpoint and DSId fields.
  2140. */
  2141. headers: [],
  2142. /**
  2143. * @property {String} messageId - message identifier
  2144. */
  2145. messageId: "",
  2146. /**
  2147. * @property {String} operation - the method name to call
  2148. */
  2149. operation: "",
  2150. /**
  2151. * @property {Array} source - should be empty for security purposes
  2152. */
  2153. source: "",
  2154. /**
  2155. * @property {Number} timestamp - when the message was created
  2156. */
  2157. timestamp: [],
  2158. /**
  2159. * @property {Number} timeToLive - how long the message is still valid for passing
  2160. */
  2161. timeToLive: []
  2162. },
  2163. /**
  2164. * Creates new message.
  2165. * @param {Object} config Configuration options
  2166. */
  2167. constructor: function(config) {
  2168. this.initConfig(config);
  2169. },
  2170. /**
  2171. * Returns an AMFX encoded version of the message.
  2172. */
  2173. encodeMessage: function() {
  2174. var encoder = Ext.create('Ext.data.amf.XmlEncoder'),
  2175. cleanObj;
  2176. cleanObj = Ext.copyTo({}, this, "$flexType,body,clientId,destination,headers,messageId,operation,source,timestamp,timeToLive", true);
  2177. encoder.writeObject(cleanObj);
  2178. return encoder.body;
  2179. }
  2180. });
  2181. // @tag enterprise
  2182. /**
  2183. * @class Ext.data.amf.XmlDecoder
  2184. * This class parses an XML-based AMFX message and returns the deserialized
  2185. * objects. You should not need to use this class directly. It's mostly used by
  2186. * the AMFX Direct implementation.
  2187. * To decode a message, first construct a Decoder:
  2188. *
  2189. * decoder = Ext.create('Ext.data.amf.XmlDecoder');
  2190. *
  2191. * Then ask it to read in the message :
  2192. *
  2193. * resp = decoder.readAmfxMessage(str);
  2194. *
  2195. * For more information on working with AMF data please refer to the
  2196. * [AMF Guide](../guides/backend_connectors/amf.html).
  2197. */
  2198. Ext.define('Ext.data.amf.XmlDecoder', {
  2199. alias: 'data.amf.xmldecoder',
  2200. statics: {
  2201. /**
  2202. * Parses an xml string and returns an xml document
  2203. * @private
  2204. * @param {String} xml
  2205. */
  2206. readXml: function(xml) {
  2207. var doc;
  2208. if (window.DOMParser) {
  2209. doc = (new DOMParser()).parseFromString(xml, "text/xml");
  2210. } else {
  2211. doc = new ActiveXObject("Microsoft.XMLDOM");
  2212. doc.loadXML(xml);
  2213. }
  2214. return doc;
  2215. },
  2216. /**
  2217. * parses a node containing a byte array in hexadecimal format, returning the reconstructed array.
  2218. * @param {HTMLElement/XMLElement} node the node
  2219. * @return {Array} a byte array
  2220. */
  2221. readByteArray: function(node) {
  2222. var bytes = [],
  2223. c, i, str;
  2224. str = node.firstChild.nodeValue;
  2225. for (i = 0; i < str.length; i = i + 2) {
  2226. c = str.substr(i, 2);
  2227. bytes.push(parseInt(c, 16));
  2228. }
  2229. return bytes;
  2230. },
  2231. /**
  2232. * Deserializes an AMF3 binary object from a byte array
  2233. * @param {Array} bytes the byte array containing one AMF3-encoded value
  2234. * @return {Object} the decoded value
  2235. */
  2236. readAMF3Value: function(bytes) {
  2237. var packet;
  2238. packet = Ext.create('Ext.data.amf.Packet');
  2239. return packet.decodeValue(bytes);
  2240. },
  2241. /**
  2242. * Accepts Flex-style UID and decodes the number in the first four bytes (8 hex digits) of data.
  2243. * @param {String} messageId the message ID
  2244. * @return {Number} the transaction ID
  2245. */
  2246. decodeTidFromFlexUID: function(messageId) {
  2247. var str;
  2248. str = messageId.substr(0, 8);
  2249. return parseInt(str, 16);
  2250. }
  2251. },
  2252. /**
  2253. * Creates new encoder.
  2254. * @param {Object} config Configuration options
  2255. */
  2256. constructor: function(config) {
  2257. this.initConfig(config);
  2258. this.clear();
  2259. },
  2260. /**
  2261. * Clears the accumulated data and reference tables
  2262. */
  2263. clear: function() {
  2264. // reset reference counters
  2265. this.objectReferences = [];
  2266. this.traitsReferences = [];
  2267. this.stringReferences = [];
  2268. },
  2269. /**
  2270. * Reads and returns a decoded AMFX packet.
  2271. * @param {String} xml the xml of the message
  2272. * @return {Object} the response object containing the message
  2273. */
  2274. readAmfxMessage: function(xml) {
  2275. var doc, amfx, body, i,
  2276. resp = {};
  2277. this.clear();
  2278. // reset counters
  2279. doc = Ext.data.amf.XmlDecoder.readXml(xml);
  2280. amfx = doc.getElementsByTagName('amfx')[0];
  2281. //<debug>
  2282. if (!amfx) {
  2283. Ext.warn.log("No AMFX tag in message");
  2284. }
  2285. if (amfx.getAttribute('ver') != "3") {
  2286. Ext.raise("Unsupported AMFX version: " + amfx.getAttribute('ver'));
  2287. }
  2288. //</debug>
  2289. body = amfx.getElementsByTagName('body')[0];
  2290. resp.targetURI = body.getAttribute('targetURI');
  2291. resp.responseURI = body.getAttribute('responseURI');
  2292. // most likely empty string
  2293. for (i = 0; i < body.childNodes.length; i++) {
  2294. if (body.childNodes.item(i).nodeType != 1) {
  2295. // only process element nodes, ignore white space and text nodes
  2296. continue;
  2297. }
  2298. resp.message = this.readValue(body.childNodes.item(i));
  2299. break;
  2300. }
  2301. // no need to keep iterating
  2302. return resp;
  2303. },
  2304. /**
  2305. * Parses an HTML element returning the appropriate JavaScript value from the AMFX data.
  2306. * @param {HTMLElement} node The node to parse
  2307. * @return {Object} a JavaScript object or value
  2308. */
  2309. readValue: function(node) {
  2310. var val;
  2311. if (typeof node.normalize === 'function') {
  2312. node.normalize();
  2313. }
  2314. // 2DO: handle references!
  2315. if (node.tagName == "null") {
  2316. return null;
  2317. } else if (node.tagName == "true") {
  2318. return true;
  2319. } else if (node.tagName == "false") {
  2320. return false;
  2321. } else if (node.tagName == "string") {
  2322. return this.readString(node);
  2323. } else if (node.tagName == "int") {
  2324. return parseInt(node.firstChild.nodeValue);
  2325. } else if (node.tagName == "double") {
  2326. return parseFloat(node.firstChild.nodeValue);
  2327. } else if (node.tagName == "date") {
  2328. val = new Date(parseFloat(node.firstChild.nodeValue));
  2329. // record in object reference table
  2330. this.objectReferences.push(val);
  2331. return val;
  2332. } else if (node.tagName == "dictionary") {
  2333. return this.readDictionary(node);
  2334. } else if (node.tagName == "array") {
  2335. return this.readArray(node);
  2336. } else if (node.tagName == "ref") {
  2337. return this.readObjectRef(node);
  2338. } else if (node.tagName == "object") {
  2339. return this.readObject(node);
  2340. } else if (node.tagName == "xml") {
  2341. // the CDATA content of the node is a parseable XML document. parse it.
  2342. return Ext.data.amf.XmlDecoder.readXml(node.firstChild.nodeValue);
  2343. } else if (node.tagName == "bytearray") {
  2344. // 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
  2345. return Ext.data.amf.XmlDecoder.readAMF3Value(Ext.data.amf.XmlDecoder.readByteArray(node));
  2346. }
  2347. //<debug>
  2348. Ext.raise("Unknown tag type: " + node.tagName);
  2349. //</debug>
  2350. return null;
  2351. },
  2352. /**
  2353. * Reads a string or string reference and return the value
  2354. * @param {HTMLElement/XMLElement} node the node containing a string object
  2355. * @return {String} the parsed string
  2356. */
  2357. readString: function(node) {
  2358. var val;
  2359. if (node.getAttributeNode('id')) {
  2360. return this.stringReferences[parseInt(node.getAttribute('id'))];
  2361. }
  2362. val = (node.firstChild ? node.firstChild.nodeValue : "") || "";
  2363. this.stringReferences.push(val);
  2364. return val;
  2365. },
  2366. /**
  2367. * Parses and returns an ordered list of trait names
  2368. * @param {HTMLElement/XMLElement} node the traits node from the XML doc
  2369. * @return {Array} an array of ordered trait names or null if it's an externalizable object
  2370. */
  2371. readTraits: function(node) {
  2372. var traits = [],
  2373. i, rawtraits;
  2374. if (node === null) {
  2375. return null;
  2376. }
  2377. if (node.getAttribute('externalizable') == "true") {
  2378. // no traits since it's an externalizable or a null object.
  2379. return null;
  2380. }
  2381. if (node.getAttributeNode('id')) {
  2382. // return traits reference
  2383. return this.traitsReferences[parseInt(node.getAttributeNode('id').value)];
  2384. }
  2385. /* // empty anonymous objects still seem to get their empty traits in the reference table
  2386. if (!node.hasChildNodes()) {
  2387. var className = node.parentNode.getElementsByTagName('type');
  2388. if (className.length == 0) {
  2389. return traits; // special case of an anonymous object with no traits. Does not get reference counted
  2390. }
  2391. }
  2392. */
  2393. rawtraits = node.childNodes;
  2394. for (i = 0; i < rawtraits.length; i++) {
  2395. if (rawtraits.item(i).nodeType != 1) {
  2396. // only process element nodes, ignore white space and text nodes
  2397. continue;
  2398. }
  2399. // this will be a string, but let the readValue function handle it nonetheless
  2400. traits.push(this.readValue(rawtraits.item(i)));
  2401. }
  2402. // register traits in ref table:
  2403. this.traitsReferences.push(traits);
  2404. return traits;
  2405. },
  2406. /**
  2407. * Parses and return an object / array / dictionary / date from reference
  2408. * @param {HTMLElement/XMLElement} node the ref node
  2409. * @return {Object} the previously instantiated object referred to by the ref node
  2410. */
  2411. readObjectRef: function(node) {
  2412. var id;
  2413. id = parseInt(node.getAttribute('id'));
  2414. return this.objectReferences[id];
  2415. },
  2416. /**
  2417. * Parses and returns an AMFX object.
  2418. * @param {HTMLElement/XMLElement} node the `<object>` node to parse
  2419. * @return {Object} the deserialized object
  2420. */
  2421. readObject: function(node) {
  2422. var obj,
  2423. traits = [],
  2424. traitsNode, i, j, n, key, val,
  2425. klass = null,
  2426. className;
  2427. className = node.getAttribute('type');
  2428. if (className) {
  2429. klass = Ext.ClassManager.getByAlias('amfx.' + className);
  2430. }
  2431. // check if special case for class
  2432. obj = klass ? new klass() : (className ? {
  2433. $className: className
  2434. } : {});
  2435. // if there is no klass, mark the classname for easier parsing of returned results
  2436. // check if we need special handling for this class
  2437. if ((!klass) && this.converters[className]) {
  2438. obj = this.converters[className](this, node);
  2439. return obj;
  2440. }
  2441. // we're done
  2442. traitsNode = node.getElementsByTagName('traits')[0];
  2443. traits = this.readTraits(traitsNode);
  2444. //<debug>
  2445. if (traits === null) {
  2446. Ext.raise("No support for externalizable object: " + className);
  2447. }
  2448. //</debug>
  2449. // Register object if ref table, in case there's a cyclical reference coming
  2450. this.objectReferences.push(obj);
  2451. // 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
  2452. j = 0;
  2453. for (i = 0; i < node.childNodes.length; i++) {
  2454. n = node.childNodes.item(i);
  2455. if (n.nodeType != 1) {
  2456. // Ignore text nodes and non-element nodes
  2457. continue;
  2458. }
  2459. if (n.tagName == "traits") {
  2460. // ignore the traits node. We've already covered it.
  2461. continue;
  2462. }
  2463. key = traits[j];
  2464. val = this.readValue(n);
  2465. j = j + 1;
  2466. obj[key] = val;
  2467. //<debug>
  2468. if (j > traits.length) {
  2469. Ext.raise("Too many items for object, not enough traits: " + className);
  2470. }
  2471. }
  2472. //</debug>
  2473. return obj;
  2474. },
  2475. /**
  2476. * Parses and returns an AMFX array.
  2477. * @param {HTMLElement/XMLElement} node the array node
  2478. * @return {Array} the deserialized array
  2479. */
  2480. readArray: function(node) {
  2481. var arr = [],
  2482. n, i, j, l, name, val, len, childnodes, cn;
  2483. // register array in object references table before we parse, in case of circular references
  2484. this.objectReferences.push(arr);
  2485. len = parseInt(node.getAttributeNode('length').value);
  2486. i = 0;
  2487. // the length only accounts for the ordinal values. For the rest, we'll read them as ECMA key-value pairs
  2488. for (l = 0; l < node.childNodes.length; l++) {
  2489. n = node.childNodes.item(l);
  2490. if (n.nodeType != 1) {
  2491. // Ignore text nodes and non-element nodes
  2492. continue;
  2493. }
  2494. if (n.tagName == "item") {
  2495. // parse item node
  2496. name = n.getAttributeNode('name').value;
  2497. childnodes = n.childNodes;
  2498. for (j = 0; j < childnodes.length; j++) {
  2499. cn = childnodes.item(j);
  2500. if (cn.nodeType != 1) {
  2501. // Ignore text nodes and non-element nodes
  2502. continue;
  2503. }
  2504. val = this.readValue(cn);
  2505. break;
  2506. }
  2507. // out of loop. We've found our value
  2508. arr[name] = val;
  2509. } else {
  2510. // ordinal node
  2511. arr[i] = this.readValue(n);
  2512. i++;
  2513. //<debug>
  2514. if (i > len) {
  2515. Ext.raise("Array has more items than declared length: " + i + " > " + len);
  2516. }
  2517. }
  2518. }
  2519. //</debug>
  2520. //<debug>
  2521. if (i < len) {
  2522. Ext.raise("Array has less items than declared length: " + i + " < " + len);
  2523. }
  2524. //</debug>
  2525. return arr;
  2526. },
  2527. /**
  2528. * Parses and returns an AMFX dictionary.
  2529. * @param {HTMLElement/XMLElement} node the `<dictionary>` node
  2530. * @return {Object} a javascript object with the dictionary value-pair elements
  2531. */
  2532. readDictionary: function(node) {
  2533. // For now, handle regular objects
  2534. var dict = {},
  2535. key, val, i, j, n, len;
  2536. len = parseInt(node.getAttribute('length'));
  2537. // Register dictionary in the ref table, in case there's a cyclical reference coming
  2538. this.objectReferences.push(dict);
  2539. // now find pairs of keys and values
  2540. key = null;
  2541. val = null;
  2542. j = 0;
  2543. for (i = 0; i < node.childNodes.length; i++) {
  2544. n = node.childNodes.item(i);
  2545. if (n.nodeType != 1) {
  2546. // Ignore text nodes and non-element nodes
  2547. continue;
  2548. }
  2549. if (!key) {
  2550. key = this.readValue(n);
  2551. continue;
  2552. }
  2553. // next element is the value
  2554. val = this.readValue(n);
  2555. j = j + 1;
  2556. dict[key] = val;
  2557. key = null;
  2558. val = null;
  2559. }
  2560. //<debug>
  2561. if (j != len) {
  2562. Ext.raise("Incorrect number of dictionary values: " + j + " != " + len);
  2563. }
  2564. //</debug>
  2565. return dict;
  2566. },
  2567. /**
  2568. * Converts externalizable flex objects with a source array to a regular array.
  2569. * @private
  2570. */
  2571. convertObjectWithSourceField: function(node) {
  2572. var i, n, val;
  2573. for (i = 0; i < node.childNodes.length; i++) {
  2574. n = node.childNodes.item(i);
  2575. if (n.tagName == "bytearray") {
  2576. val = this.readValue(n);
  2577. this.objectReferences.push(val);
  2578. return val;
  2579. }
  2580. }
  2581. return null;
  2582. },
  2583. // we shouldn't reach here, but just in case
  2584. /**
  2585. * Converters used in converting specific typed Flex classes to JavaScript usable form.
  2586. * @private
  2587. */
  2588. converters: {
  2589. 'flex.messaging.io.ArrayCollection': function(decoder, node) {
  2590. return decoder.convertObjectWithSourceField(node);
  2591. },
  2592. 'mx.collections.ArrayList': function(decoder, node) {
  2593. return decoder.convertObjectWithSourceField(node);
  2594. },
  2595. 'mx.collections.ArrayCollection': function(decoder, node) {
  2596. return decoder.convertObjectWithSourceField(node);
  2597. }
  2598. }
  2599. });
  2600. // @tag enterprise
  2601. /**
  2602. * @class Ext.data.amf.XmlEncoder
  2603. * This class serializes data in the Action Message Format XML (AMFX) format.
  2604. * It can write simple and complex objects, to be used in conjunction with an
  2605. * AMFX-compliant server.
  2606. * To create an encoded XMl, first construct an Encoder:
  2607. *
  2608. * var encoder = Ext.create('Ext.data.amf.XmlEncoder');
  2609. *
  2610. * Then use the writer methods to out data to the :
  2611. *
  2612. * encoder.writeObject(1);
  2613. * encoder.writeObject({a: "b"});
  2614. *
  2615. * And access the data through the #bytes property:
  2616. * encoder.body;
  2617. *
  2618. * You can also reset the class to start a new body:
  2619. *
  2620. * encoder.clear();
  2621. *
  2622. * Current limitations:
  2623. * AMF3 format (format:3)
  2624. * - Each object is written out explicitly, not using the reference tables
  2625. * supported by the AMFX format. This means the function does NOT support
  2626. * circular reference objects.
  2627. * - Objects that aren't Arrays, Dates, Strings, Document (XML) or primitive
  2628. * values will be written out as anonymous objects with dynamic data.
  2629. * - If the object has a $flexType field, that field will be used in signifying
  2630. * the object-type as an attribute, instead of being passed as data.
  2631. * - There's no JavaScript equivalent to the ByteArray type in ActionScript,
  2632. * hence data will never be searialized as ByteArrays by the writeObject
  2633. * function. A writeByteArray method is provided for writing out ByteArray objects.
  2634. *
  2635. * For more information on working with AMF data please refer to the
  2636. * [AMF Guide](../guides/backend_connectors/amf.html).
  2637. */
  2638. Ext.define('Ext.data.amf.XmlEncoder', {
  2639. alias: 'data.amf.xmlencoder',
  2640. /**
  2641. * @property {String} body - The output string
  2642. */
  2643. body: "",
  2644. statics: {
  2645. /**
  2646. * Utility function to generate a flex-friendly UID
  2647. * @param {Number} id used in the first 8 chars of the id. If not provided, a random number will be used.
  2648. * @return {String} a string-encoded opaque UID
  2649. */
  2650. generateFlexUID: function(id) {
  2651. var uid = "",
  2652. i, j, t;
  2653. if (id === undefined) {
  2654. id = Ext.Number.randomInt(0, 4.294967295E9);
  2655. }
  2656. // The format of a UID is XXXXXXXX-XXXX-XXXX-XXXX-YYYYYYYYXXXX
  2657. // where each X is a random hex digit and each Y is a hex digit from the least significant part of a time stamp.
  2658. t = (id + 4.294967296E9).toString(16).toUpperCase();
  2659. // padded
  2660. uid = t.substr(t.length - 8, 8);
  2661. // last 8 chars
  2662. for (j = 0; j < 3; j++) {
  2663. // 3 -XXXX segments
  2664. uid += "-";
  2665. for (i = 0; i < 4; i++) {
  2666. uid += Ext.Number.randomInt(0, 15).toString(16).toUpperCase();
  2667. }
  2668. }
  2669. uid += "-";
  2670. // add timestamp
  2671. t = new Number(new Date()).valueOf().toString(16).toUpperCase();
  2672. // get the String representation of milliseconds in hex format
  2673. j = 0;
  2674. if (t.length < 8) {
  2675. // pad with "0" if needed
  2676. for (i = 0; i < t.length - 8; i++) {
  2677. j++;
  2678. uid += "0";
  2679. }
  2680. }
  2681. // actual timestamp:
  2682. uid += t.substr(-(8 - j));
  2683. // last few chars
  2684. // and last 4 random digits
  2685. for (i = 0; i < 4; i++) {
  2686. uid += Ext.Number.randomInt(0, 15).toString(16).toUpperCase();
  2687. }
  2688. return uid;
  2689. }
  2690. },
  2691. /**
  2692. * Creates new encoder.
  2693. * @param {Object} config Configuration options
  2694. */
  2695. constructor: function(config) {
  2696. this.initConfig(config);
  2697. this.clear();
  2698. },
  2699. /**
  2700. * Clears the accumulated data, starting with an empty string
  2701. */
  2702. clear: function() {
  2703. this.body = "";
  2704. },
  2705. /**
  2706. * Returns the encoding for undefined (which is the same as the encoding for null)
  2707. */
  2708. encodeUndefined: function() {
  2709. return this.encodeNull();
  2710. },
  2711. /**
  2712. * Writes the undefined value to the string
  2713. */
  2714. writeUndefined: function() {
  2715. this.write(this.encodeUndefined());
  2716. },
  2717. /**
  2718. * Returns the encoding for null
  2719. */
  2720. encodeNull: function() {
  2721. return "<null />";
  2722. },
  2723. /**
  2724. * Writes the null value to the string
  2725. */
  2726. writeNull: function() {
  2727. this.write(this.encodeNull());
  2728. },
  2729. /**
  2730. * Returns an encoded boolean
  2731. * @param {Boolean} val a boolean value
  2732. */
  2733. encodeBoolean: function(val) {
  2734. var str;
  2735. if (val) {
  2736. str = "<true />";
  2737. } else {
  2738. str = "<false />";
  2739. }
  2740. return str;
  2741. },
  2742. /**
  2743. * Writes a boolean value to the string
  2744. * @param {Boolean} val a boolean value
  2745. */
  2746. writeBoolean: function(val) {
  2747. this.write(this.encodeBoolean(val));
  2748. },
  2749. /**
  2750. * Returns an encoded string
  2751. * @param {String} str the string to encode
  2752. */
  2753. encodeString: function(str) {
  2754. var ret;
  2755. if (str === "") {
  2756. ret = "<string />";
  2757. } else {
  2758. ret = "<string>" + str + "</string>";
  2759. }
  2760. return ret;
  2761. },
  2762. /**
  2763. * Writes a string tag with the string content.
  2764. * @param {String} str the string to encode
  2765. */
  2766. writeString: function(str) {
  2767. this.write(this.encodeString(str));
  2768. },
  2769. /**
  2770. * Returns an encoded int
  2771. * @param {Number} num the integer to encode
  2772. */
  2773. encodeInt: function(num) {
  2774. return "<int>" + num.toString() + "</int>";
  2775. },
  2776. /**
  2777. * Writes a int tag with the content.
  2778. * @param {Number} num the integer to encode
  2779. */
  2780. writeInt: function(num) {
  2781. this.write(this.encodeInt(num));
  2782. },
  2783. /**
  2784. * Returns an encoded double
  2785. * @param {Number} num the double to encode
  2786. */
  2787. encodeDouble: function(num) {
  2788. return "<double>" + num.toString() + "</double>";
  2789. },
  2790. /**
  2791. * Writes a double tag with the content.
  2792. * @param {Number} num the double to encode
  2793. */
  2794. writeDouble: function(num) {
  2795. this.write(this.encodeDouble(num));
  2796. },
  2797. /**
  2798. * Returns an encoded number. Decides wheter to use int or double encoding.
  2799. * @param {Number} num the number to encode
  2800. */
  2801. encodeNumber: function(num) {
  2802. var maxInt = 536870911,
  2803. minSignedInt = -268435455;
  2804. //<debug>
  2805. if (typeof (num) !== "number" && !(num instanceof Number)) {
  2806. Ext.log.warn("Encoder: writeNumber argument is not numeric. Can't coerce.");
  2807. }
  2808. // </debug>
  2809. // switch to the primitive value for handling:
  2810. if (num instanceof Number) {
  2811. num = num.valueOf();
  2812. }
  2813. // Determine if this is an integer or a float.
  2814. if (num % 1 === 0 && num >= minSignedInt && num <= maxInt) {
  2815. // The number has no decimal point and is within bounds. Let's encode it.
  2816. return this.encodeInt(num);
  2817. } else {
  2818. return this.encodeDouble(num);
  2819. }
  2820. },
  2821. /**
  2822. * Writes a number, deciding if to use int or double as the tag
  2823. * @param {Number} num the number to encode
  2824. */
  2825. writeNumber: function(num) {
  2826. this.write(this.encodeNumber(num));
  2827. },
  2828. /**
  2829. * Encode a date
  2830. * @param {Date} date the date to encode
  2831. */
  2832. encodeDate: function(date) {
  2833. return "<date>" + (new Number(date)).toString() + "</date>";
  2834. },
  2835. /**
  2836. * Write a date to the string
  2837. * @param {Date} date the date to encode
  2838. */
  2839. writeDate: function(date) {
  2840. this.write(this.encodeDate(date));
  2841. },
  2842. /**
  2843. * @private
  2844. * Encodes one ECMA array element
  2845. * @param {String} key the name of the element
  2846. * @param {Object} value the value of the element
  2847. * @return {String} the encoded key-value pair
  2848. */
  2849. encodeEcmaElement: function(key, value) {
  2850. var str = '<item name="' + key.toString() + '">' + this.encodeObject(value) + '</item>';
  2851. return str;
  2852. },
  2853. /**
  2854. * Encodes an array, marking it as an ECMA array if it has associative (non-ordinal) indices
  2855. * @param {Array} array the array to encode
  2856. */
  2857. encodeArray: function(array) {
  2858. var ordinals = [],
  2859. firstNonOrdinal,
  2860. ecmaElements = [],
  2861. length = array.length,
  2862. // length is of ordinal section only
  2863. i, str;
  2864. for (i in array) {
  2865. if (Ext.isNumeric(i) && (i % 1 == 0)) {
  2866. //this is an integer. Add to ordinals array
  2867. ordinals[i] = this.encodeObject(array[i]);
  2868. } else {
  2869. ecmaElements.push(this.encodeEcmaElement(i, array[i]));
  2870. }
  2871. }
  2872. firstNonOrdinal = ordinals.length;
  2873. // now, check if we have consecutive numbers in the ordinals array
  2874. for (i = 0; i < ordinals.length; i++) {
  2875. if (ordinals[i] === undefined) {
  2876. // we have a gap in the array. Mark it - the rest of the items become ECMA elements
  2877. firstNonOrdinal = i;
  2878. break;
  2879. }
  2880. }
  2881. if (firstNonOrdinal < ordinals.length) {
  2882. // transfer some of the elements to the ecma array
  2883. for (i = firstNonOrdinals; i < ordinals.length; i++) {
  2884. if (ordinals[i] !== undefined) {
  2885. ecmaElements.push(this.encodeEcmaElement(i, ordinals[i]));
  2886. }
  2887. }
  2888. ordinals = ordinals.slice(0, firstNonOrdinal);
  2889. }
  2890. // finally start constructing the string
  2891. str = '<array length="' + ordinals.length + '"';
  2892. if (ecmaElements.length > 0) {
  2893. str += ' ecma="true"';
  2894. }
  2895. str += '>';
  2896. // first add the oridnals in consecutive order:
  2897. for (i = 0; i < ordinals.length; i++) {
  2898. // iterate by counting since we need to guarantee the order
  2899. str += ordinals[i];
  2900. }
  2901. // Now add ECMA items
  2902. for (i in ecmaElements) {
  2903. str += ecmaElements[i];
  2904. }
  2905. // And close the array:
  2906. str += '</array>';
  2907. return str;
  2908. },
  2909. /**
  2910. * Writes an array to the string, marking it as an ECMA array if it has associative (non-ordinal) indices
  2911. * @param {Array} array the array to encode
  2912. */
  2913. writeArray: function(array) {
  2914. this.write(this.encodeArray(array));
  2915. },
  2916. /**
  2917. * Encodes an xml document into a CDATA section
  2918. * @param {XMLElement/HTMLElement} xml an XML document or element (Document type in some browsers)
  2919. */
  2920. encodeXml: function(xml) {
  2921. var str = this.convertXmlToString(xml);
  2922. return "<xml><![CDATA[" + str + "]]></xml>";
  2923. },
  2924. /**
  2925. * Write an XML document to the string
  2926. * @param {XMLElement/HTMLElement} xml an XML document or element (Document type in some browsers)
  2927. */
  2928. writeXml: function(xml) {
  2929. this.write(this.encodeXml(xml));
  2930. },
  2931. /**
  2932. * Encodes a generic object into AMFX format. If a <tt>$flexType</tt> member is defined, list that as the object type.
  2933. * @param {Object} obj the object to encode
  2934. * @return {String} the encoded text
  2935. */
  2936. encodeGenericObject: function(obj) {
  2937. var traits = [],
  2938. values = [],
  2939. flexType = null,
  2940. i, str;
  2941. for (i in obj) {
  2942. if (i == "$flexType") {
  2943. flexType = obj[i];
  2944. } else {
  2945. traits.push(this.encodeString(new String(i)));
  2946. values.push(this.encodeObject(obj[i]));
  2947. }
  2948. }
  2949. if (flexType) {
  2950. str = '<object type="' + flexType + '">';
  2951. } else {
  2952. str = "<object>";
  2953. }
  2954. if (traits.length > 0) {
  2955. str += "<traits>";
  2956. str += traits.join("");
  2957. str += "</traits>";
  2958. } else {
  2959. str += "<traits />";
  2960. }
  2961. str += values.join("");
  2962. str += "</object>";
  2963. return str;
  2964. },
  2965. /**
  2966. * Writes a generic object to the string. If a <tt>$flexType</tt> member is defined, list that as the object type.
  2967. * @param {Object} obj the object to encode
  2968. */
  2969. writeGenericObject: function(obj) {
  2970. this.write(this.encodeGenericObject(obj));
  2971. },
  2972. /**
  2973. * Encodes a byte arrat in AMFX format
  2974. * @param {Array} array the byte array to encode
  2975. */
  2976. encodeByteArray: function(array) {
  2977. var str, i, h;
  2978. if (array.length > 0) {
  2979. str = "<bytearray>";
  2980. for (i = 0; i < array.length; i++) {
  2981. //<debug>
  2982. if (!Ext.isNumber(array[i])) {
  2983. Ext.raise("Byte array contains a non-number: " + array[i] + " in index: " + i);
  2984. }
  2985. if (array[i] < 0 || array[i] > 255) {
  2986. Ext.raise("Byte array value out of bounds: " + array[i]);
  2987. }
  2988. //</debug>
  2989. h = array[i].toString(16).toUpperCase();
  2990. if (array[i] < 16) {
  2991. h = "0" + h;
  2992. }
  2993. str += h;
  2994. }
  2995. str += "</bytearray>";
  2996. } else {
  2997. str = "<bytearray />";
  2998. }
  2999. return str;
  3000. },
  3001. /**
  3002. * Writes an AMFX byte array to the string. This is for convenience only and is not called automatically by writeObject.
  3003. * @param {Array} array the byte array to encode
  3004. */
  3005. writeByteArray: function(array) {
  3006. this.write(this.encodeByteArray(array));
  3007. },
  3008. /**
  3009. * encode the appropriate data item. Supported types:
  3010. * - undefined
  3011. * - null
  3012. * - boolean
  3013. * - integer
  3014. * - double
  3015. * - UTF-8 string
  3016. * - XML Document (identified by being instaneof Document. Can be generated with: new DOMParser()).parseFromString(xml, "text/xml");
  3017. * - Date
  3018. * - Array
  3019. * - Generic object
  3020. * @param {Object} item A primitive or object to write to the stream
  3021. * @return {String} the encoded object in AMFX format
  3022. */
  3023. encodeObject: function(item) {
  3024. var t = typeof (item);
  3025. //Ext.log("Writing " + item + " of type " + t);
  3026. if (t === "undefined") {
  3027. return this.encodeUndefined();
  3028. } else if (item === null) {
  3029. // can't check type since typeof(null) returns "object"
  3030. return this.encodeNull();
  3031. } else if (Ext.isBoolean(item)) {
  3032. return this.encodeBoolean(item);
  3033. } else if (Ext.isString(item)) {
  3034. return this.encodeString(item);
  3035. } else if (t === "number" || item instanceof Number) {
  3036. // Can't use Ext.isNumeric since it accepts strings as well
  3037. return this.encodeNumber(item);
  3038. } else if (t === "object") {
  3039. // Figure out which object this is
  3040. if (item instanceof Date) {
  3041. return this.encodeDate(item);
  3042. } else if (Ext.isArray(item)) {
  3043. return this.encodeArray(item);
  3044. } else if (this.isXmlDocument(item)) {
  3045. return this.encodeXml(item);
  3046. } else {
  3047. // Treat this as a generic object with name/value pairs of data.
  3048. return this.encodeGenericObject(item);
  3049. }
  3050. } else {
  3051. //<debug>
  3052. Ext.log.warn("AMFX Encoder: Unknown item type " + t + " can't be written to stream: " + item);
  3053. }
  3054. //</debug>
  3055. return null;
  3056. },
  3057. // if we reached here, return null
  3058. /**
  3059. * Writes the appropriate data item to the string. Supported types:
  3060. * - undefined
  3061. * - null
  3062. * - boolean
  3063. * - integer
  3064. * - double
  3065. * - UTF-8 string
  3066. * - XML Document (identified by being instaneof Document. Can be generated with: new DOMParser()).parseFromString(xml, "text/xml");
  3067. * - Date
  3068. * - Array
  3069. * - Generic object
  3070. * @param {Object} item A primitive or object to write to the stream
  3071. */
  3072. writeObject: function(item) {
  3073. this.write(this.encodeObject(item));
  3074. },
  3075. /**
  3076. * Encodes an AMFX remoting message with the AMFX envelope.
  3077. * @param {Ext.data.amf.RemotingMessage} message the message to pass on to serialize.
  3078. */
  3079. encodeAmfxRemotingPacket: function(message) {
  3080. var msg, str;
  3081. str = '<amfx ver="3" xmlns="http://www.macromedia.com/2005/amfx"><body>';
  3082. str += message.encodeMessage();
  3083. str += '</body></amfx>';
  3084. return str;
  3085. },
  3086. /**
  3087. * Writes an AMFX remoting message with the AMFX envelope to the string.
  3088. * @param {Ext.data.amf.RemotingMessage} message the message to pass on to serialize.
  3089. */
  3090. writeAmfxRemotingPacket: function(message) {
  3091. this.write(this.encodeAmfxRemotingPacket(message));
  3092. },
  3093. /**
  3094. * Converts an XML Document object to a string.
  3095. * @param {Object} xml XML document to convert (typically Document object)
  3096. * @return {String} A string representing the document
  3097. * @private
  3098. */
  3099. convertXmlToString: function(xml) {
  3100. var str;
  3101. if (window.XMLSerializer) {
  3102. // this is not IE, so:
  3103. str = new window.XMLSerializer().serializeToString(xml);
  3104. } else {
  3105. //no XMLSerializer, might be an old version of IE
  3106. str = xml.xml;
  3107. }
  3108. return str;
  3109. },
  3110. /**
  3111. * Tries to determine if an object is an XML document
  3112. * @param {Object} item to identify
  3113. * @return {Boolean} true if it's an XML document, false otherwise
  3114. */
  3115. isXmlDocument: function(item) {
  3116. // We can't test if Document is defined since IE just throws an exception. Instead rely on the DOMParser object
  3117. if (window.DOMParser) {
  3118. if (Ext.isDefined(item.doctype)) {
  3119. return true;
  3120. }
  3121. }
  3122. // Otherwise, check if it has an XML field
  3123. if (Ext.isString(item.xml)) {
  3124. // and we can get the xml
  3125. return true;
  3126. }
  3127. return false;
  3128. },
  3129. /**
  3130. * Appends a string to the body of the message
  3131. * @param {String} str the string to append
  3132. * @private
  3133. */
  3134. write: function(str) {
  3135. this.body += str;
  3136. }
  3137. });
  3138. // @tag enterprise
  3139. /**
  3140. * @class Ext.direct.AmfRemotingProvider
  3141. *
  3142. * <p>The {@link Ext.direct.AmfRemotingProvider AmfRemotingProvider}
  3143. * allows making RPC calls to a Java object on a BlazeDS or ColdFusion using either the AMFX or the AMF protocols.</p>
  3144. *
  3145. * <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>
  3146. * <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>
  3147. * <pre><code>
  3148. &lt;channel-definition id="my-http" class="mx.messaging.channels.HTTPChannel"&gt;
  3149. &lt;endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/http" class="flex.messaging.endpoints.HTTPEndpoint"/&gt;
  3150. &lt;/channel-definition&gt;
  3151. </code></pre>
  3152. *
  3153. * <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.
  3154. * For example this allows services to be accessed remotely by both AMF and AMFX:</p>
  3155. * <pre><code>
  3156. &lt;default-channels&gt;
  3157. &lt;channel ref="my-amf"/&gt;
  3158. &lt;channel ref="my-http"/&gt;
  3159. &lt;/default-channels&gt;
  3160. * </code></pre>
  3161. *
  3162. * <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>
  3163. * <pre><code>
  3164. Ext.direct.Manager.addProvider({
  3165. "url":"/samples/messagebroker/http", // URL for the HTTPEndpoint
  3166. "type":"amfremoting",
  3167. "endpoint": "my-http", // the name of the HTTPEndpoint channel as defined in the server's services-config.xml
  3168. "actions":{
  3169. "product":[{ // name of the destination as defined in remoting-config.xml on the server
  3170. "name":"getProducts", // method name of the method to call
  3171. "len":0 // number of parameters
  3172. },{
  3173. "name":"add",
  3174. "len":1
  3175. },{
  3176. "name":"bad",
  3177. "len":0
  3178. }]
  3179. }
  3180. });
  3181. * </code></pre>
  3182. * <p>You can now call the service as follows:</p>
  3183. <pre><code>
  3184. product.getProducts((function(provider, response) {
  3185. // do something with the response
  3186. console.log("Got " + response.data.length + " objects");
  3187. });
  3188. </code></pre>
  3189. *
  3190. * 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.
  3191. *
  3192. *
  3193. * <p>The following example shows how to define a binary AMF-based call:</p>
  3194. * <pre><code>
  3195. Ext.direct.Manager.addProvider({
  3196. "url":"/samples/messagebroker/amf", // URL for the AMFEndpoint
  3197. "type":"amfremoting",
  3198. "endpoint": "my-amf", // the name of the AMFEndpoint channel as defined in the server's services-config.xml
  3199. "binary": true, // chooses AMF encoding
  3200. "actions":{
  3201. "product":[{ // name of the destination as defined in remoting-config.xml on the server
  3202. "name":"getProducts", // method name of the method to call
  3203. "len":0 // number of parameters
  3204. },{
  3205. "name":"add",
  3206. "len":1
  3207. },{
  3208. "name":"bad",
  3209. "len":0
  3210. }]
  3211. }
  3212. });
  3213. * </code></pre>
  3214. * <p>Calling the server is done the same way as for the AMFX-based definition.</p>
  3215. */
  3216. Ext.define('Ext.direct.AmfRemotingProvider', {
  3217. /* Begin Definitions */
  3218. alias: 'direct.amfremotingprovider',
  3219. extend: 'Ext.direct.Provider',
  3220. requires: [
  3221. 'Ext.util.MixedCollection',
  3222. 'Ext.util.DelayedTask',
  3223. 'Ext.direct.Transaction',
  3224. 'Ext.direct.RemotingMethod',
  3225. 'Ext.data.amf.XmlEncoder',
  3226. 'Ext.data.amf.XmlDecoder',
  3227. 'Ext.data.amf.Encoder',
  3228. 'Ext.data.amf.Packet',
  3229. 'Ext.data.amf.RemotingMessage',
  3230. 'Ext.direct.ExceptionEvent'
  3231. ],
  3232. /* End Definitions */
  3233. /**
  3234. * @cfg {Object} actions
  3235. * Object literal defining the server side actions and methods. For example, if
  3236. * the Provider is configured with:
  3237. * <pre><code>
  3238. "actions":{ // each property within the 'actions' object represents a server side Class
  3239. "TestAction":[ // array of methods within each server side Class to be
  3240. { // stubbed out on client
  3241. "name":"doEcho",
  3242. "len":1
  3243. },{
  3244. "name":"multiply",// name of method
  3245. "len":2 // The number of parameters that will be used to create an
  3246. // array of data to send to the server side function.
  3247. // Ensure the server sends back a Number, not a String.
  3248. },{
  3249. "name":"doForm",
  3250. "formHandler":true, // direct the client to use specialized form handling method
  3251. "len":1
  3252. }]
  3253. }
  3254. * </code></pre>
  3255. * <p>Note that a Store is not required, a server method can be called at any time.
  3256. * In the following example a <b>client side</b> handler is used to call the
  3257. * server side method "multiply" in the server-side "TestAction" Class:</p>
  3258. * <pre><code>
  3259. TestAction.multiply(
  3260. 2, 4, // pass two arguments to server, so specify len=2
  3261. // callback function after the server is called
  3262. // result: the result returned by the server
  3263. // e: Ext.direct.RemotingEvent object
  3264. function(result, e) {
  3265. var t = e.getTransaction();
  3266. var action = t.action; // server side Class called
  3267. var method = t.method; // server side method called
  3268. if(e.status) {
  3269. var answer = Ext.encode(result); // 8
  3270. } else {
  3271. var msg = e.message; // failure message
  3272. }
  3273. }
  3274. );
  3275. * </code></pre>
  3276. * In the example above, the server side "multiply" function will be passed two
  3277. * arguments (2 and 4). The "multiply" method should return the value 8 which will be
  3278. * available as the <tt>result</tt> in the example above.
  3279. */
  3280. /**
  3281. * @cfg {String/Object} namespace
  3282. * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
  3283. * Explicitly specify the namespace Object, or specify a String to have a
  3284. * {@link Ext#namespace namespace created} implicitly.
  3285. */
  3286. /**
  3287. * @cfg {String} url
  3288. * <b>Required</b>. The URL to connect to the Flex remoting server (LCDS, BlazeDS, etc).
  3289. * This should include the /messagebroker/amf suffix as defined in the services-config.xml and remoting-config.xml files.
  3290. */
  3291. /**
  3292. * @cfg {String} endpoint
  3293. * <b>Requred</b>. This is the channel id defined in services-config.xml on the server (e.g. my-amf or my-http).
  3294. */
  3295. /**
  3296. * @cfg {String} enableUrlEncode
  3297. * Specify which param will hold the arguments for the method.
  3298. * Defaults to <tt>'data'</tt>.
  3299. */
  3300. /**
  3301. * @cfg {String} binary
  3302. * 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.
  3303. */
  3304. binary: false,
  3305. /**
  3306. * @cfg {Number} maxRetries
  3307. * Number of times to re-attempt delivery on failure of a call.
  3308. */
  3309. maxRetries: 1,
  3310. /**
  3311. * @cfg {Number} timeout
  3312. * The timeout to use for each request.
  3313. */
  3314. timeout: undefined,
  3315. /**
  3316. * @event beforecall
  3317. * Fires immediately before the client-side sends off the RPC call.
  3318. * By returning false from an event handler you can prevent the call from
  3319. * executing.
  3320. * @param {Ext.direct.AmfRemotingProvider} provider
  3321. * @param {Ext.direct.Transaction} transaction
  3322. * @param {Object} meta The meta data
  3323. */
  3324. /**
  3325. * @event call
  3326. * Fires immediately after the request to the server-side is sent. This does
  3327. * NOT fire after the response has come back from the call.
  3328. * @param {Ext.direct.AmfRemotingProvider} provider
  3329. * @param {Ext.direct.Transaction} transaction
  3330. * @param {Object} meta The meta data
  3331. */
  3332. constructor: function(config) {
  3333. var me = this;
  3334. me.callParent(arguments);
  3335. me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || window;
  3336. me.transactions = new Ext.util.MixedCollection();
  3337. me.callBuffer = [];
  3338. },
  3339. /**
  3340. * Initialize the API
  3341. * @private
  3342. */
  3343. initAPI: function() {
  3344. var actions = this.actions,
  3345. namespace = this.namespace,
  3346. action, cls, methods, i, len, method;
  3347. for (action in actions) {
  3348. if (actions.hasOwnProperty(action)) {
  3349. cls = namespace[action];
  3350. if (!cls) {
  3351. cls = namespace[action] = {};
  3352. }
  3353. methods = actions[action];
  3354. for (i = 0 , len = methods.length; i < len; ++i) {
  3355. method = new Ext.direct.RemotingMethod(methods[i]);
  3356. cls[method.name] = this.createHandler(action, method);
  3357. }
  3358. }
  3359. }
  3360. },
  3361. /**
  3362. * Create a handler function for a direct call.
  3363. * @private
  3364. * @param {String} action The action the call is for
  3365. * @param {Object} method The details of the method
  3366. * @return {Function} A JS function that will kick off the call
  3367. */
  3368. createHandler: function(action, method) {
  3369. var me = this,
  3370. handler;
  3371. if (!method.formHandler) {
  3372. handler = function() {
  3373. me.configureRequest(action, method, Array.prototype.slice.call(arguments, 0));
  3374. };
  3375. } else {
  3376. handler = function(form, callback, scope) {
  3377. me.configureFormRequest(action, method, form, callback, scope);
  3378. };
  3379. }
  3380. handler.directCfg = {
  3381. action: action,
  3382. method: method
  3383. };
  3384. return handler;
  3385. },
  3386. isConnected: function() {
  3387. return !!this.connected;
  3388. },
  3389. connect: function() {
  3390. var me = this;
  3391. if (me.url) {
  3392. // Generate a unique ID for this client
  3393. me.clientId = Ext.data.amf.XmlEncoder.generateFlexUID();
  3394. me.initAPI();
  3395. me.connected = true;
  3396. me.fireEvent('connect', me);
  3397. me.DSId = null;
  3398. } else if (!me.url) {
  3399. //<debug>
  3400. Ext.raise('Error initializing RemotingProvider, no url configured.');
  3401. }
  3402. },
  3403. //</debug>
  3404. disconnect: function() {
  3405. var me = this;
  3406. if (me.connected) {
  3407. me.connected = false;
  3408. me.fireEvent('disconnect', me);
  3409. }
  3410. },
  3411. /**
  3412. * Run any callbacks related to the transaction.
  3413. * @private
  3414. * @param {Ext.direct.Transaction} transaction The transaction
  3415. * @param {Ext.direct.Event} event The event
  3416. */
  3417. runCallback: function(transaction, event) {
  3418. var success = !!event.status,
  3419. funcName = success ? 'success' : 'failure',
  3420. callback, result;
  3421. if (transaction && transaction.callback) {
  3422. callback = transaction.callback;
  3423. result = Ext.isDefined(event.result) ? event.result : event.data;
  3424. if (Ext.isFunction(callback)) {
  3425. callback(result, event, success);
  3426. } else {
  3427. Ext.callback(callback[funcName], callback.scope, [
  3428. result,
  3429. event,
  3430. success
  3431. ]);
  3432. Ext.callback(callback.callback, callback.scope, [
  3433. result,
  3434. event,
  3435. success
  3436. ]);
  3437. }
  3438. }
  3439. },
  3440. /**
  3441. * React to the ajax request being completed
  3442. * @private
  3443. */
  3444. onData: function(options, success, response) {
  3445. var me = this,
  3446. i = 0,
  3447. len, events, event, transaction, transactions;
  3448. if (success) {
  3449. events = me.createEvents(response);
  3450. for (len = events.length; i < len; ++i) {
  3451. event = events[i];
  3452. transaction = me.getTransaction(event);
  3453. me.fireEvent('data', me, event);
  3454. if (transaction) {
  3455. me.runCallback(transaction, event, true);
  3456. Ext.direct.Manager.removeTransaction(transaction);
  3457. }
  3458. }
  3459. } else {
  3460. transactions = [].concat(options.transaction);
  3461. for (len = transactions.length; i < len; ++i) {
  3462. transaction = me.getTransaction(transactions[i]);
  3463. if (transaction && transaction.retryCount < me.maxRetries) {
  3464. transaction.retry();
  3465. } else {
  3466. event = new Ext.direct.ExceptionEvent({
  3467. data: null,
  3468. transaction: transaction,
  3469. code: Ext.direct.Manager.exceptions.TRANSPORT,
  3470. message: 'Unable to connect to the server.',
  3471. xhr: response
  3472. });
  3473. me.fireEvent('data', me, event);
  3474. if (transaction) {
  3475. me.runCallback(transaction, event, false);
  3476. Ext.direct.Manager.removeTransaction(transaction);
  3477. }
  3478. }
  3479. }
  3480. }
  3481. },
  3482. /**
  3483. * Get transaction from XHR options
  3484. * @private
  3485. * @param {Object} options The options sent to the Ajax request
  3486. * @return {Ext.direct.Transaction} The transaction, null if not found
  3487. */
  3488. getTransaction: function(options) {
  3489. return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
  3490. },
  3491. /**
  3492. * Configure a direct request
  3493. * @private
  3494. * @param {String} action The action being executed
  3495. * @param {Object} method The method being executed
  3496. * @param {Object} args The argument to pass to the request
  3497. */
  3498. configureRequest: function(action, method, args) {
  3499. var me = this,
  3500. callData = method.getCallData(args),
  3501. data = callData.data,
  3502. callback = callData.callback,
  3503. scope = callData.scope,
  3504. transaction;
  3505. transaction = new Ext.direct.Transaction({
  3506. provider: me,
  3507. args: args,
  3508. action: action,
  3509. method: method.name,
  3510. data: data,
  3511. callback: scope && Ext.isFunction(callback) ? callback.bind(scope) : callback
  3512. });
  3513. if (me.fireEvent('beforecall', me, transaction, method) !== false) {
  3514. Ext.direct.Manager.addTransaction(transaction);
  3515. me.queueTransaction(transaction);
  3516. me.fireEvent('call', me, transaction, method);
  3517. }
  3518. },
  3519. /**
  3520. * Gets the Flex remoting message info for a transaction
  3521. * @private
  3522. * @param {Ext.direct.Transaction} transaction The transaction
  3523. * @return {Object} The Flex remoting message structure ready to encode in an AMFX RemoteMessage
  3524. */
  3525. getCallData: function(transaction) {
  3526. if (this.binary) {
  3527. return {
  3528. targetUri: transaction.action + "." + transaction.method,
  3529. responseUri: '/' + transaction.id,
  3530. body: transaction.data || []
  3531. };
  3532. } else {
  3533. return new Ext.data.amf.RemotingMessage({
  3534. body: transaction.data || [],
  3535. clientId: this.clientId,
  3536. destination: transaction.action,
  3537. headers: {
  3538. DSEndpoint: this.endpoint,
  3539. DSId: this.DSId || "nil"
  3540. },
  3541. // if unknown yet, use "nil"
  3542. messageId: Ext.data.amf.XmlEncoder.generateFlexUID(transaction.id),
  3543. // encode as first 4 bytes of UID
  3544. operation: transaction.method,
  3545. timestamp: 0,
  3546. timeToLive: 0
  3547. });
  3548. }
  3549. },
  3550. /*
  3551. return {
  3552. action: transaction.action,
  3553. method: transaction.method,
  3554. data: transaction.data,
  3555. type: 'rpc',
  3556. tid: transaction.id
  3557. };
  3558. */
  3559. /**
  3560. * Sends a request to the server
  3561. * @private
  3562. * @param {Object/Array} data The data to send
  3563. */
  3564. sendRequest: function(data) {
  3565. var me = this,
  3566. request = {
  3567. url: me.url,
  3568. callback: me.onData,
  3569. scope: me,
  3570. transaction: data,
  3571. timeout: me.timeout
  3572. },
  3573. callData,
  3574. i = 0,
  3575. len, params, encoder,
  3576. amfMessages = [],
  3577. amfHeaders = [];
  3578. // prepare AMFX messages
  3579. if (Ext.isArray(data)) {
  3580. //<debug>
  3581. if (!me.binary) {
  3582. Ext.raise("Mutltiple messages in the same call are not supported in AMFX");
  3583. }
  3584. //</debug>
  3585. for (len = data.length; i < len; ++i) {
  3586. amfMessages.push(me.getCallData(data[i]));
  3587. }
  3588. } else {
  3589. amfMessages.push(me.getCallData(data));
  3590. }
  3591. if (me.binary) {
  3592. encoder = new Ext.data.amf.Encoder({
  3593. format: 0
  3594. });
  3595. // AMF message sending always uses AMF0
  3596. // encode packet
  3597. encoder.writeAmfPacket(amfHeaders, amfMessages);
  3598. request.binaryData = encoder.bytes;
  3599. request.binary = true;
  3600. // Binary response
  3601. request.headers = {
  3602. 'Content-Type': 'application/x-amf'
  3603. };
  3604. } else {
  3605. encoder = new Ext.data.amf.XmlEncoder();
  3606. // encode packet
  3607. encoder.writeAmfxRemotingPacket(amfMessages[0]);
  3608. request.xmlData = encoder.body;
  3609. }
  3610. // prepare Ajax request
  3611. Ext.Ajax.request(request);
  3612. },
  3613. /**
  3614. * Add a new transaction to the queue
  3615. * @private
  3616. * @param {Ext.direct.Transaction} transaction The transaction
  3617. */
  3618. queueTransaction: function(transaction) {
  3619. var me = this,
  3620. enableBuffer = false;
  3621. // no queueing for AMFX
  3622. if (transaction.form) {
  3623. me.sendFormRequest(transaction);
  3624. return;
  3625. }
  3626. me.callBuffer.push(transaction);
  3627. if (enableBuffer) {
  3628. if (!me.callTask) {
  3629. me.callTask = new Ext.util.DelayedTask(me.combineAndSend, me);
  3630. }
  3631. me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
  3632. } else {
  3633. me.combineAndSend();
  3634. }
  3635. },
  3636. /**
  3637. * Combine any buffered requests and send them off
  3638. * @private
  3639. */
  3640. combineAndSend: function() {
  3641. var buffer = this.callBuffer,
  3642. len = buffer.length;
  3643. if (len > 0) {
  3644. this.sendRequest(len == 1 ? buffer[0] : buffer);
  3645. this.callBuffer = [];
  3646. }
  3647. },
  3648. /**
  3649. * Configure a form submission request
  3650. * @private
  3651. * @param {String} action The action being executed
  3652. * @param {Object} method The method being executed
  3653. * @param {HTMLElement} form The form being submitted
  3654. * @param {Function} callback (optional) A callback to run after the form submits
  3655. * @param {Object} scope (optional) A scope to execute the callback in
  3656. */
  3657. configureFormRequest: function(action, method, form, callback, scope) {
  3658. //<debug>
  3659. Ext.raise("Form requests are not supported for AmfRemoting");
  3660. },
  3661. //</debug>
  3662. /*
  3663. var me = this,
  3664. transaction = new Ext.direct.Transaction({
  3665. provider: me,
  3666. action: action,
  3667. method: method.name,
  3668. args: [form, callback, scope],
  3669. callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback,
  3670. isForm: true
  3671. }),
  3672. isUpload,
  3673. params;
  3674. if (me.fireEvent('beforecall', me, transaction, method) !== false) {
  3675. Ext.direct.Manager.addTransaction(transaction);
  3676. isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data';
  3677. params = {
  3678. extTID: transaction.id,
  3679. extAction: action,
  3680. extMethod: method.name,
  3681. extType: 'rpc',
  3682. extUpload: String(isUpload)
  3683. };
  3684. // change made from typeof callback check to callback.params
  3685. // to support addl param passing in DirectSubmit EAC 6/2
  3686. Ext.apply(transaction, {
  3687. form: Ext.getDom(form),
  3688. isUpload: isUpload,
  3689. params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
  3690. });
  3691. me.fireEvent('call', me, transaction, method);
  3692. me.sendFormRequest(transaction);
  3693. }
  3694. */
  3695. /**
  3696. * Sends a form request
  3697. * @private
  3698. * @param {Ext.direct.Transaction} transaction The transaction to send
  3699. */
  3700. sendFormRequest: function(transaction) {
  3701. //<debug>
  3702. Ext.raise("Form requests are not supported for AmfRemoting");
  3703. },
  3704. //</debug>
  3705. /*
  3706. Ext.Ajax.request({
  3707. url: this.url,
  3708. params: transaction.params,
  3709. callback: this.onData,
  3710. scope: this,
  3711. form: transaction.form,
  3712. isUpload: transaction.isUpload,
  3713. transaction: transaction
  3714. });
  3715. */
  3716. /**
  3717. * Creates a set of events based on the XHR response
  3718. * @private
  3719. * @param {Object} response The XHR response
  3720. * @return {Ext.direct.Event[]} An array of Ext.direct.Event
  3721. */
  3722. createEvents: function(response) {
  3723. var data = null,
  3724. rawBytes = [],
  3725. events = [],
  3726. event,
  3727. i = 0,
  3728. len, decoder;
  3729. try {
  3730. if (this.binary) {
  3731. decoder = new Ext.data.amf.Packet();
  3732. data = decoder.decode(response.responseBytes);
  3733. } else {
  3734. decoder = new Ext.data.amf.XmlDecoder();
  3735. data = decoder.readAmfxMessage(response.responseText);
  3736. }
  3737. } /*
  3738. // This won't be sent back unless we use a ping message, so ignore for now
  3739. // if we don't have the server ID yet, check for it here
  3740. if (!this.DSId) {
  3741. if (data.message.headers && data.message.headers.DSId) {
  3742. this.DSId = data.message.headers.DSId;
  3743. }
  3744. }
  3745. */
  3746. catch (e) {
  3747. event = new Ext.direct.ExceptionEvent({
  3748. data: e,
  3749. xhr: response,
  3750. code: Ext.direct.Manager.exceptions.PARSE,
  3751. message: 'Error parsing AMF response: \n\n ' + data
  3752. });
  3753. return [
  3754. event
  3755. ];
  3756. }
  3757. if (this.binary) {
  3758. for (i = 0; i < data.messages.length; i++) {
  3759. events.push(this.createEvent(data.messages[i]));
  3760. }
  3761. } else {
  3762. // AMFX messages have one response per message
  3763. events.push(this.createEvent(data));
  3764. }
  3765. return events;
  3766. },
  3767. /**
  3768. * Create an event from an AMFX response object
  3769. * @param {Object} response The AMFX response object
  3770. * @return {Ext.direct.Event} The event
  3771. */
  3772. createEvent: function(response) {
  3773. // Check targetUri to identify transaction ID and status
  3774. var status = response.targetURI.split("/"),
  3775. tid, event, data, statusIndex,
  3776. me = this;
  3777. if (me.binary) {
  3778. tid = status[1];
  3779. statusIndex = 2;
  3780. } else {
  3781. tid = Ext.data.amf.XmlDecoder.decodeTidFromFlexUID(response.message.correlationId);
  3782. statusIndex = 1;
  3783. }
  3784. // construct data structure
  3785. if (status[statusIndex] == "onStatus") {
  3786. // The call failed
  3787. data = {
  3788. tid: tid,
  3789. data: (me.binary ? response.body : response.message)
  3790. };
  3791. event = Ext.create('direct.exception', data);
  3792. } else if (status[statusIndex] == "onResult") {
  3793. // Call succeeded
  3794. data = {
  3795. tid: tid,
  3796. data: (me.binary ? response.body : response.message.body)
  3797. };
  3798. event = Ext.create('direct.rpc', data);
  3799. } else {
  3800. //<debug>
  3801. Ext.raise("Unknown AMF return status: " + status[statusIndex]);
  3802. }
  3803. //</debug>
  3804. return event;
  3805. }
  3806. });