amf-debug.js 141 KB

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