Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

jquery.atmosphere.js 107KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731
  1. /**
  2. * Copyright 2012 Jeanfrancois Arcand
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * IE streaming/XDR supports is copied/highly inspired by http://code.google.com/p/jquery-stream/
  18. *
  19. * Copyright 2011, Donghwan Kim
  20. * Licensed under the Apache License, Version 2.0
  21. * http://www.apache.org/licenses/LICENSE-2.0
  22. *
  23. * LocalStorage supports is copied/highly inspired by https://github.com/flowersinthesand/jquery-socket
  24. * Copyright 2011, Donghwan Kim
  25. * Licensed under the Apache License, Version 2.0
  26. * http://www.apache.org/licenses/LICENSE-2.0
  27. * */
  28. /**
  29. * Official documentation of this library: https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-API
  30. */
  31. jQuery.atmosphere = function() {
  32. jQuery(window).bind("unload.atmosphere", function() {
  33. jQuery.atmosphere.unsubscribe();
  34. });
  35. // Prevent ESC to kill the connection from Firefox.
  36. jQuery(window).keypress(function(e){
  37. if(e.keyCode == 27){
  38. e.preventDefault();
  39. }
  40. });
  41. var parseHeaders = function(headerString) {
  42. var match, rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, headers = {};
  43. while (match = rheaders.exec(headerString)) {
  44. headers[match[1]] = match[2];
  45. }
  46. return headers;
  47. };
  48. return {
  49. version : "1.0.12",
  50. requests : [],
  51. callbacks : [],
  52. onError : function(response) {
  53. },
  54. onClose : function(response) {
  55. },
  56. onOpen : function(response) {
  57. },
  58. onMessage : function(response) {
  59. },
  60. onReconnect : function(request, response) {
  61. },
  62. onMessagePublished : function(response) {
  63. },
  64. onTransportFailure : function (reason, request) {
  65. },
  66. onLocalMessage : function (response) {
  67. },
  68. AtmosphereRequest : function(options) {
  69. /**
  70. * {Object} Request parameters.
  71. * @private
  72. */
  73. var _request = {
  74. timeout: 300000,
  75. method: 'GET',
  76. headers: {},
  77. contentType : '',
  78. callback: null,
  79. url : '',
  80. data : '',
  81. suspend : true,
  82. maxRequest : -1,
  83. reconnect : true,
  84. maxStreamingLength : 10000000,
  85. lastIndex : 0,
  86. logLevel : 'info',
  87. requestCount : 0,
  88. fallbackMethod: 'GET',
  89. fallbackTransport : 'streaming',
  90. transport : 'long-polling',
  91. webSocketImpl: null,
  92. webSocketUrl: null,
  93. webSocketPathDelimiter: "@@",
  94. enableXDR : false,
  95. rewriteURL : false,
  96. attachHeadersAsQueryString : true,
  97. executeCallbackBeforeReconnect : false,
  98. readyState : 0,
  99. lastTimestamp : 0,
  100. withCredentials : false,
  101. trackMessageLength : false ,
  102. messageDelimiter : '|',
  103. connectTimeout : -1,
  104. reconnectInterval : 0,
  105. dropAtmosphereHeaders : true,
  106. uuid : 0,
  107. shared : false,
  108. readResponsesHeaders : true,
  109. maxReconnectOnClose: 5,
  110. enableProtocol: false,
  111. onError : function(response) {
  112. },
  113. onClose : function(response) {
  114. },
  115. onOpen : function(response) {
  116. },
  117. onMessage : function(response) {
  118. },
  119. onReconnect : function(request, response) {
  120. },
  121. onMessagePublished : function(response) {
  122. },
  123. onTransportFailure : function (reason, request) {
  124. },
  125. onLocalMessage : function (request) {
  126. }
  127. };
  128. /**
  129. * {Object} Request's last response.
  130. * @private
  131. */
  132. var _response = {
  133. status: 200,
  134. responseBody : '',
  135. headers : [],
  136. state : "messageReceived",
  137. transport : "polling",
  138. error: null,
  139. request : null,
  140. partialMessage : "",
  141. id : 0
  142. };
  143. /**
  144. * {websocket} Opened web socket.
  145. *
  146. * @private
  147. */
  148. var _websocket = null;
  149. /**
  150. * {SSE} Opened SSE.
  151. *
  152. * @private
  153. */
  154. var _sse = null;
  155. /**
  156. * {XMLHttpRequest, ActiveXObject} Opened ajax request (in case of
  157. * http-streaming or long-polling)
  158. *
  159. * @private
  160. */
  161. var _activeRequest = null;
  162. /**
  163. * {Object} Object use for streaming with IE.
  164. *
  165. * @private
  166. */
  167. var _ieStream = null;
  168. /**
  169. * {Object} Object use for jsonp transport.
  170. *
  171. * @private
  172. */
  173. var _jqxhr = null;
  174. /**
  175. * {boolean} If request has been subscribed or not.
  176. *
  177. * @private
  178. */
  179. var _subscribed = true;
  180. /**
  181. * {number} Number of test reconnection.
  182. *
  183. * @private
  184. */
  185. var _requestCount = 0;
  186. /**
  187. * {boolean} If request is currently aborded.
  188. *
  189. * @private
  190. */
  191. var _abordingConnection = false;
  192. /**
  193. * A local "channel' of communication.
  194. * @private
  195. */
  196. var _localSocketF = null;
  197. /**
  198. * The storage used.
  199. * @private
  200. */
  201. var _storageService;
  202. /**
  203. * Local communication
  204. * @private
  205. */
  206. var _localStorageService = null;
  207. /**
  208. * A Unique ID
  209. * @private
  210. */
  211. var guid = jQuery.now();
  212. /** Trace time */
  213. var _traceTimer;
  214. // Automatic call to subscribe
  215. _subscribe(options);
  216. /**
  217. * Initialize atmosphere request object.
  218. *
  219. * @private
  220. */
  221. function _init() {
  222. _subscribed = true;
  223. _abordingConnection = false;
  224. _requestCount = 0;
  225. _websocket = null;
  226. _sse = null;
  227. _activeRequest = null;
  228. _ieStream = null;
  229. }
  230. /**
  231. * Re-initialize atmosphere object.
  232. * @private
  233. */
  234. function _reinit() {
  235. _clearState();
  236. _init();
  237. }
  238. /**
  239. * Subscribe request using request transport. <br>
  240. * If request is currently opened, this one will be closed.
  241. *
  242. * @param {Object}
  243. * Request parameters.
  244. * @private
  245. */
  246. function _subscribe(options) {
  247. _reinit();
  248. _request = jQuery.extend(_request, options);
  249. // Allow at least 1 request
  250. _request.mrequest = _request.reconnect;
  251. if (!_request.reconnect) {
  252. _request.reconnect = true;
  253. }
  254. }
  255. /**
  256. * Check if web socket is supported (check for custom implementation
  257. * provided by request object or browser implementation).
  258. *
  259. * @returns {boolean} True if web socket is supported, false
  260. * otherwise.
  261. * @private
  262. */
  263. function _supportWebsocket() {
  264. return _request.webSocketImpl != null || window.WebSocket || window.MozWebSocket;
  265. }
  266. /**
  267. * Check if server side events (SSE) is supported (check for custom implementation
  268. * provided by request object or browser implementation).
  269. *
  270. * @returns {boolean} True if web socket is supported, false
  271. * otherwise.
  272. * @private
  273. */
  274. function _supportSSE() {
  275. return window.EventSource;
  276. }
  277. /**
  278. * Open request using request transport. <br>
  279. * If request transport is 'websocket' but websocket can't be
  280. * opened, request will automatically reconnect using fallback
  281. * transport.
  282. *
  283. * @private
  284. */
  285. function _execute() {
  286. // Shared across multiple tabs/windows.
  287. if (_request.shared) {
  288. _localStorageService = _local(_request);
  289. if (_localStorageService != null) {
  290. if (_request.logLevel == 'debug') {
  291. jQuery.atmosphere.debug("Storage service available. All communication will be local");
  292. }
  293. if (_localStorageService.open(_request)) {
  294. // Local connection.
  295. return;
  296. }
  297. }
  298. if (_request.logLevel == 'debug') {
  299. jQuery.atmosphere.debug("No Storage service available.");
  300. }
  301. // Wasn't local or an error occurred
  302. _localStorageService = null;
  303. }
  304. // Protocol
  305. _request.firstMessage= true;
  306. _request.ctime = jQuery.now();
  307. if (_request.transport != 'websocket' && _request.transport != 'sse') {
  308. // Gives a chance to the connection to be established before calling the callback
  309. setTimeout(function() {
  310. _open('opening', _request.transport, _request);
  311. }, 500);
  312. _executeRequest();
  313. } else if (_request.transport == 'websocket') {
  314. if (!_supportWebsocket()) {
  315. _reconnectWithFallbackTransport("Websocket is not supported, using request.fallbackTransport (" + _request.fallbackTransport + ")");
  316. } else {
  317. _executeWebSocket(false);
  318. }
  319. } else if (_request.transport == 'sse') {
  320. if (!_supportSSE()) {
  321. _reconnectWithFallbackTransport("Server Side Events(SSE) is not supported, using request.fallbackTransport (" + _request.fallbackTransport + ")");
  322. } else {
  323. _executeSSE(false);
  324. }
  325. }
  326. }
  327. function _local(request) {
  328. var trace, connector, orphan, name = "atmosphere-" + request.url, connectors = {
  329. storage: function() {
  330. if (!jQuery.atmosphere.supportStorage()) {
  331. return;
  332. }
  333. var storage = window.localStorage,
  334. get = function(key) {
  335. return jQuery.parseJSON(storage.getItem(name + "-" + key));
  336. },
  337. set = function(key, value) {
  338. storage.setItem(name + "-" + key, jQuery.stringifyJSON(value));
  339. };
  340. return {
  341. init: function() {
  342. set("children", get("children").concat([guid]));
  343. jQuery(window).on("storage.socket", function(event) {
  344. event = event.originalEvent;
  345. if (event.key === name && event.newValue) {
  346. listener(event.newValue);
  347. }
  348. });
  349. return get("opened");
  350. },
  351. signal: function(type, data) {
  352. storage.setItem(name, jQuery.stringifyJSON({target: "p", type: type, data: data}));
  353. },
  354. close: function() {
  355. var index, children = get("children");
  356. jQuery(window).off("storage.socket");
  357. if (children) {
  358. index = jQuery.inArray(request.id, children);
  359. if (index > -1) {
  360. children.splice(index, 1);
  361. set("children", children);
  362. }
  363. }
  364. }
  365. };
  366. },
  367. windowref: function() {
  368. var win = window.open("", name.replace(/\W/g, ""));
  369. if (!win || win.closed || !win.callbacks) {
  370. return;
  371. }
  372. return {
  373. init: function() {
  374. win.callbacks.push(listener);
  375. win.children.push(guid);
  376. return win.opened;
  377. },
  378. signal: function(type, data) {
  379. if (!win.closed && win.fire) {
  380. win.fire(jQuery.stringifyJSON({target: "p", type: type, data: data}));
  381. }
  382. },
  383. close : function() {
  384. function remove(array, e) {
  385. var index = jQuery.inArray(e, array);
  386. if (index > -1) {
  387. array.splice(index, 1);
  388. }
  389. }
  390. // Removes traces only if the parent is alive
  391. if (!orphan) {
  392. remove(win.callbacks, listener);
  393. remove(win.children, guid);
  394. }
  395. }
  396. };
  397. }
  398. };
  399. // Receives open, close and message command from the parent
  400. function listener(string) {
  401. var command = jQuery.parseJSON(string), data = command.data;
  402. if (command.target === "c") {
  403. switch (command.type) {
  404. case "open":
  405. _open("opening", 'local', _request)
  406. break;
  407. case "close":
  408. if (!orphan) {
  409. orphan = true;
  410. if (data.reason === "aborted") {
  411. _close();
  412. } else {
  413. // Gives the heir some time to reconnect
  414. if (data.heir === guid) {
  415. _execute();
  416. } else {
  417. setTimeout(function() {
  418. _execute();
  419. }, 100);
  420. }
  421. }
  422. }
  423. break;
  424. case "message":
  425. _prepareCallback(data, "messageReceived", 200, request.transport);
  426. break;
  427. case "localMessage":
  428. _localMessage(data);
  429. break;
  430. }
  431. }
  432. }
  433. function findTrace() {
  434. var matcher = new RegExp("(?:^|; )(" + encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie);
  435. if (matcher) {
  436. return jQuery.parseJSON(decodeURIComponent(matcher[2]));
  437. }
  438. }
  439. // Finds and validates the parent socket's trace from the cookie
  440. trace = findTrace();
  441. if (!trace || jQuery.now() - trace.ts > 1000) {
  442. return;
  443. }
  444. // Chooses a connector
  445. connector = connectors.storage() || connectors.windowref();
  446. if (!connector) {
  447. return;
  448. }
  449. return {
  450. open: function() {
  451. var parentOpened;
  452. // Checks the shared one is alive
  453. _traceTimer = setInterval(function() {
  454. var oldTrace = trace;
  455. trace = findTrace();
  456. if (!trace || oldTrace.ts === trace.ts) {
  457. // Simulates a close signal
  458. listener(jQuery.stringifyJSON({target: "c", type: "close", data: {reason: "error", heir: oldTrace.heir}}));
  459. }
  460. }, 1000);
  461. parentOpened = connector.init();
  462. if (parentOpened) {
  463. // Firing the open event without delay robs the user of the opportunity to bind connecting event handlers
  464. setTimeout(function() {
  465. _open("opening", 'local', request)
  466. }, 50);
  467. }
  468. return parentOpened;
  469. },
  470. send: function(event) {
  471. connector.signal("send", event);
  472. },
  473. localSend: function(event) {
  474. connector.signal("localSend", jQuery.stringifyJSON({id: guid , event: event}));
  475. },
  476. close: function() {
  477. // Do not signal the parent if this method is executed by the unload event handler
  478. if (!_abordingConnection) {
  479. clearInterval(_traceTimer);
  480. connector.signal("close");
  481. connector.close();
  482. }
  483. }
  484. };
  485. };
  486. function share() {
  487. var storageService, name = "atmosphere-" + _request.url, servers = {
  488. // Powered by the storage event and the localStorage
  489. // http://www.w3.org/TR/webstorage/#event-storage
  490. storage: function() {
  491. if (!jQuery.atmosphere.supportStorage()) {
  492. return;
  493. }
  494. var storage = window.localStorage;
  495. return {
  496. init: function() {
  497. // Handles the storage event
  498. jQuery(window).on("storage.socket", function(event) {
  499. event = event.originalEvent;
  500. // When a deletion, newValue initialized to null
  501. if (event.key === name && event.newValue) {
  502. listener(event.newValue);
  503. }
  504. });
  505. },
  506. signal: function(type, data) {
  507. storage.setItem(name, jQuery.stringifyJSON({target: "c", type: type, data: data}));
  508. },
  509. get: function(key) {
  510. return jQuery.parseJSON(storage.getItem(name + "-" + key));
  511. },
  512. set: function(key, value) {
  513. storage.setItem(name + "-" + key, jQuery.stringifyJSON(value));
  514. },
  515. close : function() {
  516. jQuery(window).off("storage.socket");
  517. storage.removeItem(name);
  518. storage.removeItem(name + "-opened");
  519. storage.removeItem(name + "-children");
  520. }
  521. };
  522. },
  523. // Powered by the window.open method
  524. // https://developer.mozilla.org/en/DOM/window.open
  525. windowref: function() {
  526. // Internet Explorer raises an invalid argument error
  527. // when calling the window.open method with the name containing non-word characters
  528. var neim = name.replace(/\W/g, ""), win = (jQuery('iframe[name="' + neim + '"]')[0]
  529. || jQuery('<iframe name="' + neim + '" />').hide().appendTo("body")[0]).contentWindow;
  530. return {
  531. init: function() {
  532. // Callbacks from different windows
  533. win.callbacks = [listener];
  534. // In IE 8 and less, only string argument can be safely passed to the function in other window
  535. win.fire = function(string) {
  536. var i;
  537. for (i = 0; i < win.callbacks.length; i++) {
  538. win.callbacks[i](string);
  539. }
  540. };
  541. },
  542. signal: function(type, data) {
  543. if (!win.closed && win.fire) {
  544. win.fire(jQuery.stringifyJSON({target: "c", type: type, data: data}));
  545. }
  546. },
  547. get: function(key) {
  548. return !win.closed ? win[key] : null;
  549. },
  550. set: function(key, value) {
  551. if (!win.closed) {
  552. win[key] = value;
  553. }
  554. },
  555. close : function() {}
  556. };
  557. }
  558. };
  559. // Receives send and close command from the children
  560. function listener(string) {
  561. var command = jQuery.parseJSON(string), data = command.data;
  562. if (command.target === "p") {
  563. switch (command.type) {
  564. case "send":
  565. _push(data);
  566. break;
  567. case "localSend":
  568. _localMessage(data);
  569. break;
  570. case "close":
  571. _close();
  572. break;
  573. }
  574. }
  575. }
  576. _localSocketF = function propagateMessageEvent(context) {
  577. storageService.signal("message", context);
  578. }
  579. function leaveTrace() {
  580. document.cookie = encodeURIComponent(name) + "=" +
  581. // Opera's JSON implementation ignores a number whose a last digit of 0 strangely
  582. // but has no problem with a number whose a last digit of 9 + 1
  583. encodeURIComponent(jQuery.stringifyJSON({ts: jQuery.now() + 1, heir: (storageService.get("children") || [])[0]}));
  584. }
  585. // Chooses a storageService
  586. storageService = servers.storage() || servers.windowref();
  587. storageService.init();
  588. if (_request.logLevel == 'debug') {
  589. jQuery.atmosphere.debug("Installed StorageService " + storageService);
  590. }
  591. // List of children sockets
  592. storageService.set("children", []);
  593. if (storageService.get("opened") != null && !storageService.get("opened")) {
  594. // Flag indicating the parent socket is opened
  595. storageService.set("opened", false);
  596. }
  597. // Leaves traces
  598. leaveTrace();
  599. _traceTimer = setInterval(leaveTrace, 1000);
  600. _storageService = storageService;
  601. }
  602. /**
  603. * @private
  604. */
  605. function _open(state, transport, request) {
  606. if (_request.shared && transport != 'local') {
  607. share();
  608. }
  609. if (_storageService != null) {
  610. _storageService.set("opened", true);
  611. }
  612. request.close = function() {
  613. _close();
  614. };
  615. _response.request = request;
  616. var prevState = _response.state;
  617. _response.state = state;
  618. _response.status = 200;
  619. var prevTransport = _response.transport;
  620. _response.transport = transport;
  621. var _body = _response.responseBody;
  622. _invokeCallback();
  623. _response.responseBody = _body;
  624. _response.state = prevState;
  625. _response.transport = prevTransport;
  626. }
  627. /**
  628. * Execute request using jsonp transport.
  629. *
  630. * @param request
  631. * {Object} request Request parameters, if
  632. * undefined _request object will be used.
  633. * @private
  634. */
  635. function _jsonp(request) {
  636. // When CORS is enabled, make sure we force the proper transport.
  637. request.transport="jsonp";
  638. var rq = _request;
  639. if ((request != null) && (typeof(request) != 'undefined')) {
  640. rq = request;
  641. }
  642. var url = rq.url;
  643. var data = rq.data;
  644. if (rq.attachHeadersAsQueryString) {
  645. url = _attachHeaders(rq);
  646. if (data != '') {
  647. url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data);
  648. }
  649. data = '';
  650. }
  651. _jqxhr = jQuery.ajax({
  652. url : url,
  653. type : rq.method,
  654. dataType: "jsonp",
  655. error : function(jqXHR, textStatus, errorThrown) {
  656. if (jqXHR.status < 300) {
  657. _reconnect(_jqxhr, rq);
  658. } else {
  659. _prepareCallback(textStatus, "error", jqXHR.status, rq.transport);
  660. }
  661. },
  662. jsonp : "jsonpTransport",
  663. success: function(json) {
  664. if (rq.reconnect && (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest)) {
  665. _readHeaders(_jqxhr, rq);
  666. if (!rq.executeCallbackBeforeReconnect) {
  667. _reconnect(_jqxhr, rq);
  668. }
  669. var msg = json.message;
  670. if (msg != null && typeof msg != 'string') {
  671. try {
  672. msg = jQuery.stringifyJSON(msg);
  673. } catch (err) {
  674. // The message was partial
  675. }
  676. }
  677. if (_handleProtocol(rq, msg)) {
  678. _prepareCallback(msg, "messageReceived", 200, rq.transport);
  679. }
  680. if (rq.executeCallbackBeforeReconnect) {
  681. _reconnect(_jqxhr, rq);
  682. }
  683. } else {
  684. jQuery.atmosphere.log(_request.logLevel, ["JSONP reconnect maximum try reached " + _request.requestCount]);
  685. _onError();
  686. }
  687. },
  688. data : rq.data,
  689. beforeSend : function(jqXHR) {
  690. _doRequest(jqXHR, rq, false);
  691. }
  692. });
  693. }
  694. /**
  695. * Execute request using ajax transport.
  696. *
  697. * @param request
  698. * {Object} request Request parameters, if
  699. * undefined _request object will be used.
  700. * @private
  701. */
  702. function _ajax(request) {
  703. var rq = _request;
  704. if ((request != null) && (typeof(request) != 'undefined')) {
  705. rq = request;
  706. }
  707. var url = rq.url;
  708. var data = rq.data;
  709. if (rq.attachHeadersAsQueryString) {
  710. url = _attachHeaders(rq);
  711. if (data != '') {
  712. url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data);
  713. }
  714. data = '';
  715. }
  716. var async = typeof(rq.async) != 'undefined' ? rq.async : true;
  717. _jqxhr = jQuery.ajax({
  718. url : url,
  719. type : rq.method,
  720. error : function(jqXHR, textStatus, errorThrown) {
  721. if (jqXHR.status < 300) {
  722. _reconnect(_jqxhr, rq);
  723. } else {
  724. _prepareCallback(textStatus, "error", jqXHR.status, rq.transport);
  725. }
  726. },
  727. success: function(data, textStatus, jqXHR) {
  728. if (rq.reconnect && (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest)) {
  729. if (!rq.executeCallbackBeforeReconnect) {
  730. _reconnect(_jqxhr, rq);
  731. }
  732. if (_handleProtocol(rq, data)) {
  733. _prepareCallback(data, "messageReceived", 200, rq.transport);
  734. }
  735. if (rq.executeCallbackBeforeReconnect) {
  736. _reconnect(_jqxhr, rq);
  737. }
  738. } else {
  739. jQuery.atmosphere.log(_request.logLevel, ["AJAX reconnect maximum try reached " + _request.requestCount]);
  740. _onError();
  741. }
  742. },
  743. beforeSend : function(jqXHR) {
  744. _doRequest(jqXHR, rq, false);
  745. },
  746. crossDomain : rq.enableXDR,
  747. async: async
  748. });
  749. }
  750. /**
  751. * Build websocket object.
  752. *
  753. * @param location
  754. * {string} Web socket url.
  755. * @returns {websocket} Web socket object.
  756. * @private
  757. */
  758. function _getWebSocket(location) {
  759. if (_request.webSocketImpl != null) {
  760. return _request.webSocketImpl;
  761. } else {
  762. if (window.WebSocket) {
  763. return new WebSocket(location);
  764. } else {
  765. return new MozWebSocket(location);
  766. }
  767. }
  768. }
  769. /**
  770. * Build web socket url from request url.
  771. *
  772. * @return {string} Web socket url (start with "ws" or "wss" for
  773. * secure web socket).
  774. * @private
  775. */
  776. function _buildWebSocketUrl() {
  777. var url = _attachHeaders(_request);
  778. return decodeURI(jQuery('<a href="' + url + '"/>')[0].href.replace(/^http/, "ws"));
  779. }
  780. /**
  781. * Build SSE url from request url.
  782. *
  783. * @return a url with Atmosphere's headers
  784. * @private
  785. */
  786. function _buildSSEUrl() {
  787. var url = _attachHeaders(_request);
  788. return url;
  789. }
  790. /**
  791. * Open SSE. <br>
  792. * Automatically use fallback transport if SSE can't be
  793. * opened.
  794. *
  795. * @private
  796. */
  797. function _executeSSE(sseOpened) {
  798. _response.transport = "sse";
  799. var location = _buildSSEUrl(_request.url);
  800. if (_request.logLevel == 'debug') {
  801. jQuery.atmosphere.debug("Invoking executeSSE");
  802. jQuery.atmosphere.debug("Using URL: " + location);
  803. }
  804. if (sseOpened) {
  805. _open('re-opening', "sse", _request);
  806. }
  807. if (_request.enableProtocol && sseOpened) {
  808. var time = jQuery.now() - _request.ctime;
  809. _request.lastTimestamp = Number(_request.stime) + Number(time);
  810. }
  811. if (sseOpened && !_request.reconnect) {
  812. if (_sse != null) {
  813. _clearState();
  814. }
  815. return;
  816. }
  817. _sse = new EventSource(location, {withCredentials: _request.withCredentials});
  818. if (_request.connectTimeout > 0) {
  819. _request.id = setTimeout(function() {
  820. if (!sseOpened) {
  821. _clearState();
  822. }
  823. }, _request.connectTimeout);
  824. }
  825. _sse.onopen = function(event) {
  826. if (_request.logLevel == 'debug') {
  827. jQuery.atmosphere.debug("SSE successfully opened");
  828. }
  829. if (!sseOpened) {
  830. _open('opening', "sse", _request);
  831. }
  832. sseOpened = true;
  833. if (_request.method == 'POST') {
  834. _response.state = "messageReceived";
  835. _sse.send(_request.data);
  836. }
  837. };
  838. _sse.onmessage = function(message) {
  839. if (message.origin != window.location.protocol + "//" + window.location.host) {
  840. jQuery.atmosphere.log(_request.logLevel, ["Origin was not " + window.location.protocol + "//" + window.location.host]);
  841. return;
  842. }
  843. if (!_handleProtocol(_request, message.data)) return;
  844. _response.state = 'messageReceived';
  845. _response.status = 200;
  846. var message = message.data;
  847. var skipCallbackInvocation = _trackMessageSize(message, _request, _response);
  848. if (jQuery.trim(message).length == 0) {
  849. skipCallbackInvocation = true;
  850. }
  851. if (!skipCallbackInvocation) {
  852. _invokeCallback();
  853. _response.responseBody = '';
  854. }
  855. };
  856. _sse.onerror = function(message) {
  857. clearTimeout(_request.id);
  858. _response.state = 'closed';
  859. _response.responseBody = "";
  860. _response.status = !sseOpened ? 501 : 200;
  861. _invokeCallback();
  862. _clearState();
  863. if (_abordingConnection) {
  864. jQuery.atmosphere.log(_request.logLevel, ["SSE closed normally"]);
  865. } else if (!sseOpened) {
  866. _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending");
  867. } else if (_request.reconnect && (_response.transport == 'sse')) {
  868. if (_requestCount++ < _request.maxReconnectOnClose) {
  869. _request.id = setTimeout(function() {
  870. _executeSSE(true);
  871. }, _request.reconnectInterval);
  872. _response.responseBody = "";
  873. } else {
  874. jQuery.atmosphere.log(_request.logLevel, ["SSE reconnect maximum try reached " + _requestCount]);
  875. _onError();
  876. }
  877. }
  878. };
  879. }
  880. /**
  881. * Open web socket. <br>
  882. * Automatically use fallback transport if web socket can't be
  883. * opened.
  884. *
  885. * @private
  886. */
  887. function _executeWebSocket(webSocketOpened) {
  888. _response.transport = "websocket";
  889. if (_request.enableProtocol && webSocketOpened) {
  890. var time = jQuery.now() - _request.ctime;
  891. _request.lastTimestamp = Number(_request.stime) + Number(time);
  892. }
  893. var location = _buildWebSocketUrl(_request.url);
  894. var closed = false;
  895. if (_request.logLevel == 'debug') {
  896. jQuery.atmosphere.debug("Invoking executeWebSocket");
  897. jQuery.atmosphere.debug("Using URL: " + location);
  898. }
  899. if (webSocketOpened) {
  900. _open('re-opening', "websocket", _request);
  901. }
  902. if (webSocketOpened && !_request.reconnect) {
  903. if (_websocket != null) {
  904. _clearState();
  905. }
  906. return;
  907. }
  908. _websocket = _getWebSocket(location);
  909. if (_request.connectTimeout > 0) {
  910. _request.id = setTimeout(function() {
  911. if (!webSocketOpened) {
  912. var _message = {
  913. code : 1002,
  914. reason : "",
  915. wasClean : false
  916. };
  917. _websocket.onclose(_message);
  918. // Close it anyway
  919. try {
  920. _clearState();
  921. } catch (e) {
  922. }
  923. return;
  924. }
  925. }, _request.connectTimeout);
  926. }
  927. _request.id = setTimeout(function() {
  928. setTimeout(function () {
  929. _clearState();
  930. }, _request.reconnectInterval)
  931. }, _request.timeout);
  932. _websocket.onopen = function(message) {
  933. if (_request.logLevel == 'debug') {
  934. jQuery.atmosphere.debug("Websocket successfully opened");
  935. }
  936. if (!webSocketOpened) {
  937. _open('opening', "websocket", _request);
  938. }
  939. webSocketOpened = true;
  940. if (_request.method == 'POST') {
  941. _response.state = "messageReceived";
  942. _websocket.send(_request.data);
  943. }
  944. };
  945. _websocket.onmessage = function(message) {
  946. clearTimeout(_request.id);
  947. _request.id = setTimeout(function() {
  948. setTimeout(function () {
  949. _clearState();
  950. }, _request.reconnectInterval)
  951. }, _request.timeout);
  952. if (!_handleProtocol(_request, message.data)) return;
  953. _response.state = 'messageReceived';
  954. _response.status = 200;
  955. var message = message.data;
  956. var skipCallbackInvocation = _trackMessageSize(message, _request, _response);
  957. if (!skipCallbackInvocation) {
  958. _invokeCallback();
  959. _response.responseBody = '';
  960. }
  961. };
  962. _websocket.onerror = function(message) {
  963. clearTimeout(_request.id)
  964. };
  965. _websocket.onclose = function(message) {
  966. if (closed) return
  967. var reason = message.reason;
  968. if (reason === "") {
  969. switch (message.code) {
  970. case 1000:
  971. reason = "Normal closure; the connection successfully completed whatever purpose for which " +
  972. "it was created.";
  973. break;
  974. case 1001:
  975. reason = "The endpoint is going away, either because of a server failure or because the " +
  976. "browser is navigating away from the page that opened the connection.";
  977. break;
  978. case 1002:
  979. reason = "The endpoint is terminating the connection due to a protocol error.";
  980. break;
  981. case 1003:
  982. reason = "The connection is being terminated because the endpoint received data of a type it " +
  983. "cannot accept (for example, a text-only endpoint received binary data).";
  984. break;
  985. case 1004:
  986. reason = "The endpoint is terminating the connection because a data frame was received that " +
  987. "is too large.";
  988. break;
  989. case 1005:
  990. reason = "Unknown: no status code was provided even though one was expected.";
  991. break;
  992. case 1006:
  993. reason = "Connection was closed abnormally (that is, with no close frame being sent).";
  994. break;
  995. }
  996. }
  997. jQuery.atmosphere.warn("Websocket closed, reason: " + reason);
  998. jQuery.atmosphere.warn("Websocket closed, wasClean: " + message.wasClean);
  999. _response.state = 'closed';
  1000. _response.responseBody = "";
  1001. _response.status = !webSocketOpened ? 501 : 200;
  1002. _invokeCallback();
  1003. clearTimeout(_request.id);
  1004. closed = true;
  1005. if (_abordingConnection) {
  1006. jQuery.atmosphere.log(_request.logLevel, ["Websocket closed normally"]);
  1007. } else if (!webSocketOpened) {
  1008. _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending");
  1009. } else if (_request.reconnect && _response.transport == 'websocket') {
  1010. _clearState();
  1011. if (_request.reconnect && _requestCount++ < _request.maxReconnectOnClose) {
  1012. _request.id = setTimeout(function() {
  1013. _response.responseBody = "";
  1014. _executeWebSocket(true);
  1015. }, _request.reconnectInterval);
  1016. } else {
  1017. jQuery.atmosphere.log(_request.logLevel, ["Websocket reconnect maximum try reached " + _requestCount]);
  1018. jQuery.atmosphere.warn("Websocket error, reason: " + message.reason);
  1019. _onError();
  1020. }
  1021. }
  1022. };
  1023. }
  1024. function _handleProtocol(request, message) {
  1025. // The first messages is always the uuid.
  1026. if (request.enableProtocol && request.firstMessage) {
  1027. request.firstMessage = false;
  1028. var messages = message.split(request.messageDelimiter);
  1029. request.uuid = messages[0];
  1030. request.stime = messages[1];
  1031. return false;
  1032. }
  1033. return true;
  1034. }
  1035. function _onError() {
  1036. _clearState();
  1037. _response.state = 'error';
  1038. _response.responseBody = "";
  1039. _response.status = 500;
  1040. _invokeCallback();
  1041. }
  1042. /**
  1043. * Track received message and make sure callbacks/functions are only invoked when the complete message
  1044. * has been received.
  1045. *
  1046. * @param message
  1047. * @param request
  1048. * @param response
  1049. */
  1050. function _trackMessageSize(message, request, response) {
  1051. if (request.trackMessageLength) {
  1052. // If we have found partial message, prepend them.
  1053. if (response.partialMessage.length != 0) {
  1054. message = response.partialMessage + message;
  1055. }
  1056. var messages = [];
  1057. var messageLength = 0;
  1058. var messageStart = message.indexOf(request.messageDelimiter);
  1059. while (messageStart != -1) {
  1060. messageLength = message.substring(messageLength, messageStart);
  1061. message = message.substring(messageStart + request.messageDelimiter.length, message.length);
  1062. if (message.length == 0 || message.length < messageLength) break;
  1063. messageStart = message.indexOf(request.messageDelimiter);
  1064. messages.push(message.substring(0, messageLength));
  1065. }
  1066. if (messages.length == 0 || (messageStart != -1 && message.length != 0 && messageLength != message.length)){
  1067. response.partialMessage = messageLength + request.messageDelimiter + message ;
  1068. } else {
  1069. response.partialMessage = "";
  1070. }
  1071. if (messages.length != 0) {
  1072. response.responseBody = messages.join(request.messageDelimiter);
  1073. return false;
  1074. } else {
  1075. return true;
  1076. }
  1077. } else {
  1078. response.responseBody = message;
  1079. }
  1080. return false;
  1081. }
  1082. /**
  1083. * Reconnect request with fallback transport. <br>
  1084. * Used in case websocket can't be opened.
  1085. *
  1086. * @private
  1087. */
  1088. function _reconnectWithFallbackTransport(errorMessage) {
  1089. jQuery.atmosphere.log(_request.logLevel, [errorMessage]);
  1090. if (typeof(_request.onTransportFailure) != 'undefined') {
  1091. _request.onTransportFailure(errorMessage, _request);
  1092. } else if (typeof(jQuery.atmosphere.onTransportFailure) != 'undefined') {
  1093. jQuery.atmosphere.onTransportFailure(errorMessage, _request);
  1094. }
  1095. _request.transport = _request.fallbackTransport;
  1096. var reconnect = _request.reconnect && _requestCount++ < _request.maxReconnectOnClose;
  1097. if (reconnect && _request.transport != 'none' || _request.transport == null) {
  1098. _request.method = _request.fallbackMethod;
  1099. _response.transport = _request.fallbackTransport;
  1100. _request.id = setTimeout(function() {
  1101. _execute();
  1102. }, _request.reconnectInterval);
  1103. } else if (!reconnect) {
  1104. _onError();
  1105. }
  1106. }
  1107. /**
  1108. * Get url from request and attach headers to it.
  1109. *
  1110. * @param request
  1111. * {Object} request Request parameters, if
  1112. * undefined _request object will be used.
  1113. *
  1114. * @returns {Object} Request object, if undefined,
  1115. * _request object will be used.
  1116. * @private
  1117. */
  1118. function _attachHeaders(request) {
  1119. var rq = _request;
  1120. if ((request != null) && (typeof(request) != 'undefined')) {
  1121. rq = request;
  1122. }
  1123. var url = rq.url;
  1124. // If not enabled
  1125. if (!rq.attachHeadersAsQueryString) return url;
  1126. // If already added
  1127. if (url.indexOf("X-Atmosphere-Framework") != -1) {
  1128. return url;
  1129. }
  1130. url += (url.indexOf('?') != -1) ? '&' : '?';
  1131. url += "X-Atmosphere-tracking-id=" + rq.uuid;
  1132. url += "&X-Atmosphere-Framework=" + jQuery.atmosphere.version;
  1133. url += "&X-Atmosphere-Transport=" + rq.transport;
  1134. if (rq.trackMessageLength) {
  1135. url += "&X-Atmosphere-TrackMessageSize=" + "true";
  1136. }
  1137. if (rq.lastTimestamp != undefined) {
  1138. url += "&X-Cache-Date=" + rq.lastTimestamp;
  1139. } else {
  1140. url += "&X-Cache-Date=" + 0;
  1141. }
  1142. if (rq.contentType != '') {
  1143. url += "&Content-Type=" + rq.contentType;
  1144. }
  1145. if (rq.enableProtocol) {
  1146. url += "&X-atmo-protocol=true";
  1147. }
  1148. jQuery.each(rq.headers, function(name, value) {
  1149. var h = jQuery.isFunction(value) ? value.call(this, rq, request, _response) : value;
  1150. if (h != null) {
  1151. url += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(h);
  1152. }
  1153. });
  1154. return url;
  1155. }
  1156. /**
  1157. * Build ajax request. <br>
  1158. * Ajax Request is an XMLHttpRequest object, except for IE6 where
  1159. * ajax request is an ActiveXObject.
  1160. *
  1161. * @return {XMLHttpRequest, ActiveXObject} Ajax request.
  1162. * @private
  1163. */
  1164. function _buildAjaxRequest() {
  1165. if (jQuery.browser.msie) {
  1166. if (typeof XMLHttpRequest == "undefined")
  1167. XMLHttpRequest = function () {
  1168. try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
  1169. catch (e) {}
  1170. try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
  1171. catch (e) {}
  1172. try { return new ActiveXObject("Microsoft.XMLHTTP"); }
  1173. catch (e) {}
  1174. //Microsoft.XMLHTTP points to Msxml2.XMLHTTP and is redundant
  1175. throw new Error("This browser does not support XMLHttpRequest.");
  1176. };
  1177. }
  1178. return new XMLHttpRequest();
  1179. }
  1180. /**
  1181. * Execute ajax request. <br>
  1182. *
  1183. * @param request
  1184. * {Object} request Request parameters, if
  1185. * undefined _request object will be used.
  1186. * @private
  1187. */
  1188. function _executeRequest(request) {
  1189. var rq = _request;
  1190. if ((request != null) || (typeof(request) != 'undefined')) {
  1191. rq = request;
  1192. }
  1193. // CORS fake using JSONP
  1194. if ((rq.transport == 'jsonp') || ((rq.enableXDR) && (jQuery.atmosphere.checkCORSSupport()))) {
  1195. _jsonp(rq);
  1196. return;
  1197. }
  1198. if (rq.transport == 'ajax') {
  1199. _ajax(request);
  1200. return;
  1201. }
  1202. if (jQuery.browser.msie && jQuery.browser.version < 10) {
  1203. if ((rq.transport == 'streaming')) {
  1204. rq.enableXDR && window.XDomainRequest ? _ieXDR(rq) : _ieStreaming(rq);
  1205. return;
  1206. }
  1207. if ((rq.enableXDR) && (window.XDomainRequest)) {
  1208. _ieXDR(rq);
  1209. return;
  1210. }
  1211. }
  1212. if (rq.reconnect && ( rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest)) {
  1213. var ajaxRequest = _buildAjaxRequest();
  1214. _doRequest(ajaxRequest, rq, true);
  1215. if (rq.suspend) {
  1216. _activeRequest = ajaxRequest;
  1217. }
  1218. if (rq.transport != 'polling') {
  1219. _response.transport = rq.transport;
  1220. }
  1221. if (!jQuery.browser.msie) {
  1222. ajaxRequest.onerror = function() {
  1223. try {
  1224. _response.status = XMLHttpRequest.status;
  1225. } catch(e) {
  1226. _response.status = 500;
  1227. }
  1228. if (!_response.status) {
  1229. _response.status = 500;
  1230. }
  1231. _clearState();
  1232. if (rq.reconnect) {
  1233. _reconnect(ajaxRequest, rq, true);
  1234. } else {
  1235. _onError();
  1236. }
  1237. };
  1238. }
  1239. ajaxRequest.onreadystatechange = function() {
  1240. if (_abordingConnection) {
  1241. return;
  1242. }
  1243. var skipCallbackInvocation = false;
  1244. var update = false;
  1245. // Remote server disconnected us, reconnect.
  1246. if (rq.transport == 'streaming'
  1247. && rq.readyState > 2
  1248. && ajaxRequest.readyState == 4) {
  1249. rq.readyState = 0;
  1250. rq.lastIndex = 0;
  1251. _reconnect(ajaxRequest, rq, true);
  1252. return;
  1253. }
  1254. rq.readyState = ajaxRequest.readyState;
  1255. if (ajaxRequest.readyState == 4) {
  1256. if (jQuery.browser.msie) {
  1257. update = true;
  1258. } else if (rq.transport == 'streaming') {
  1259. update = true;
  1260. } else if (rq.transport == 'long-polling') {
  1261. update = true;
  1262. clearTimeout(rq.id);
  1263. }
  1264. } else if (rq.transport == 'streaming' && jQuery.browser.msie && ajaxRequest.readyState >= 3) {
  1265. update = true;
  1266. } else if (!jQuery.browser.msie && ajaxRequest.readyState == 3 && ajaxRequest.status == 200 && rq.transport != 'long-polling') {
  1267. update = true;
  1268. } else {
  1269. clearTimeout(rq.id);
  1270. }
  1271. if (update) {
  1272. var responseText = ajaxRequest.responseText;
  1273. // MSIE status can be higher than 1000, Chrome can be 0
  1274. if (ajaxRequest.status >= 500 || ajaxRequest.status == 0) {
  1275. if (rq.reconnect) {
  1276. _reconnect(ajaxRequest, rq, true);
  1277. } else {
  1278. _onError();
  1279. }
  1280. return;
  1281. }
  1282. _readHeaders(ajaxRequest, _request);
  1283. if (rq.transport == 'streaming') {
  1284. var text = responseText.substring(rq.lastIndex, responseText.length);
  1285. _response.isJunkEnded = true;
  1286. //fix junk is comming in parts
  1287. if (!_response.junkFull && (text.indexOf("<!-- Welcome to the Atmosphere Framework.") == -1 || text.indexOf("<!-- EOD -->") == -1)) {
  1288. return;
  1289. }
  1290. _response.junkFull = true;
  1291. //if it's the start and we see the junk start
  1292. //fix for reconnecting on chrome - junk is comming in parts
  1293. if (rq.lastIndex == 0 && text.indexOf("<!-- Welcome to the Atmosphere Framework.") != -1 && text.indexOf("<!-- EOD -->") != -1) {
  1294. _response.isJunkEnded = false;
  1295. }
  1296. if (!_response.isJunkEnded) {
  1297. var endOfJunk = "<!-- EOD -->";
  1298. var endOfJunkLength = endOfJunk.length;
  1299. var junkEnd = text.indexOf(endOfJunk) + endOfJunkLength;
  1300. if (junkEnd > endOfJunkLength && junkEnd != text.length) {
  1301. _response.responseBody = text.substring(junkEnd);
  1302. rq.lastIndex = responseText.length;
  1303. if (!_handleProtocol( _request, _response.responseBody)) {
  1304. return;
  1305. }
  1306. skipCallbackInvocation = _trackMessageSize(_response.responseBody, rq, _response);
  1307. } else {
  1308. skipCallbackInvocation = true;
  1309. }
  1310. } else {
  1311. var message = responseText.substring(rq.lastIndex, responseText.length);
  1312. rq.lastIndex = responseText.length;
  1313. if (!_handleProtocol( _request, message)) {
  1314. return;
  1315. }
  1316. skipCallbackInvocation = _trackMessageSize(message, rq, _response);
  1317. }
  1318. rq.lastIndex = responseText.length;
  1319. if (jQuery.browser.opera) {
  1320. jQuery.atmosphere.iterate(function() {
  1321. if (ajaxRequest.responseText.length > rq.lastIndex) {
  1322. try {
  1323. _response.status = ajaxRequest.status;
  1324. _response.headers = parseHeaders(ajaxRequest.getAllResponseHeaders());
  1325. _readHeaders(ajaxRequest, _request);
  1326. }
  1327. catch(e) {
  1328. _response.status = 404;
  1329. }
  1330. _response.state = "messageReceived";
  1331. _response.responseBody = ajaxRequest.responseText.substring(rq.lastIndex);
  1332. rq.lastIndex = ajaxRequest.responseText.length;
  1333. if (!_handleProtocol( _request, _response.responseBody)) {
  1334. _reconnect(ajaxRequest, rq, false);
  1335. return;
  1336. }
  1337. _invokeCallback();
  1338. if ((rq.transport == 'streaming') && (ajaxRequest.responseText.length > rq.maxStreamingLength)) {
  1339. // Close and reopen connection on large data received
  1340. _clearState();
  1341. _doRequest(_buildAjaxRequest(), rq, true);
  1342. }
  1343. }
  1344. }, 0);
  1345. }
  1346. if (skipCallbackInvocation) {
  1347. return;
  1348. }
  1349. } else {
  1350. if (!_handleProtocol( _request, responseText)) {
  1351. _reconnect(ajaxRequest, rq, false);
  1352. return;
  1353. }
  1354. _trackMessageSize(responseText, rq, _response);
  1355. rq.lastIndex = responseText.length;
  1356. }
  1357. try {
  1358. _response.status = ajaxRequest.status;
  1359. _response.headers = parseHeaders(ajaxRequest.getAllResponseHeaders());
  1360. _readHeaders(ajaxRequest, rq);
  1361. } catch(e) {
  1362. _response.status = 404;
  1363. }
  1364. if (rq.suspend) {
  1365. _response.state = _response.status == 0 ? "closed" : "messageReceived";
  1366. } else {
  1367. _response.state = "messagePublished";
  1368. }
  1369. if (!rq.executeCallbackBeforeReconnect) {
  1370. _reconnect(ajaxRequest, rq, false);
  1371. }
  1372. // For backward compatibility with Atmosphere < 0.8
  1373. if (_response.responseBody.indexOf("parent.callback") != -1) {
  1374. jQuery.atmosphere.log(rq.logLevel, ["parent.callback no longer supported with 0.8 version and up. Please upgrade"]);
  1375. }
  1376. _invokeCallback();
  1377. if (rq.executeCallbackBeforeReconnect) {
  1378. _reconnect(ajaxRequest, rq, false);
  1379. }
  1380. if ((rq.transport == 'streaming') && (responseText.length > rq.maxStreamingLength)) {
  1381. // Close and reopen connection on large data received
  1382. _clearState();
  1383. _doRequest(_buildAjaxRequest(), rq, true);
  1384. }
  1385. }
  1386. };
  1387. ajaxRequest.send(rq.data);
  1388. if (rq.suspend) {
  1389. rq.id = setTimeout(function() {
  1390. if (_subscribed) {
  1391. setTimeout(function () {
  1392. _clearState();
  1393. _executeRequest(rq);
  1394. }, rq.reconnectInterval)
  1395. }
  1396. }, rq.timeout);
  1397. }
  1398. _subscribed = true;
  1399. } else {
  1400. if (rq.logLevel == 'debug') {
  1401. jQuery.atmosphere.log(rq.logLevel, ["Max re-connection reached."]);
  1402. }
  1403. _onError();
  1404. }
  1405. }
  1406. /**
  1407. * Do ajax request.
  1408. * @param ajaxRequest Ajax request.
  1409. * @param request Request parameters.
  1410. * @param create If ajax request has to be open.
  1411. */
  1412. function _doRequest(ajaxRequest, request, create) {
  1413. // Prevent Android to cache request
  1414. var url = _attachHeaders(request);
  1415. url = jQuery.atmosphere.prepareURL(url);
  1416. if (create) {
  1417. ajaxRequest.open(request.method, url, true);
  1418. if (request.connectTimeout > -1) {
  1419. request.id = setTimeout(function() {
  1420. if (request.requestCount == 0) {
  1421. _clearState();
  1422. _prepareCallback("Connect timeout", "closed", 200, request.transport);
  1423. }
  1424. }, request.connectTimeout);
  1425. }
  1426. }
  1427. if (_request.withCredentials) {
  1428. if ("withCredentials" in ajaxRequest) {
  1429. ajaxRequest.withCredentials = true;
  1430. }
  1431. }
  1432. if (!_request.dropAtmosphereHeaders) {
  1433. ajaxRequest.setRequestHeader("X-Atmosphere-Framework", jQuery.atmosphere.version);
  1434. ajaxRequest.setRequestHeader("X-Atmosphere-Transport", request.transport);
  1435. if (request.lastTimestamp != undefined) {
  1436. ajaxRequest.setRequestHeader("X-Cache-Date", request.lastTimestamp);
  1437. } else {
  1438. ajaxRequest.setRequestHeader("X-Cache-Date", 0);
  1439. }
  1440. if (request.trackMessageLength) {
  1441. ajaxRequest.setRequestHeader("X-Atmosphere-TrackMessageSize", "true")
  1442. }
  1443. if (request.contentType != '') {
  1444. ajaxRequest.setRequestHeader("Content-Type", request.contentType);
  1445. }
  1446. ajaxRequest.setRequestHeader("X-Atmosphere-tracking-id", request.uuid);
  1447. }
  1448. jQuery.each(request.headers, function(name, value) {
  1449. var h = jQuery.isFunction(value) ? value.call(this, ajaxRequest, request, create, _response) : value;
  1450. if (h != null) {
  1451. ajaxRequest.setRequestHeader(name, h);
  1452. }
  1453. });
  1454. }
  1455. function _reconnect(ajaxRequest, request, force) {
  1456. var reconnect = request.reconnect && _requestCount++ < request.maxReconnectOnClose;
  1457. if (reconnect && force || (request.suspend && ajaxRequest.status == 200 && request.transport != 'streaming' && _subscribed)) {
  1458. if (request.reconnect) {
  1459. _open('re-opening', request.transport, request);
  1460. request.id = setTimeout(function() {
  1461. _executeRequest();
  1462. }, request.reconnectInterval);
  1463. }
  1464. } else if (!reconnect) {
  1465. _onError();
  1466. }
  1467. }
  1468. // From jquery-stream, which is APL2 licensed as well.
  1469. function _ieXDR(request) {
  1470. if (request.transport != "polling") {
  1471. _ieStream = _configureXDR(request);
  1472. _ieStream.open();
  1473. } else {
  1474. _configureXDR(request).open();
  1475. }
  1476. }
  1477. // From jquery-stream
  1478. function _configureXDR(request) {
  1479. var rq = _request;
  1480. if ((request != null) && (typeof(request) != 'undefined')) {
  1481. rq = request;
  1482. }
  1483. var transport = rq.transport;
  1484. var lastIndex = 0;
  1485. var xdrCallback = function (xdr) {
  1486. var responseBody = xdr.responseText;
  1487. var isJunkEnded = false;
  1488. if (responseBody.indexOf("<!-- Welcome to the Atmosphere Framework.") != -1) {
  1489. isJunkEnded = true;
  1490. }
  1491. if (isJunkEnded) {
  1492. var endOfJunk = "<!-- EOD -->";
  1493. var endOfJunkLenght = endOfJunk.length;
  1494. var junkEnd = responseBody.indexOf(endOfJunk);
  1495. if (junkEnd !== -1) {
  1496. responseBody = responseBody.substring(junkEnd + endOfJunkLenght + lastIndex);
  1497. lastIndex += responseBody.length;
  1498. }
  1499. }
  1500. if (!_handleProtocol(request, responseBody)) return;
  1501. _prepareCallback(responseBody, "messageReceived", 200, transport);
  1502. };
  1503. var xdr = new window.XDomainRequest();
  1504. var rewriteURL = rq.rewriteURL || function(url) {
  1505. // Maintaining session by rewriting URL
  1506. // http://stackoverflow.com/questions/6453779/maintaining-session-by-rewriting-url
  1507. var match = /(?:^|;\s*)(JSESSIONID|PHPSESSID)=([^;]*)/.exec(document.cookie);
  1508. switch (match && match[1]) {
  1509. case "JSESSIONID":
  1510. return url.replace(/;jsessionid=[^\?]*|(\?)|$/, ";jsessionid=" + match[2] + "$1");
  1511. case "PHPSESSID":
  1512. return url.replace(/\?PHPSESSID=[^&]*&?|\?|$/, "?PHPSESSID=" + match[2] + "&").replace(/&$/, "");
  1513. }
  1514. return url;
  1515. };
  1516. // Handles open and message event
  1517. xdr.onprogress = function() {
  1518. handle(xdr);
  1519. };
  1520. // Handles error event
  1521. xdr.onerror = function() {
  1522. // If the server doesn't send anything back to XDR will fail with polling
  1523. if (rq.transport != 'polling') {
  1524. _prepareCallback(xdr.responseText, "error", 500, transport);
  1525. }
  1526. _reconnect(xdr, rq, false);
  1527. };
  1528. // Handles close event
  1529. xdr.onload = function() {
  1530. handle(xdr);
  1531. };
  1532. var handle = function (xdr) {
  1533. // XDomain loop forever on itself without this.
  1534. // TODO: Clearly I need to come with something better than that solution
  1535. if (rq.lastMessage == xdr.responseText) return;
  1536. if (rq.executeCallbackBeforeReconnect) {
  1537. xdrCallback(xdr);
  1538. }
  1539. if (rq.transport == "long-polling" && (rq.reconnect && (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest))) {
  1540. xdr.status = 200;
  1541. _reconnect(xdr, rq, false);
  1542. }
  1543. if (!rq.executeCallbackBeforeReconnect) {
  1544. xdrCallback(xdr);
  1545. }
  1546. rq.lastMessage = xdr.responseText;
  1547. };
  1548. return {
  1549. open: function() {
  1550. if (rq.method == 'POST') {
  1551. rq.attachHeadersAsQueryString = true;
  1552. }
  1553. var url = _attachHeaders(rq);
  1554. if (rq.method == 'POST') {
  1555. url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(rq.data);
  1556. }
  1557. xdr.open(rq.method, rewriteURL(url));
  1558. xdr.send();
  1559. if (rq.connectTimeout > -1) {
  1560. rq.id = setTimeout(function() {
  1561. if (rq.requestCount == 0) {
  1562. _clearState();
  1563. _prepareCallback("Connect timeout", "closed", 200, rq.transport);
  1564. }
  1565. }, rq.connectTimeout);
  1566. }
  1567. },
  1568. close: function() {
  1569. xdr.abort();
  1570. _prepareCallback(xdr.responseText, "closed", 200, transport);
  1571. }
  1572. };
  1573. }
  1574. // From jquery-stream, which is APL2 licensed as well.
  1575. function _ieStreaming(request) {
  1576. _ieStream = _configureIE(request);
  1577. _ieStream.open();
  1578. }
  1579. function _configureIE(request) {
  1580. var rq = _request;
  1581. if ((request != null) && (typeof(request) != 'undefined')) {
  1582. rq = request;
  1583. }
  1584. var stop;
  1585. var doc = new window.ActiveXObject("htmlfile");
  1586. doc.open();
  1587. doc.close();
  1588. var url = rq.url;
  1589. if (rq.transport != 'polling') {
  1590. _response.transport = rq.transport;
  1591. }
  1592. return {
  1593. open: function() {
  1594. var iframe = doc.createElement("iframe");
  1595. url = _attachHeaders(rq);
  1596. if (rq.data != '') {
  1597. url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(rq.data);
  1598. }
  1599. // Finally attach a timestamp to prevent Android and IE caching.
  1600. url = jQuery.atmosphere.prepareURL(url);
  1601. iframe.src = url;
  1602. doc.body.appendChild(iframe);
  1603. // For the server to respond in a consistent format regardless of user agent, we polls response text
  1604. var cdoc = iframe.contentDocument || iframe.contentWindow.document;
  1605. stop = jQuery.atmosphere.iterate(function() {
  1606. try {
  1607. if (!cdoc.firstChild) {
  1608. return;
  1609. }
  1610. // Detects connection failure
  1611. if (cdoc.readyState === "complete") {
  1612. try {
  1613. jQuery.noop(cdoc.fileSize);
  1614. } catch(e) {
  1615. _prepareCallback("Connection Failure", "error", 500, rq.transport);
  1616. return false;
  1617. }
  1618. }
  1619. var res = cdoc.body ? cdoc.body.lastChild : cdoc;
  1620. var readResponse = function() {
  1621. // Clones the element not to disturb the original one
  1622. var clone = res.cloneNode(true);
  1623. // If the last character is a carriage return or a line feed, IE ignores it in the innerText property
  1624. // therefore, we add another non-newline character to preserve it
  1625. clone.appendChild(cdoc.createTextNode("."));
  1626. var text = clone.innerText;
  1627. var isJunkEnded = true;
  1628. if (text.indexOf("<!-- Welcome to the Atmosphere Framework.") == -1) {
  1629. isJunkEnded = false;
  1630. }
  1631. if (isJunkEnded) {
  1632. var endOfJunk = "<!-- EOD -->";
  1633. var endOfJunkLength = endOfJunk.length;
  1634. var junkEnd = text.indexOf(endOfJunk) + endOfJunkLength;
  1635. text = text.substring(junkEnd);
  1636. }
  1637. text = text.substring(0, text.length - 1);
  1638. _handleProtocol(rq, text);
  1639. return text;
  1640. };
  1641. //To support text/html content type
  1642. if (!jQuery.nodeName(res, "pre")) {
  1643. // Injects a plaintext element which renders text without interpreting the HTML and cannot be stopped
  1644. // it is deprecated in HTML5, but still works
  1645. var head = cdoc.head || cdoc.getElementsByTagName("head")[0] || cdoc.documentElement || cdoc;
  1646. var script = cdoc.createElement("script");
  1647. script.text = "document.write('<plaintext>')";
  1648. head.insertBefore(script, head.firstChild);
  1649. head.removeChild(script);
  1650. // The plaintext element will be the response container
  1651. res = cdoc.body.lastChild;
  1652. }
  1653. // Handles open event
  1654. _prepareCallback(readResponse(), "opening", 200, rq.transport);
  1655. // Handles message and close event
  1656. stop = jQuery.atmosphere.iterate(function() {
  1657. var text = readResponse();
  1658. if (text.length > rq.lastIndex) {
  1659. _response.status = 200;
  1660. // Empties response every time that it is handled
  1661. res.innerText = "";
  1662. _prepareCallback(text, "messageReceived", 200, rq.transport);
  1663. rq.lastIndex = 0;
  1664. }
  1665. if (cdoc.readyState === "complete") {
  1666. _prepareCallback("", "closed", 200, rq.transport);
  1667. _prepareCallback("", "re-opening", 200, rq.transport);
  1668. rq.id = setTimeout(function() {
  1669. _ieStreaming(rq);
  1670. }, rq.reconnectInterval);
  1671. return false;
  1672. }
  1673. }, null);
  1674. return false;
  1675. } catch (err) {
  1676. if (_requestCount++ < rq.maxReconnectOnClose) {
  1677. rq.id = setTimeout(function() {
  1678. _ieStreaming(rq);
  1679. }, rq.reconnectInterval);
  1680. } else {
  1681. _onError();
  1682. }
  1683. doc.execCommand("Stop");
  1684. doc.close();
  1685. return false;
  1686. }
  1687. });
  1688. },
  1689. close: function() {
  1690. if (stop) {
  1691. stop();
  1692. }
  1693. doc.execCommand("Stop");
  1694. _prepareCallback("", "closed", 200, rq.transport);
  1695. }
  1696. };
  1697. }
  1698. /*
  1699. * Send message. <br>
  1700. * Will be automatically dispatch to other connected.
  1701. *
  1702. * @param {Object,string} Message to send.
  1703. * @private
  1704. */
  1705. function _push(message) {
  1706. if (_response.status == 408) {
  1707. _pushOnClose(message);
  1708. } else if (_localStorageService != null) {
  1709. _pushLocal(message);
  1710. } else if (_activeRequest != null || _sse != null) {
  1711. _pushAjaxMessage(message);
  1712. } else if (_ieStream != null) {
  1713. _pushIE(message);
  1714. } else if (_jqxhr != null) {
  1715. _pushJsonp(message);
  1716. } else if (_websocket != null) {
  1717. _pushWebSocket(message);
  1718. }
  1719. }
  1720. function _pushOnClose(message) {
  1721. var rq = _getPushRequest(message);
  1722. rq.transport = "ajax";
  1723. rq.method = "GET";
  1724. rq.async = false;
  1725. rq.reconnect = false;
  1726. _executeRequest(rq);
  1727. }
  1728. function _pushLocal(message) {
  1729. _localStorageService.send(message);
  1730. }
  1731. function _intraPush(message) {
  1732. // IE 9 will crash if not.
  1733. if (message.length == 0) return;
  1734. try {
  1735. if (_localStorageService) {
  1736. _localStorageService.localSend(message);
  1737. } else if (_storageService) {
  1738. _storageService.signal("localMessage", jQuery.stringifyJSON({id: guid , event: message}));
  1739. }
  1740. } catch (err) {
  1741. jQuery.atmosphere.error(err);
  1742. }
  1743. }
  1744. /**
  1745. * Send a message using currently opened ajax request (using
  1746. * http-streaming or long-polling). <br>
  1747. *
  1748. * @param {string, Object} Message to send. This is an object, string
  1749. * message is saved in data member.
  1750. * @private
  1751. */
  1752. function _pushAjaxMessage(message) {
  1753. var rq = _getPushRequest(message);
  1754. _executeRequest(rq);
  1755. }
  1756. /**
  1757. * Send a message using currently opened ie streaming (using
  1758. * http-streaming or long-polling). <br>
  1759. *
  1760. * @param {string, Object} Message to send. This is an object, string
  1761. * message is saved in data member.
  1762. * @private
  1763. */
  1764. function _pushIE(message) {
  1765. if (_request.enableXDR && jQuery.atmosphere.checkCORSSupport()) {
  1766. var rq = _getPushRequest(message);
  1767. // Do not reconnect since we are pushing.
  1768. rq.reconnect = false;
  1769. _jsonp(rq);
  1770. } else {
  1771. _pushAjaxMessage(message);
  1772. }
  1773. }
  1774. /**
  1775. * Send a message using jsonp transport. <br>
  1776. *
  1777. * @param {string, Object} Message to send. This is an object, string
  1778. * message is saved in data member.
  1779. * @private
  1780. */
  1781. function _pushJsonp(message) {
  1782. _pushAjaxMessage(message);
  1783. }
  1784. function _getStringMessage(message) {
  1785. var msg = message;
  1786. if (typeof(msg) == 'object') {
  1787. msg = message.data;
  1788. }
  1789. return msg;
  1790. }
  1791. /**
  1792. * Build request use to push message using method 'POST' <br>.
  1793. * Transport is defined as 'polling' and 'suspend' is set to false.
  1794. *
  1795. * @return {Object} Request object use to push message.
  1796. * @private
  1797. */
  1798. function _getPushRequest(message) {
  1799. var msg = _getStringMessage(message);
  1800. var rq = {
  1801. connected: false,
  1802. timeout: 60000,
  1803. method: 'POST',
  1804. url: _request.url,
  1805. contentType : _request.contentType,
  1806. headers: {},
  1807. reconnect : true,
  1808. callback: null,
  1809. data : msg,
  1810. suspend : false,
  1811. maxRequest : -1,
  1812. logLevel : 'info',
  1813. requestCount : 0,
  1814. withCredentials : _request.withCredentials,
  1815. transport: 'polling',
  1816. attachHeadersAsQueryString: true,
  1817. enableXDR: _request.enableXDR,
  1818. uuid : _request.uuid,
  1819. enableProtocol : false,
  1820. maxReconnectOnClose : _request.maxReconnectOnClose
  1821. };
  1822. if (typeof(message) == 'object') {
  1823. rq = jQuery.extend(rq, message);
  1824. }
  1825. return rq;
  1826. }
  1827. /**
  1828. * Send a message using currently opened websocket. <br>
  1829. *
  1830. */
  1831. function _pushWebSocket(message) {
  1832. var msg = _getStringMessage(message);
  1833. var data;
  1834. try {
  1835. if (_request.webSocketUrl != null) {
  1836. data = _request.webSocketPathDelimiter
  1837. + _request.webSocketUrl
  1838. + _request.webSocketPathDelimiter
  1839. + msg;
  1840. } else {
  1841. data = msg;
  1842. }
  1843. _websocket.send(data);
  1844. } catch (e) {
  1845. _websocket.onclose = function(message) {
  1846. };
  1847. _clearState();
  1848. _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending " + data);
  1849. _pushAjaxMessage(message);
  1850. }
  1851. }
  1852. function _localMessage(message) {
  1853. var m = jQuery.parseJSON(message);
  1854. if (m.id != guid) {
  1855. if (typeof(_request.onLocalMessage) != 'undefined') {
  1856. _request.onLocalMessage(m.event);
  1857. } else if (typeof(jQuery.atmosphere.onLocalMessage) != 'undefined') {
  1858. jQuery.atmosphere.onLocalMessage(m.event);
  1859. }
  1860. }
  1861. }
  1862. function _prepareCallback(messageBody, state, errorCode, transport) {
  1863. _response.responseBody = messageBody;
  1864. if (state == "messageReceived") {
  1865. if (_trackMessageSize(messageBody, _request, _response)) {
  1866. return;
  1867. }
  1868. }
  1869. _response.transport = transport;
  1870. _response.status = errorCode;
  1871. _response.state = state;
  1872. _invokeCallback();
  1873. }
  1874. function _readHeaders(xdr, request) {
  1875. if (!request.readResponsesHeaders && !request.enableProtocol) {
  1876. request.lastTimestamp = jQuery.now();
  1877. request.uuid = jQuery.atmosphere.guid();
  1878. return;
  1879. }
  1880. try {
  1881. var tempDate = xdr.getResponseHeader('X-Cache-Date');
  1882. if (tempDate && tempDate != null && tempDate.length > 0 ) {
  1883. request.lastTimestamp = tempDate.split(" ").pop();
  1884. }
  1885. var tempUUID = xdr.getResponseHeader('X-Atmosphere-tracking-id');
  1886. if (tempUUID && tempUUID != null) {
  1887. request.uuid = tempUUID.split(" ").pop();
  1888. }
  1889. // HOTFIX for firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=608735
  1890. if (request.headers) {
  1891. jQuery.each(_request.headers, function (name) {
  1892. var v = xdr.getResponseHeader(name);
  1893. if (v) {
  1894. _response.headers[name] = v;
  1895. }
  1896. });
  1897. }
  1898. } catch (e) {
  1899. }
  1900. }
  1901. function _invokeFunction(response) {
  1902. _f(response, _request);
  1903. // Global
  1904. _f(response, jQuery.atmosphere);
  1905. }
  1906. function _f(response, f) {
  1907. switch (response.state) {
  1908. case "messageReceived" :
  1909. _requestCount = 0;
  1910. if (typeof(f.onMessage) != 'undefined') f.onMessage(response);
  1911. break;
  1912. case "error" :
  1913. if (typeof(f.onError) != 'undefined') f.onError(response);
  1914. break;
  1915. case "opening" :
  1916. if (typeof(f.onOpen) != 'undefined') f.onOpen(response);
  1917. break;
  1918. case "messagePublished" :
  1919. if (typeof(f.onMessagePublished) != 'undefined') f.onMessagePublished(response);
  1920. break;
  1921. case "re-opening" :
  1922. if (typeof(f.onReconnect) != 'undefined') f.onReconnect(_request, response);
  1923. break;
  1924. case "unsubscribe" :
  1925. case "closed" :
  1926. var closed = typeof(_request.closed) != 'undefined' ? _request.closed : false;
  1927. if (typeof(f.onClose) != 'undefined' && !closed) f.onClose(response);
  1928. _request.closed = true;
  1929. break;
  1930. }
  1931. }
  1932. /**
  1933. * Invoke request callbacks.
  1934. *
  1935. * @private
  1936. */
  1937. function _invokeCallback() {
  1938. var call = function (index, func) {
  1939. func(_response);
  1940. };
  1941. if (_localStorageService == null && _localSocketF != null) {
  1942. _localSocketF(_response.responseBody);
  1943. }
  1944. _request.reconnect = _request.mrequest;
  1945. var messages = (typeof(_response.responseBody) == 'string' && _request.trackMessageLength) ?
  1946. _response.responseBody.split(_request.messageDelimiter) : new Array(_response.responseBody);
  1947. for (var i = 0; i < messages.length; i++) {
  1948. if (messages.length > 1 && messages[i].length == 0) {
  1949. continue;
  1950. }
  1951. _response.responseBody = jQuery.trim(messages[i]);
  1952. // Ugly see issue 400.
  1953. if (_response.responseBody.length == 0 && _response.transport == 'streaming' && _response.state == "messageReceived") {
  1954. var ua = navigator.userAgent.toLowerCase();
  1955. var isAndroid = ua.indexOf("android") > -1;
  1956. if (isAndroid) {
  1957. continue;
  1958. }
  1959. }
  1960. _invokeFunction(_response);
  1961. // Invoke global callbacks
  1962. if (jQuery.atmosphere.callbacks.length > 0) {
  1963. if (_request.logLevel == 'debug') {
  1964. jQuery.atmosphere.debug("Invoking " + jQuery.atmosphere.callbacks.length + " global callbacks: " + _response.state);
  1965. }
  1966. try {
  1967. jQuery.each(jQuery.atmosphere.callbacks, call);
  1968. } catch (e) {
  1969. jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]);
  1970. }
  1971. }
  1972. // Invoke request callback
  1973. if (typeof(_request.callback) == 'function') {
  1974. if (_request.logLevel == 'debug') {
  1975. jQuery.atmosphere.debug("Invoking request callbacks");
  1976. }
  1977. try {
  1978. _request.callback(_response);
  1979. } catch (e) {
  1980. jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]);
  1981. }
  1982. }
  1983. }
  1984. }
  1985. /**
  1986. * Close request.
  1987. *
  1988. * @private
  1989. */
  1990. function _close() {
  1991. _abordingConnection = true;
  1992. _request.reconnect = false;
  1993. _response.request = _request;
  1994. _response.state = 'unsubscribe';
  1995. _response.responseBody = "";
  1996. _response.status = 408;
  1997. _invokeCallback();
  1998. _clearState();
  1999. }
  2000. function _clearState() {
  2001. if (_ieStream != null) {
  2002. _ieStream.close();
  2003. _ieStream = null;
  2004. }
  2005. if (_jqxhr != null) {
  2006. _jqxhr.abort();
  2007. _jqxhr = null;
  2008. }
  2009. if (_activeRequest != null) {
  2010. _activeRequest.abort();
  2011. _activeRequest = null;
  2012. }
  2013. if (_websocket != null) {
  2014. _websocket.close();
  2015. _websocket = null;
  2016. }
  2017. if (_sse != null) {
  2018. _sse.close();
  2019. _sse = null;
  2020. }
  2021. _clearStorage();
  2022. }
  2023. function _clearStorage() {
  2024. // Stop sharing a connection
  2025. if (_storageService != null) {
  2026. // Clears trace timer
  2027. clearInterval(_traceTimer);
  2028. // Removes the trace
  2029. document.cookie = encodeURIComponent("atmosphere-" + _request.url) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
  2030. // The heir is the parent unless unloading
  2031. _storageService.signal("close", {reason: "", heir: !_abordingConnection ? guid : (_storageService.get("children") || [])[0]});
  2032. _storageService.close();
  2033. }
  2034. if (_localStorageService != null) {
  2035. _localStorageService.close();
  2036. }
  2037. }
  2038. this.subscribe = function(options) {
  2039. _subscribe(options);
  2040. _execute();
  2041. };
  2042. this.execute = function() {
  2043. _execute();
  2044. };
  2045. this.invokeCallback = function() {
  2046. _invokeCallback();
  2047. };
  2048. this.close = function() {
  2049. _close();
  2050. };
  2051. this.getUrl = function() {
  2052. return _request.url;
  2053. };
  2054. this.getUUID = function() {
  2055. return _request.uuid;
  2056. };
  2057. this.push = function(message) {
  2058. _push(message);
  2059. };
  2060. this.pushLocal = function(message) {
  2061. _intraPush(message);
  2062. };
  2063. this.enableProtocol = function(message) {
  2064. return _request.enableProtocol;
  2065. };
  2066. this.response = _response;
  2067. },
  2068. subscribe: function(url, callback, request) {
  2069. if (typeof(callback) == 'function') {
  2070. jQuery.atmosphere.addCallback(callback);
  2071. }
  2072. if (typeof(url) != "string") {
  2073. request = url;
  2074. } else {
  2075. request.url = url;
  2076. }
  2077. var rq = new jQuery.atmosphere.AtmosphereRequest(request);
  2078. rq.execute();
  2079. jQuery.atmosphere.requests[jQuery.atmosphere.requests.length] = rq;
  2080. return rq;
  2081. },
  2082. addCallback: function(func) {
  2083. if (jQuery.inArray(func, jQuery.atmosphere.callbacks) == -1) {
  2084. jQuery.atmosphere.callbacks.push(func);
  2085. }
  2086. },
  2087. removeCallback: function(func) {
  2088. var index = jQuery.inArray(func, jQuery.atmosphere.callbacks);
  2089. if (index != -1) {
  2090. jQuery.atmosphere.callbacks.splice(index, 1);
  2091. }
  2092. },
  2093. unsubscribe : function() {
  2094. if (jQuery.atmosphere.requests.length > 0) {
  2095. var requestsClone = [].concat(jQuery.atmosphere.requests);
  2096. for (var i = 0; i < requestsClone.length; i++) {
  2097. var rq = requestsClone[i];
  2098. rq.close();
  2099. if (rq.enableProtocol()) {
  2100. jQuery.ajax({url: this._closeUrl(rq), async:false});
  2101. }
  2102. clearTimeout(rq.response.request.id);
  2103. }
  2104. }
  2105. jQuery.atmosphere.requests = [];
  2106. jQuery.atmosphere.callbacks = [];
  2107. },
  2108. _closeUrl : function(rq) {
  2109. var query = "X-Atmosphere-Transport=close&X-Atmosphere-tracking-id=" + rq.getUUID();
  2110. var url = rq.getUrl().replace(/([?&])_=[^&]*/, query);
  2111. return url + (url === rq.getUrl() ? (/\?/.test(rq.getUrl()) ? "&" : "?") + query : "");
  2112. },
  2113. unsubscribeUrl: function(url) {
  2114. var idx = -1;
  2115. if (jQuery.atmosphere.requests.length > 0) {
  2116. for (var i = 0; i < jQuery.atmosphere.requests.length; i++) {
  2117. var rq = jQuery.atmosphere.requests[i];
  2118. // Suppose you can subscribe once to an url
  2119. if (rq.getUrl() == url) {
  2120. rq.close();
  2121. if (rq.enableProtocol()) {
  2122. jQuery.ajax({url :this._closeUrl(rq), async:false});
  2123. }
  2124. clearTimeout(rq.response.request.id);
  2125. idx = i;
  2126. break;
  2127. }
  2128. }
  2129. }
  2130. if (idx >= 0) {
  2131. jQuery.atmosphere.requests.splice(idx, 1);
  2132. }
  2133. },
  2134. publish: function(request) {
  2135. if (typeof(request.callback) == 'function') {
  2136. jQuery.atmosphere.addCallback(callback);
  2137. }
  2138. request.transport = "polling";
  2139. var rq = new jQuery.atmosphere.AtmosphereRequest(request);
  2140. jQuery.atmosphere.requests[jQuery.atmosphere.requests.length] = rq;
  2141. return rq;
  2142. },
  2143. checkCORSSupport : function() {
  2144. if (jQuery.browser.msie && !window.XDomainRequest) {
  2145. return true;
  2146. } else if (jQuery.browser.opera && jQuery.browser.version < 12.0) {
  2147. return true;
  2148. }
  2149. // Force Android to use CORS as some version like 2.2.3 fail otherwise
  2150. var ua = navigator.userAgent.toLowerCase();
  2151. var isAndroid = ua.indexOf("android") > -1;
  2152. if (isAndroid) {
  2153. return true;
  2154. }
  2155. return false;
  2156. },
  2157. S4 : function() {
  2158. return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  2159. },
  2160. guid : function() {
  2161. return (jQuery.atmosphere.S4() + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + jQuery.atmosphere.S4() + jQuery.atmosphere.S4());
  2162. },
  2163. // From jQuery-Stream
  2164. prepareURL: function(url) {
  2165. // Attaches a time stamp to prevent caching
  2166. var ts = jQuery.now();
  2167. var ret = url.replace(/([?&])_=[^&]*/, "$1_=" + ts);
  2168. return ret + (ret === url ? (/\?/.test(url) ? "&" : "?") + "_=" + ts : "");
  2169. },
  2170. // From jQuery-Stream
  2171. param : function(data) {
  2172. return jQuery.param(data, jQuery.ajaxSettings.traditional);
  2173. },
  2174. supportStorage : function() {
  2175. var storage = window.localStorage;
  2176. if (storage) {
  2177. try {
  2178. storage.setItem("t", "t");
  2179. storage.removeItem("t");
  2180. // The storage event of Internet Explorer and Firefox 3 works strangely
  2181. return window.StorageEvent && !jQuery.browser.msie && !(jQuery.browser.mozilla && jQuery.browser.version.split(".")[0] === "1");
  2182. } catch (e) {
  2183. }
  2184. }
  2185. return false;
  2186. },
  2187. iterate : function (fn, interval) {
  2188. var timeoutId;
  2189. // Though the interval is 0 for real-time application, there is a delay between setTimeout calls
  2190. // For detail, see https://developer.mozilla.org/en/window.setTimeout#Minimum_delay_and_timeout_nesting
  2191. interval = interval || 0;
  2192. (function loop() {
  2193. timeoutId = setTimeout(function() {
  2194. if (fn() === false) {
  2195. return;
  2196. }
  2197. loop();
  2198. }, interval);
  2199. })();
  2200. return function() {
  2201. clearTimeout(timeoutId);
  2202. };
  2203. },
  2204. log: function (level, args) {
  2205. if (window.console) {
  2206. var logger = window.console[level];
  2207. if (typeof logger == 'function') {
  2208. logger.apply(window.console, args);
  2209. }
  2210. }
  2211. },
  2212. warn: function() {
  2213. jQuery.atmosphere.log('warn', arguments);
  2214. },
  2215. info :function() {
  2216. jQuery.atmosphere.log('info', arguments);
  2217. },
  2218. debug: function() {
  2219. jQuery.atmosphere.log('debug', arguments);
  2220. },
  2221. error: function() {
  2222. jQuery.atmosphere.log('error', arguments);
  2223. }
  2224. };
  2225. }();
  2226. // http://stackoverflow.com/questions/9645803/whats-the-replacement-for-browser
  2227. // Limit scope pollution from any deprecated API
  2228. (function () {
  2229. var matched, browser;
  2230. // Use of jQuery.browser is frowned upon.
  2231. // More details: http://api.jquery.com/jQuery.browser
  2232. // jQuery.uaMatch maintained for back-compat
  2233. jQuery.uaMatch = function (ua) {
  2234. ua = ua.toLowerCase();
  2235. var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
  2236. /(webkit)[ \/]([\w.]+)/.exec(ua) ||
  2237. /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
  2238. /(msie) ([\w.]+)/.exec(ua) ||
  2239. ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
  2240. [];
  2241. return {
  2242. browser: match[ 1 ] || "",
  2243. version: match[ 2 ] || "0"
  2244. };
  2245. };
  2246. matched = jQuery.uaMatch(navigator.userAgent);
  2247. browser = {};
  2248. if (matched.browser) {
  2249. browser[ matched.browser ] = true;
  2250. browser.version = matched.version;
  2251. }
  2252. // Chrome is Webkit, but Webkit is also Safari.
  2253. if (browser.chrome) {
  2254. browser.webkit = true;
  2255. } else if (browser.webkit) {
  2256. browser.safari = true;
  2257. }
  2258. jQuery.browser = browser;
  2259. jQuery.sub = function () {
  2260. function jQuerySub(selector, context) {
  2261. return new jQuerySub.fn.init(selector, context);
  2262. }
  2263. jQuery.extend(true, jQuerySub, this);
  2264. jQuerySub.superclass = this;
  2265. jQuerySub.fn = jQuerySub.prototype = this();
  2266. jQuerySub.fn.constructor = jQuerySub;
  2267. jQuerySub.sub = this.sub;
  2268. jQuerySub.fn.init = function init(selector, context) {
  2269. if (context && context instanceof jQuery && !(context instanceof jQuerySub)) {
  2270. context = jQuerySub(context);
  2271. }
  2272. return jQuery.fn.init.call(this, selector, context, rootjQuerySub);
  2273. };
  2274. jQuerySub.fn.init.prototype = jQuerySub.fn;
  2275. var rootjQuerySub = jQuerySub(document);
  2276. return jQuerySub;
  2277. };
  2278. })();
  2279. /*
  2280. * jQuery stringifyJSON
  2281. * http://github.com/flowersinthesand/jquery-stringifyJSON
  2282. *
  2283. * Copyright 2011, Donghwan Kim
  2284. * Licensed under the Apache License, Version 2.0
  2285. * http://www.apache.org/licenses/LICENSE-2.0
  2286. */
  2287. // This plugin is heavily based on Douglas Crockford's reference implementation
  2288. (function(jQuery) {
  2289. var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, meta = {
  2290. '\b' : '\\b',
  2291. '\t' : '\\t',
  2292. '\n' : '\\n',
  2293. '\f' : '\\f',
  2294. '\r' : '\\r',
  2295. '"' : '\\"',
  2296. '\\' : '\\\\'
  2297. };
  2298. function quote(string) {
  2299. return '"' + string.replace(escapable, function(a) {
  2300. var c = meta[a];
  2301. return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
  2302. }) + '"';
  2303. }
  2304. function f(n) {
  2305. return n < 10 ? "0" + n : n;
  2306. }
  2307. function str(key, holder) {
  2308. var i, v, len, partial, value = holder[key], type = typeof value;
  2309. if (value && typeof value === "object" && typeof value.toJSON === "function") {
  2310. value = value.toJSON(key);
  2311. type = typeof value;
  2312. }
  2313. switch (type) {
  2314. case "string":
  2315. return quote(value);
  2316. case "number":
  2317. return isFinite(value) ? String(value) : "null";
  2318. case "boolean":
  2319. return String(value);
  2320. case "object":
  2321. if (!value) {
  2322. return "null";
  2323. }
  2324. switch (Object.prototype.toString.call(value)) {
  2325. case "[object Date]":
  2326. return isFinite(value.valueOf()) ? '"' + value.getUTCFullYear() + "-" + f(value.getUTCMonth() + 1) + "-" + f(value.getUTCDate()) + "T" +
  2327. f(value.getUTCHours()) + ":" + f(value.getUTCMinutes()) + ":" + f(value.getUTCSeconds()) + "Z" + '"' : "null";
  2328. case "[object Array]":
  2329. len = value.length;
  2330. partial = [];
  2331. for (i = 0; i < len; i++) {
  2332. partial.push(str(i, value) || "null");
  2333. }
  2334. return "[" + partial.join(",") + "]";
  2335. default:
  2336. partial = [];
  2337. for (i in value) {
  2338. if (Object.prototype.hasOwnProperty.call(value, i)) {
  2339. v = str(i, value);
  2340. if (v) {
  2341. partial.push(quote(i) + ":" + v);
  2342. }
  2343. }
  2344. }
  2345. return "{" + partial.join(",") + "}";
  2346. }
  2347. }
  2348. }
  2349. jQuery.stringifyJSON = function(value) {
  2350. if (window.JSON && window.JSON.stringify) {
  2351. return window.JSON.stringify(value);
  2352. }
  2353. return str("", {"": value});
  2354. };
  2355. }(jQuery));