binnacle 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (449) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.lock +138 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.asc +264 -0
  9. data/Rakefile +48 -0
  10. data/bin/binnacle +5 -0
  11. data/binnacle.gemspec +49 -0
  12. data/lib/binnacle.rb +112 -0
  13. data/lib/binnacle/client.rb +115 -0
  14. data/lib/binnacle/commands/commands.rb +20 -0
  15. data/lib/binnacle/commands/help.rb +21 -0
  16. data/lib/binnacle/commands/tail.rb +187 -0
  17. data/lib/binnacle/configuration.rb +278 -0
  18. data/lib/binnacle/connection.rb +66 -0
  19. data/lib/binnacle/errors.rb +4 -0
  20. data/lib/binnacle/http_logging/adapters/ethon.rb +44 -0
  21. data/lib/binnacle/http_logging/adapters/excon.rb +62 -0
  22. data/lib/binnacle/http_logging/adapters/http.rb +63 -0
  23. data/lib/binnacle/http_logging/adapters/httpclient.rb +93 -0
  24. data/lib/binnacle/http_logging/adapters/net_http.rb +54 -0
  25. data/lib/binnacle/http_logging/adapters/patron.rb +24 -0
  26. data/lib/binnacle/http_logging/adapters/typhoeus.rb +20 -0
  27. data/lib/binnacle/http_logging/http_logger.rb +83 -0
  28. data/lib/binnacle/logging/formatter.rb +62 -0
  29. data/lib/binnacle/logging/logging.rb +36 -0
  30. data/lib/binnacle/logging/request_log_subscriber.rb +120 -0
  31. data/lib/binnacle/resource.rb +54 -0
  32. data/lib/binnacle/resources/event.rb +100 -0
  33. data/lib/binnacle/trap/backtrace.rb +130 -0
  34. data/lib/binnacle/trap/exception_event.rb +176 -0
  35. data/lib/binnacle/trap/middleware.rb +45 -0
  36. data/lib/binnacle/trap/railtie.rb +28 -0
  37. data/lib/binnacle/version.rb +3 -0
  38. data/lib/generators/binnacle/binnacle_generator.rb +78 -0
  39. data/lib/generators/binnacle/templates/firebase-messaging-sw.js.erb +35 -0
  40. data/spec/adapters/ethon_adapter.rb +19 -0
  41. data/spec/adapters/excon_adapter.rb +10 -0
  42. data/spec/adapters/faraday_adapter.rb +46 -0
  43. data/spec/adapters/http_adapter.rb +14 -0
  44. data/spec/adapters/http_base_adapter.rb +37 -0
  45. data/spec/adapters/httparty_adapter.rb +10 -0
  46. data/spec/adapters/httpclient_adapter.rb +21 -0
  47. data/spec/adapters/net_http_adapter.rb +14 -0
  48. data/spec/adapters/open_uri_adapter.rb +17 -0
  49. data/spec/adapters/patron_adapter.rb +29 -0
  50. data/spec/adapters/typhoeus_adapter.rb +25 -0
  51. data/spec/backtrace_spec.rb +197 -0
  52. data/spec/binnacle_spec.rb +33 -0
  53. data/spec/client_spec.rb +128 -0
  54. data/spec/commands_spec.rb +91 -0
  55. data/spec/configuration_spec.rb +139 -0
  56. data/spec/connection_spec.rb +34 -0
  57. data/spec/exception_event_spec.rb +72 -0
  58. data/spec/http_logger_spec.rb +77 -0
  59. data/spec/logger_spec.rb +60 -0
  60. data/spec/spec_helper.rb +117 -0
  61. data/spec/support/index.html +8 -0
  62. data/spec/support/index.html.gz +0 -0
  63. data/spec/support/test.bin +0 -0
  64. data/spec/support/test_server.rb +31 -0
  65. data/spec/support/utf8-invalid.html +0 -0
  66. data/spec/support/utf8.html +8 -0
  67. data/spec/vcr/binnacle/configure_can_be_configured_via_a_hash_of_options.yml +42 -0
  68. data/spec/vcr/binnacle/configure_creates_a_module_level_instance_of_a_binnacle_client.yml +42 -0
  69. data/spec/vcr/binnacle_client/events_invokes_the_events_api.yml +124 -0
  70. data/spec/vcr/binnacle_client/events_returns_a_collection_of_event_objects.yml +124 -0
  71. data/spec/vcr/binnacle_client/recents_invokes_the_events_api_recents.yml +83 -0
  72. data/spec/vcr/binnacle_client/recents_returns_a_collection_of_event_objects.yml +83 -0
  73. data/spec/vcr/binnacle_client/report_exception_invokes_the_events_api_signal.yml +121 -0
  74. data/spec/vcr/binnacle_client/signal_asynch_invokes_the_events_api_signal.yml +74 -0
  75. data/spec/vcr/binnacle_client/signal_invokes_the_events_api_signal.yml +74 -0
  76. data/spec/vcr/binnacle_client_ready_/returns_true_if_a_connection_has_been_successfully_established.yml +42 -0
  77. data/spec/vcr/binnacle_command/tail_command_with_n_flag_returns_recent_events.yml +81 -0
  78. data/spec/vcr/binnacle_connection/initialize_fails_with_incorrect_credentials.yml +40 -0
  79. data/spec/vcr/binnacle_connection/initialize_retrieves_available_endpoints_upon_successful_connection.yml +42 -0
  80. data/spec/vcr/binnacle_http_logger/_htt_party_adapter_should_log__ge_t_requests.yml +122 -0
  81. data/spec/vcr/binnacle_http_logger/_http_adapter_should_log__ge_t_requests.yml +165 -0
  82. data/spec/vcr/binnacle_http_logger/_http_client_adapter_should_log__ge_t_requests.yml +274 -0
  83. data/spec/vcr/binnacle_http_logger/ethon_adapter_should_log__ge_t_requests.yml +87 -0
  84. data/spec/vcr/binnacle_http_logger/excon_adapter_should_log__ge_t_requests.yml +204 -0
  85. data/spec/vcr/binnacle_http_logger/faraday_adapter_should_log__ge_t_requests.yml +200 -0
  86. data/spec/vcr/binnacle_http_logger/net_http_adapter_should_log__ge_t_requests.yml +827 -0
  87. data/spec/vcr/binnacle_http_logger/open_uri_adapter_should_log__ge_t_requests.yml +751 -0
  88. data/spec/vcr/binnacle_http_logger/patron_adapter_should_log__ge_t_requests.yml +124 -0
  89. data/spec/vcr/binnacle_http_logger/typhoeus_adapter_should_log__ge_t_requests.yml +124 -0
  90. data/spec/vcr/binnacle_logging/logging_allows_passing_other_parameters_using_a_hash.yml +119 -0
  91. data/spec/vcr/binnacle_logging/logging_invokes_the_events_api_signal.yml +75 -0
  92. data/spec/vcr/binnacle_logging/logging_respects_the_logger_severity.yml +81 -0
  93. data/vendor/assets/javascripts/atmosphere/atmosphere.js +3487 -0
  94. data/vendor/assets/javascripts/base64/base64.js +61 -0
  95. data/vendor/assets/javascripts/binnacle.js +5 -0
  96. data/vendor/assets/javascripts/binnacle/binnacle.js +9090 -0
  97. data/vendor/assets/javascripts/firebase/firebase-app-externs.js +261 -0
  98. data/vendor/assets/javascripts/firebase/firebase-app.js +35 -0
  99. data/vendor/assets/javascripts/firebase/firebase-auth-externs.js +1300 -0
  100. data/vendor/assets/javascripts/firebase/firebase-auth.js +241 -0
  101. data/vendor/assets/javascripts/firebase/firebase-database-externs.js +1700 -0
  102. data/vendor/assets/javascripts/firebase/firebase-database.js +260 -0
  103. data/vendor/assets/javascripts/firebase/firebase-messaging-externs.js +164 -0
  104. data/vendor/assets/javascripts/firebase/firebase-messaging.js +35 -0
  105. data/vendor/assets/javascripts/firebase/firebase-storage-externs.js +663 -0
  106. data/vendor/assets/javascripts/firebase/firebase-storage.js +52 -0
  107. data/vendor/assets/javascripts/firebase/firebase.js +611 -0
  108. data/vendor/assets/javascripts/moment/locale/af.js +73 -0
  109. data/vendor/assets/javascripts/moment/locale/ar-dz.js +59 -0
  110. data/vendor/assets/javascripts/moment/locale/ar-kw.js +59 -0
  111. data/vendor/assets/javascripts/moment/locale/ar-ly.js +126 -0
  112. data/vendor/assets/javascripts/moment/locale/ar-ma.js +60 -0
  113. data/vendor/assets/javascripts/moment/locale/ar-sa.js +105 -0
  114. data/vendor/assets/javascripts/moment/locale/ar-tn.js +59 -0
  115. data/vendor/assets/javascripts/moment/locale/ar.js +142 -0
  116. data/vendor/assets/javascripts/moment/locale/az.js +105 -0
  117. data/vendor/assets/javascripts/moment/locale/be.js +134 -0
  118. data/vendor/assets/javascripts/moment/locale/bg.js +90 -0
  119. data/vendor/assets/javascripts/moment/locale/bn.js +119 -0
  120. data/vendor/assets/javascripts/moment/locale/bo.js +119 -0
  121. data/vendor/assets/javascripts/moment/locale/br.js +108 -0
  122. data/vendor/assets/javascripts/moment/locale/bs.js +143 -0
  123. data/vendor/assets/javascripts/moment/locale/ca.js +88 -0
  124. data/vendor/assets/javascripts/moment/locale/cs.js +172 -0
  125. data/vendor/assets/javascripts/moment/locale/cv.js +63 -0
  126. data/vendor/assets/javascripts/moment/locale/cy.js +81 -0
  127. data/vendor/assets/javascripts/moment/locale/da.js +60 -0
  128. data/vendor/assets/javascripts/moment/locale/de-at.js +79 -0
  129. data/vendor/assets/javascripts/moment/locale/de-ch.js +78 -0
  130. data/vendor/assets/javascripts/moment/locale/de.js +78 -0
  131. data/vendor/assets/javascripts/moment/locale/dv.js +100 -0
  132. data/vendor/assets/javascripts/moment/locale/el.js +100 -0
  133. data/vendor/assets/javascripts/moment/locale/en-au.js +67 -0
  134. data/vendor/assets/javascripts/moment/locale/en-ca.js +63 -0
  135. data/vendor/assets/javascripts/moment/locale/en-gb.js +67 -0
  136. data/vendor/assets/javascripts/moment/locale/en-ie.js +67 -0
  137. data/vendor/assets/javascripts/moment/locale/en-nz.js +67 -0
  138. data/vendor/assets/javascripts/moment/locale/eo.js +73 -0
  139. data/vendor/assets/javascripts/moment/locale/es-do.js +82 -0
  140. data/vendor/assets/javascripts/moment/locale/es.js +83 -0
  141. data/vendor/assets/javascripts/moment/locale/et.js +80 -0
  142. data/vendor/assets/javascripts/moment/locale/eu.js +66 -0
  143. data/vendor/assets/javascripts/moment/locale/fa.js +107 -0
  144. data/vendor/assets/javascripts/moment/locale/fi.js +107 -0
  145. data/vendor/assets/javascripts/moment/locale/fo.js +60 -0
  146. data/vendor/assets/javascripts/moment/locale/fr-ca.js +74 -0
  147. data/vendor/assets/javascripts/moment/locale/fr-ch.js +78 -0
  148. data/vendor/assets/javascripts/moment/locale/fr.js +83 -0
  149. data/vendor/assets/javascripts/moment/locale/fy.js +75 -0
  150. data/vendor/assets/javascripts/moment/locale/gd.js +76 -0
  151. data/vendor/assets/javascripts/moment/locale/gl.js +77 -0
  152. data/vendor/assets/javascripts/moment/locale/gom-latn.js +122 -0
  153. data/vendor/assets/javascripts/moment/locale/he.js +99 -0
  154. data/vendor/assets/javascripts/moment/locale/hi.js +124 -0
  155. data/vendor/assets/javascripts/moment/locale/hr.js +145 -0
  156. data/vendor/assets/javascripts/moment/locale/hu.js +109 -0
  157. data/vendor/assets/javascripts/moment/locale/hy-am.js +95 -0
  158. data/vendor/assets/javascripts/moment/locale/id.js +83 -0
  159. data/vendor/assets/javascripts/moment/locale/is.js +127 -0
  160. data/vendor/assets/javascripts/moment/locale/it.js +70 -0
  161. data/vendor/assets/javascripts/moment/locale/ja.js +80 -0
  162. data/vendor/assets/javascripts/moment/locale/jv.js +83 -0
  163. data/vendor/assets/javascripts/moment/locale/ka.js +89 -0
  164. data/vendor/assets/javascripts/moment/locale/kk.js +87 -0
  165. data/vendor/assets/javascripts/moment/locale/km.js +58 -0
  166. data/vendor/assets/javascripts/moment/locale/kn.js +126 -0
  167. data/vendor/assets/javascripts/moment/locale/ko.js +69 -0
  168. data/vendor/assets/javascripts/moment/locale/ky.js +88 -0
  169. data/vendor/assets/javascripts/moment/locale/lb.js +137 -0
  170. data/vendor/assets/javascripts/moment/locale/lo.js +70 -0
  171. data/vendor/assets/javascripts/moment/locale/lt.js +117 -0
  172. data/vendor/assets/javascripts/moment/locale/lv.js +97 -0
  173. data/vendor/assets/javascripts/moment/locale/me.js +111 -0
  174. data/vendor/assets/javascripts/moment/locale/mi.js +64 -0
  175. data/vendor/assets/javascripts/moment/locale/mk.js +90 -0
  176. data/vendor/assets/javascripts/moment/locale/ml.js +81 -0
  177. data/vendor/assets/javascripts/moment/locale/mr.js +159 -0
  178. data/vendor/assets/javascripts/moment/locale/ms-my.js +83 -0
  179. data/vendor/assets/javascripts/moment/locale/ms.js +82 -0
  180. data/vendor/assets/javascripts/moment/locale/my.js +96 -0
  181. data/vendor/assets/javascripts/moment/locale/nb.js +63 -0
  182. data/vendor/assets/javascripts/moment/locale/ne.js +123 -0
  183. data/vendor/assets/javascripts/moment/locale/nl-be.js +88 -0
  184. data/vendor/assets/javascripts/moment/locale/nl.js +88 -0
  185. data/vendor/assets/javascripts/moment/locale/nn.js +60 -0
  186. data/vendor/assets/javascripts/moment/locale/pa-in.js +124 -0
  187. data/vendor/assets/javascripts/moment/locale/pl.js +107 -0
  188. data/vendor/assets/javascripts/moment/locale/pt-br.js +61 -0
  189. data/vendor/assets/javascripts/moment/locale/pt.js +65 -0
  190. data/vendor/assets/javascripts/moment/locale/ro.js +75 -0
  191. data/vendor/assets/javascripts/moment/locale/ru.js +183 -0
  192. data/vendor/assets/javascripts/moment/locale/sd.js +98 -0
  193. data/vendor/assets/javascripts/moment/locale/se.js +61 -0
  194. data/vendor/assets/javascripts/moment/locale/si.js +71 -0
  195. data/vendor/assets/javascripts/moment/locale/sk.js +150 -0
  196. data/vendor/assets/javascripts/moment/locale/sl.js +162 -0
  197. data/vendor/assets/javascripts/moment/locale/sq.js +70 -0
  198. data/vendor/assets/javascripts/moment/locale/sr-cyrl.js +110 -0
  199. data/vendor/assets/javascripts/moment/locale/sr.js +110 -0
  200. data/vendor/assets/javascripts/moment/locale/ss.js +89 -0
  201. data/vendor/assets/javascripts/moment/locale/sv.js +69 -0
  202. data/vendor/assets/javascripts/moment/locale/sw.js +59 -0
  203. data/vendor/assets/javascripts/moment/locale/ta.js +130 -0
  204. data/vendor/assets/javascripts/moment/locale/te.js +89 -0
  205. data/vendor/assets/javascripts/moment/locale/tet.js +68 -0
  206. data/vendor/assets/javascripts/moment/locale/th.js +67 -0
  207. data/vendor/assets/javascripts/moment/locale/tl-ph.js +62 -0
  208. data/vendor/assets/javascripts/moment/locale/tlh.js +120 -0
  209. data/vendor/assets/javascripts/moment/locale/tr.js +90 -0
  210. data/vendor/assets/javascripts/moment/locale/tzl.js +91 -0
  211. data/vendor/assets/javascripts/moment/locale/tzm-latn.js +58 -0
  212. data/vendor/assets/javascripts/moment/locale/tzm.js +58 -0
  213. data/vendor/assets/javascripts/moment/locale/uk.js +151 -0
  214. data/vendor/assets/javascripts/moment/locale/ur.js +99 -0
  215. data/vendor/assets/javascripts/moment/locale/uz-latn.js +58 -0
  216. data/vendor/assets/javascripts/moment/locale/uz.js +58 -0
  217. data/vendor/assets/javascripts/moment/locale/vi.js +79 -0
  218. data/vendor/assets/javascripts/moment/locale/x-pseudo.js +68 -0
  219. data/vendor/assets/javascripts/moment/locale/yo.js +60 -0
  220. data/vendor/assets/javascripts/moment/locale/zh-cn.js +111 -0
  221. data/vendor/assets/javascripts/moment/locale/zh-hk.js +105 -0
  222. data/vendor/assets/javascripts/moment/locale/zh-tw.js +104 -0
  223. data/vendor/assets/javascripts/moment/min/locales.js +9252 -0
  224. data/vendor/assets/javascripts/moment/min/moment-with-locales.js +13700 -0
  225. data/vendor/assets/javascripts/moment/min/moment.min.js +7 -0
  226. data/vendor/assets/javascripts/moment/min/tests.js +78265 -0
  227. data/vendor/assets/javascripts/moment/moment.js +4463 -0
  228. data/vendor/assets/javascripts/moment/src/lib/create/check-overflow.js +34 -0
  229. data/vendor/assets/javascripts/moment/src/lib/create/date-from-array.js +21 -0
  230. data/vendor/assets/javascripts/moment/src/lib/create/from-anything.js +110 -0
  231. data/vendor/assets/javascripts/moment/src/lib/create/from-array.js +140 -0
  232. data/vendor/assets/javascripts/moment/src/lib/create/from-object.js +16 -0
  233. data/vendor/assets/javascripts/moment/src/lib/create/from-string-and-array.js +50 -0
  234. data/vendor/assets/javascripts/moment/src/lib/create/from-string-and-format.js +113 -0
  235. data/vendor/assets/javascripts/moment/src/lib/create/from-string.js +202 -0
  236. data/vendor/assets/javascripts/moment/src/lib/create/local.js +5 -0
  237. data/vendor/assets/javascripts/moment/src/lib/create/parsing-flags.js +26 -0
  238. data/vendor/assets/javascripts/moment/src/lib/create/utc.js +5 -0
  239. data/vendor/assets/javascripts/moment/src/lib/create/valid.js +49 -0
  240. data/vendor/assets/javascripts/moment/src/lib/duration/abs.js +18 -0
  241. data/vendor/assets/javascripts/moment/src/lib/duration/add-subtract.js +21 -0
  242. data/vendor/assets/javascripts/moment/src/lib/duration/as.js +61 -0
  243. data/vendor/assets/javascripts/moment/src/lib/duration/bubble.js +61 -0
  244. data/vendor/assets/javascripts/moment/src/lib/duration/constructor.js +44 -0
  245. data/vendor/assets/javascripts/moment/src/lib/duration/create.js +122 -0
  246. data/vendor/assets/javascripts/moment/src/lib/duration/duration.js +16 -0
  247. data/vendor/assets/javascripts/moment/src/lib/duration/get.js +25 -0
  248. data/vendor/assets/javascripts/moment/src/lib/duration/humanize.js +85 -0
  249. data/vendor/assets/javascripts/moment/src/lib/duration/iso-string.js +56 -0
  250. data/vendor/assets/javascripts/moment/src/lib/duration/prototype.js +50 -0
  251. data/vendor/assets/javascripts/moment/src/lib/duration/valid.js +35 -0
  252. data/vendor/assets/javascripts/moment/src/lib/format/format.js +92 -0
  253. data/vendor/assets/javascripts/moment/src/lib/locale/base-config.js +44 -0
  254. data/vendor/assets/javascripts/moment/src/lib/locale/calendar.js +15 -0
  255. data/vendor/assets/javascripts/moment/src/lib/locale/constructor.js +5 -0
  256. data/vendor/assets/javascripts/moment/src/lib/locale/en.js +15 -0
  257. data/vendor/assets/javascripts/moment/src/lib/locale/formats.js +23 -0
  258. data/vendor/assets/javascripts/moment/src/lib/locale/invalid.js +5 -0
  259. data/vendor/assets/javascripts/moment/src/lib/locale/lists.js +93 -0
  260. data/vendor/assets/javascripts/moment/src/lib/locale/locale.js +39 -0
  261. data/vendor/assets/javascripts/moment/src/lib/locale/locales.js +186 -0
  262. data/vendor/assets/javascripts/moment/src/lib/locale/ordinal.js +7 -0
  263. data/vendor/assets/javascripts/moment/src/lib/locale/pre-post-format.js +3 -0
  264. data/vendor/assets/javascripts/moment/src/lib/locale/prototype.js +69 -0
  265. data/vendor/assets/javascripts/moment/src/lib/locale/relative.js +30 -0
  266. data/vendor/assets/javascripts/moment/src/lib/locale/set.js +49 -0
  267. data/vendor/assets/javascripts/moment/src/lib/moment/add-subtract.js +55 -0
  268. data/vendor/assets/javascripts/moment/src/lib/moment/calendar.js +26 -0
  269. data/vendor/assets/javascripts/moment/src/lib/moment/clone.js +5 -0
  270. data/vendor/assets/javascripts/moment/src/lib/moment/compare.js +59 -0
  271. data/vendor/assets/javascripts/moment/src/lib/moment/constructor.js +77 -0
  272. data/vendor/assets/javascripts/moment/src/lib/moment/creation-data.js +9 -0
  273. data/vendor/assets/javascripts/moment/src/lib/moment/diff.js +62 -0
  274. data/vendor/assets/javascripts/moment/src/lib/moment/format.js +57 -0
  275. data/vendor/assets/javascripts/moment/src/lib/moment/from.js +17 -0
  276. data/vendor/assets/javascripts/moment/src/lib/moment/get-set.js +55 -0
  277. data/vendor/assets/javascripts/moment/src/lib/moment/locale.js +34 -0
  278. data/vendor/assets/javascripts/moment/src/lib/moment/min-max.js +63 -0
  279. data/vendor/assets/javascripts/moment/src/lib/moment/moment.js +28 -0
  280. data/vendor/assets/javascripts/moment/src/lib/moment/now.js +3 -0
  281. data/vendor/assets/javascripts/moment/src/lib/moment/prototype.js +150 -0
  282. data/vendor/assets/javascripts/moment/src/lib/moment/start-end-of.js +59 -0
  283. data/vendor/assets/javascripts/moment/src/lib/moment/to-type.js +34 -0
  284. data/vendor/assets/javascripts/moment/src/lib/moment/to.js +17 -0
  285. data/vendor/assets/javascripts/moment/src/lib/moment/valid.js +15 -0
  286. data/vendor/assets/javascripts/moment/src/lib/parse/regex.js +54 -0
  287. data/vendor/assets/javascripts/moment/src/lib/parse/token.js +33 -0
  288. data/vendor/assets/javascripts/moment/src/lib/units/aliases.js +30 -0
  289. data/vendor/assets/javascripts/moment/src/lib/units/constants.js +9 -0
  290. data/vendor/assets/javascripts/moment/src/lib/units/day-of-month.js +39 -0
  291. data/vendor/assets/javascripts/moment/src/lib/units/day-of-week.js +364 -0
  292. data/vendor/assets/javascripts/moment/src/lib/units/day-of-year.js +36 -0
  293. data/vendor/assets/javascripts/moment/src/lib/units/hour.js +144 -0
  294. data/vendor/assets/javascripts/moment/src/lib/units/millisecond.js +69 -0
  295. data/vendor/assets/javascripts/moment/src/lib/units/minute.js +29 -0
  296. data/vendor/assets/javascripts/moment/src/lib/units/month.js +283 -0
  297. data/vendor/assets/javascripts/moment/src/lib/units/offset.js +235 -0
  298. data/vendor/assets/javascripts/moment/src/lib/units/priorities.js +16 -0
  299. data/vendor/assets/javascripts/moment/src/lib/units/quarter.js +32 -0
  300. data/vendor/assets/javascripts/moment/src/lib/units/second.js +29 -0
  301. data/vendor/assets/javascripts/moment/src/lib/units/timestamp.js +20 -0
  302. data/vendor/assets/javascripts/moment/src/lib/units/timezone.js +16 -0
  303. data/vendor/assets/javascripts/moment/src/lib/units/units.js +20 -0
  304. data/vendor/assets/javascripts/moment/src/lib/units/week-calendar-utils.js +65 -0
  305. data/vendor/assets/javascripts/moment/src/lib/units/week-year.js +107 -0
  306. data/vendor/assets/javascripts/moment/src/lib/units/week.js +67 -0
  307. data/vendor/assets/javascripts/moment/src/lib/units/year.js +75 -0
  308. data/vendor/assets/javascripts/moment/src/lib/utils/abs-ceil.js +7 -0
  309. data/vendor/assets/javascripts/moment/src/lib/utils/abs-floor.js +8 -0
  310. data/vendor/assets/javascripts/moment/src/lib/utils/abs-round.js +7 -0
  311. data/vendor/assets/javascripts/moment/src/lib/utils/compare-arrays.js +16 -0
  312. data/vendor/assets/javascripts/moment/src/lib/utils/defaults.js +10 -0
  313. data/vendor/assets/javascripts/moment/src/lib/utils/deprecate.js +55 -0
  314. data/vendor/assets/javascripts/moment/src/lib/utils/extend.js +19 -0
  315. data/vendor/assets/javascripts/moment/src/lib/utils/has-own-prop.js +3 -0
  316. data/vendor/assets/javascripts/moment/src/lib/utils/hooks.js +13 -0
  317. data/vendor/assets/javascripts/moment/src/lib/utils/index-of.js +18 -0
  318. data/vendor/assets/javascripts/moment/src/lib/utils/is-array.js +3 -0
  319. data/vendor/assets/javascripts/moment/src/lib/utils/is-date.js +3 -0
  320. data/vendor/assets/javascripts/moment/src/lib/utils/is-function.js +3 -0
  321. data/vendor/assets/javascripts/moment/src/lib/utils/is-number.js +3 -0
  322. data/vendor/assets/javascripts/moment/src/lib/utils/is-object-empty.js +8 -0
  323. data/vendor/assets/javascripts/moment/src/lib/utils/is-object.js +5 -0
  324. data/vendor/assets/javascripts/moment/src/lib/utils/is-undefined.js +3 -0
  325. data/vendor/assets/javascripts/moment/src/lib/utils/keys.js +19 -0
  326. data/vendor/assets/javascripts/moment/src/lib/utils/map.js +7 -0
  327. data/vendor/assets/javascripts/moment/src/lib/utils/some.js +19 -0
  328. data/vendor/assets/javascripts/moment/src/lib/utils/to-int.js +12 -0
  329. data/vendor/assets/javascripts/moment/src/lib/utils/zero-fill.js +7 -0
  330. data/vendor/assets/javascripts/moment/src/locale/af.js +63 -0
  331. data/vendor/assets/javascripts/moment/src/locale/ar-dz.js +50 -0
  332. data/vendor/assets/javascripts/moment/src/locale/ar-kw.js +49 -0
  333. data/vendor/assets/javascripts/moment/src/locale/ar-ly.js +112 -0
  334. data/vendor/assets/javascripts/moment/src/locale/ar-ma.js +51 -0
  335. data/vendor/assets/javascripts/moment/src/locale/ar-sa.js +95 -0
  336. data/vendor/assets/javascripts/moment/src/locale/ar-tn.js +50 -0
  337. data/vendor/assets/javascripts/moment/src/locale/ar.js +128 -0
  338. data/vendor/assets/javascripts/moment/src/locale/az.js +96 -0
  339. data/vendor/assets/javascripts/moment/src/locale/be.js +125 -0
  340. data/vendor/assets/javascripts/moment/src/locale/bg.js +81 -0
  341. data/vendor/assets/javascripts/moment/src/locale/bn.js +109 -0
  342. data/vendor/assets/javascripts/moment/src/locale/bo.js +110 -0
  343. data/vendor/assets/javascripts/moment/src/locale/br.js +99 -0
  344. data/vendor/assets/javascripts/moment/src/locale/bs.js +133 -0
  345. data/vendor/assets/javascripts/moment/src/locale/ca.js +79 -0
  346. data/vendor/assets/javascripts/moment/src/locale/cs.js +163 -0
  347. data/vendor/assets/javascripts/moment/src/locale/cv.js +53 -0
  348. data/vendor/assets/javascripts/moment/src/locale/cy.js +72 -0
  349. data/vendor/assets/javascripts/moment/src/locale/da.js +50 -0
  350. data/vendor/assets/javascripts/moment/src/locale/de-at.js +69 -0
  351. data/vendor/assets/javascripts/moment/src/locale/de-ch.js +68 -0
  352. data/vendor/assets/javascripts/moment/src/locale/de.js +68 -0
  353. data/vendor/assets/javascripts/moment/src/locale/dv.js +89 -0
  354. data/vendor/assets/javascripts/moment/src/locale/el.js +88 -0
  355. data/vendor/assets/javascripts/moment/src/locale/en-au.js +58 -0
  356. data/vendor/assets/javascripts/moment/src/locale/en-ca.js +53 -0
  357. data/vendor/assets/javascripts/moment/src/locale/en-gb.js +58 -0
  358. data/vendor/assets/javascripts/moment/src/locale/en-ie.js +58 -0
  359. data/vendor/assets/javascripts/moment/src/locale/en-nz.js +57 -0
  360. data/vendor/assets/javascripts/moment/src/locale/eo.js +64 -0
  361. data/vendor/assets/javascripts/moment/src/locale/es-do.js +73 -0
  362. data/vendor/assets/javascripts/moment/src/locale/es.js +74 -0
  363. data/vendor/assets/javascripts/moment/src/locale/et.js +71 -0
  364. data/vendor/assets/javascripts/moment/src/locale/eu.js +57 -0
  365. data/vendor/assets/javascripts/moment/src/locale/fa.js +97 -0
  366. data/vendor/assets/javascripts/moment/src/locale/fi.js +98 -0
  367. data/vendor/assets/javascripts/moment/src/locale/fo.js +51 -0
  368. data/vendor/assets/javascripts/moment/src/locale/fr-ca.js +65 -0
  369. data/vendor/assets/javascripts/moment/src/locale/fr-ch.js +69 -0
  370. data/vendor/assets/javascripts/moment/src/locale/fr.js +74 -0
  371. data/vendor/assets/javascripts/moment/src/locale/fy.js +66 -0
  372. data/vendor/assets/javascripts/moment/src/locale/gd.js +67 -0
  373. data/vendor/assets/javascripts/moment/src/locale/gl.js +68 -0
  374. data/vendor/assets/javascripts/moment/src/locale/gom-latn.js +112 -0
  375. data/vendor/assets/javascripts/moment/src/locale/he.js +90 -0
  376. data/vendor/assets/javascripts/moment/src/locale/hi.js +115 -0
  377. data/vendor/assets/javascripts/moment/src/locale/hr.js +135 -0
  378. data/vendor/assets/javascripts/moment/src/locale/hu.js +100 -0
  379. data/vendor/assets/javascripts/moment/src/locale/hy-am.js +86 -0
  380. data/vendor/assets/javascripts/moment/src/locale/id.js +74 -0
  381. data/vendor/assets/javascripts/moment/src/locale/is.js +118 -0
  382. data/vendor/assets/javascripts/moment/src/locale/it.js +61 -0
  383. data/vendor/assets/javascripts/moment/src/locale/ja.js +71 -0
  384. data/vendor/assets/javascripts/moment/src/locale/jv.js +73 -0
  385. data/vendor/assets/javascripts/moment/src/locale/ka.js +80 -0
  386. data/vendor/assets/javascripts/moment/src/locale/kk.js +77 -0
  387. data/vendor/assets/javascripts/moment/src/locale/km.js +49 -0
  388. data/vendor/assets/javascripts/moment/src/locale/kn.js +116 -0
  389. data/vendor/assets/javascripts/moment/src/locale/ko.js +60 -0
  390. data/vendor/assets/javascripts/moment/src/locale/ky.js +78 -0
  391. data/vendor/assets/javascripts/moment/src/locale/lb.js +128 -0
  392. data/vendor/assets/javascripts/moment/src/locale/lo.js +61 -0
  393. data/vendor/assets/javascripts/moment/src/locale/lt.js +108 -0
  394. data/vendor/assets/javascripts/moment/src/locale/lv.js +88 -0
  395. data/vendor/assets/javascripts/moment/src/locale/me.js +101 -0
  396. data/vendor/assets/javascripts/moment/src/locale/mi.js +54 -0
  397. data/vendor/assets/javascripts/moment/src/locale/mk.js +81 -0
  398. data/vendor/assets/javascripts/moment/src/locale/ml.js +72 -0
  399. data/vendor/assets/javascripts/moment/src/locale/mr.js +150 -0
  400. data/vendor/assets/javascripts/moment/src/locale/ms-my.js +74 -0
  401. data/vendor/assets/javascripts/moment/src/locale/ms.js +73 -0
  402. data/vendor/assets/javascripts/moment/src/locale/my.js +86 -0
  403. data/vendor/assets/javascripts/moment/src/locale/nb.js +54 -0
  404. data/vendor/assets/javascripts/moment/src/locale/ne.js +114 -0
  405. data/vendor/assets/javascripts/moment/src/locale/nl-be.js +79 -0
  406. data/vendor/assets/javascripts/moment/src/locale/nl.js +79 -0
  407. data/vendor/assets/javascripts/moment/src/locale/nn.js +51 -0
  408. data/vendor/assets/javascripts/moment/src/locale/pa-in.js +115 -0
  409. data/vendor/assets/javascripts/moment/src/locale/pl.js +97 -0
  410. data/vendor/assets/javascripts/moment/src/locale/pt-br.js +52 -0
  411. data/vendor/assets/javascripts/moment/src/locale/pt.js +56 -0
  412. data/vendor/assets/javascripts/moment/src/locale/ro.js +66 -0
  413. data/vendor/assets/javascripts/moment/src/locale/ru.js +173 -0
  414. data/vendor/assets/javascripts/moment/src/locale/sd.js +88 -0
  415. data/vendor/assets/javascripts/moment/src/locale/se.js +51 -0
  416. data/vendor/assets/javascripts/moment/src/locale/si.js +61 -0
  417. data/vendor/assets/javascripts/moment/src/locale/sk.js +141 -0
  418. data/vendor/assets/javascripts/moment/src/locale/sl.js +152 -0
  419. data/vendor/assets/javascripts/moment/src/locale/sq.js +61 -0
  420. data/vendor/assets/javascripts/moment/src/locale/sr-cyrl.js +100 -0
  421. data/vendor/assets/javascripts/moment/src/locale/sr.js +100 -0
  422. data/vendor/assets/javascripts/moment/src/locale/ss.js +80 -0
  423. data/vendor/assets/javascripts/moment/src/locale/sv.js +60 -0
  424. data/vendor/assets/javascripts/moment/src/locale/sw.js +50 -0
  425. data/vendor/assets/javascripts/moment/src/locale/ta.js +120 -0
  426. data/vendor/assets/javascripts/moment/src/locale/te.js +79 -0
  427. data/vendor/assets/javascripts/moment/src/locale/tet.js +58 -0
  428. data/vendor/assets/javascripts/moment/src/locale/th.js +57 -0
  429. data/vendor/assets/javascripts/moment/src/locale/tl-ph.js +53 -0
  430. data/vendor/assets/javascripts/moment/src/locale/tlh.js +110 -0
  431. data/vendor/assets/javascripts/moment/src/locale/tr.js +81 -0
  432. data/vendor/assets/javascripts/moment/src/locale/tzl.js +82 -0
  433. data/vendor/assets/javascripts/moment/src/locale/tzm-latn.js +49 -0
  434. data/vendor/assets/javascripts/moment/src/locale/tzm.js +49 -0
  435. data/vendor/assets/javascripts/moment/src/locale/uk.js +142 -0
  436. data/vendor/assets/javascripts/moment/src/locale/ur.js +89 -0
  437. data/vendor/assets/javascripts/moment/src/locale/uz-latn.js +49 -0
  438. data/vendor/assets/javascripts/moment/src/locale/uz.js +49 -0
  439. data/vendor/assets/javascripts/moment/src/locale/vi.js +70 -0
  440. data/vendor/assets/javascripts/moment/src/locale/x-pseudo.js +58 -0
  441. data/vendor/assets/javascripts/moment/src/locale/yo.js +50 -0
  442. data/vendor/assets/javascripts/moment/src/locale/zh-cn.js +102 -0
  443. data/vendor/assets/javascripts/moment/src/locale/zh-hk.js +95 -0
  444. data/vendor/assets/javascripts/moment/src/locale/zh-tw.js +94 -0
  445. data/vendor/assets/javascripts/moment/src/moment.js +82 -0
  446. data/vendor/assets/javascripts/moment/templates/default.js +5 -0
  447. data/vendor/assets/javascripts/moment/templates/locale-header.js +6 -0
  448. data/vendor/assets/javascripts/moment/templates/test-header.js +6 -0
  449. metadata +943 -0
@@ -0,0 +1,3487 @@
1
+ /*
2
+ * Copyright 2015 Async-IO.org
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
+ * Atmosphere.js
18
+ * https://github.com/Atmosphere/atmosphere-javascript
19
+ *
20
+ * API reference
21
+ * https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-API
22
+ *
23
+ * Highly inspired by
24
+ * - Portal by Donghwan Kim http://flowersinthesand.github.io/portal/
25
+ */
26
+ (function (root, factory) {
27
+ if (typeof define === "function" && define.amd) {
28
+ // AMD
29
+ define(factory);
30
+ } else if(typeof exports !== 'undefined') {
31
+ // CommonJS
32
+ module.exports = factory();
33
+ } else {
34
+ // Browser globals, Window
35
+ root.atmosphere = factory();
36
+ }
37
+ }(this, function () {
38
+
39
+ "use strict";
40
+
41
+ var atmosphere = {},
42
+ guid,
43
+ offline = false,
44
+ requests = [],
45
+ callbacks = [],
46
+ uuid = 0,
47
+ hasOwn = Object.prototype.hasOwnProperty;
48
+
49
+ atmosphere = {
50
+ version: "2.3.2-javascript",
51
+ onError: function (response) {
52
+ },
53
+ onClose: function (response) {
54
+ },
55
+ onOpen: function (response) {
56
+ },
57
+ onReopen: function (response) {
58
+ },
59
+ onMessage: function (response) {
60
+ },
61
+ onReconnect: function (request, response) {
62
+ },
63
+ onMessagePublished: function (response) {
64
+ },
65
+ onTransportFailure: function (errorMessage, _request) {
66
+ },
67
+ onLocalMessage: function (response) {
68
+ },
69
+ onFailureToReconnect: function (request, response) {
70
+ },
71
+ onClientTimeout: function (request) {
72
+ },
73
+ onOpenAfterResume: function (request) {
74
+ },
75
+
76
+ /**
77
+ * Creates an object based on an atmosphere subscription that exposes functions defined by the Websocket interface.
78
+ *
79
+ * @class WebsocketApiAdapter
80
+ * @param {Object} request the request object to build the underlying subscription
81
+ * @constructor
82
+ */
83
+ WebsocketApiAdapter: function (request) {
84
+ var _socket, _adapter;
85
+
86
+ /**
87
+ * Overrides the onMessage callback in given request.
88
+ *
89
+ * @method onMessage
90
+ * @param {Object} e the event object
91
+ */
92
+ request.onMessage = function (e) {
93
+ _adapter.onmessage({data: e.responseBody});
94
+ };
95
+
96
+ /**
97
+ * Overrides the onMessagePublished callback in given request.
98
+ *
99
+ * @method onMessagePublished
100
+ * @param {Object} e the event object
101
+ */
102
+ request.onMessagePublished = function (e) {
103
+ _adapter.onmessage({data: e.responseBody});
104
+ };
105
+
106
+ /**
107
+ * Overrides the onOpen callback in given request to proxy the event to the adapter.
108
+ *
109
+ * @method onOpen
110
+ * @param {Object} e the event object
111
+ */
112
+ request.onOpen = function (e) {
113
+ _adapter.onopen(e);
114
+ };
115
+
116
+ _adapter = {
117
+ close: function () {
118
+ _socket.close();
119
+ },
120
+
121
+ send: function (data) {
122
+ _socket.push(data);
123
+ },
124
+
125
+ onmessage: function (e) {
126
+ },
127
+
128
+ onopen: function (e) {
129
+ },
130
+
131
+ onclose: function (e) {
132
+ },
133
+
134
+ onerror: function (e) {
135
+
136
+ }
137
+ };
138
+ _socket = new atmosphere.subscribe(request);
139
+
140
+ return _adapter;
141
+ },
142
+
143
+ AtmosphereRequest: function (options) {
144
+
145
+ /**
146
+ * {Object} Request parameters.
147
+ *
148
+ * @private
149
+ */
150
+ var _request = {
151
+ timeout: 300000,
152
+ method: 'GET',
153
+ headers: {},
154
+ contentType: '',
155
+ callback: null,
156
+ url: '',
157
+ data: '',
158
+ suspend: true,
159
+ maxRequest: -1,
160
+ reconnect: true,
161
+ maxStreamingLength: 10000000,
162
+ lastIndex: 0,
163
+ logLevel: 'info',
164
+ requestCount: 0,
165
+ fallbackMethod: 'GET',
166
+ fallbackTransport: 'streaming',
167
+ transport: 'long-polling',
168
+ webSocketImpl: null,
169
+ webSocketBinaryType: null,
170
+ dispatchUrl: null,
171
+ webSocketPathDelimiter: "@@",
172
+ enableXDR: false,
173
+ rewriteURL: false,
174
+ attachHeadersAsQueryString: true,
175
+ executeCallbackBeforeReconnect: false,
176
+ readyState: 0,
177
+ withCredentials: false,
178
+ trackMessageLength: false,
179
+ messageDelimiter: '|',
180
+ connectTimeout: -1,
181
+ reconnectInterval: 0,
182
+ dropHeaders: true,
183
+ uuid: 0,
184
+ async: true,
185
+ shared: false,
186
+ readResponsesHeaders: false,
187
+ maxReconnectOnClose: 5,
188
+ enableProtocol: true,
189
+ disableDisconnect: false,
190
+ pollingInterval: 0,
191
+ heartbeat: {
192
+ client: null,
193
+ server: null
194
+ },
195
+ ackInterval: 0,
196
+ closeAsync: false,
197
+ reconnectOnServerError: true,
198
+ handleOnlineOffline: true,
199
+ onError: function (response) {
200
+ },
201
+ onClose: function (response) {
202
+ },
203
+ onOpen: function (response) {
204
+ },
205
+ onMessage: function (response) {
206
+ },
207
+ onReopen: function (request, response) {
208
+ },
209
+ onReconnect: function (request, response) {
210
+ },
211
+ onMessagePublished: function (response) {
212
+ },
213
+ onTransportFailure: function (reason, request) {
214
+ },
215
+ onLocalMessage: function (request) {
216
+ },
217
+ onFailureToReconnect: function (request, response) {
218
+ },
219
+ onClientTimeout: function (request) {
220
+ },
221
+ onOpenAfterResume: function (request) {
222
+ }
223
+ };
224
+
225
+ /**
226
+ * {Object} Request's last response.
227
+ *
228
+ * @private
229
+ */
230
+ var _response = {
231
+ status: 200,
232
+ reasonPhrase: "OK",
233
+ responseBody: '',
234
+ messages: [],
235
+ headers: [],
236
+ state: "messageReceived",
237
+ transport: "polling",
238
+ error: null,
239
+ request: null,
240
+ partialMessage: "",
241
+ errorHandled: false,
242
+ closedByClientTimeout: false,
243
+ ffTryingReconnect: false
244
+ };
245
+
246
+ /**
247
+ * {websocket} Opened web socket.
248
+ *
249
+ * @private
250
+ */
251
+ var _websocket = null;
252
+
253
+ /**
254
+ * {SSE} Opened SSE.
255
+ *
256
+ * @private
257
+ */
258
+ var _sse = null;
259
+
260
+ /**
261
+ * {XMLHttpRequest, ActiveXObject} Opened ajax request (in case of http-streaming or long-polling)
262
+ *
263
+ * @private
264
+ */
265
+ var _activeRequest = null;
266
+
267
+ /**
268
+ * {Object} Object use for streaming with IE.
269
+ *
270
+ * @private
271
+ */
272
+ var _ieStream = null;
273
+
274
+ /**
275
+ * {Object} Object use for jsonp transport.
276
+ *
277
+ * @private
278
+ */
279
+ var _jqxhr = null;
280
+
281
+ /**
282
+ * {boolean} If request has been subscribed or not.
283
+ *
284
+ * @private
285
+ */
286
+ var _subscribed = true;
287
+
288
+ /**
289
+ * {number} Number of test reconnection.
290
+ *
291
+ * @private
292
+ */
293
+ var _requestCount = 0;
294
+
295
+ /**
296
+ * The Heartbeat interval send by the server.
297
+ * @type {int}
298
+ * @private
299
+ */
300
+ var _heartbeatInterval = 0;
301
+
302
+ /**
303
+ * The Heartbeat bytes send by the server.
304
+ * @type {string}
305
+ * @private
306
+ */
307
+ var _heartbeatPadding = 'X';
308
+
309
+ /**
310
+ * {boolean} If request is currently aborted.
311
+ *
312
+ * @private
313
+ */
314
+ var _abortingConnection = false;
315
+
316
+ /**
317
+ * A local "channel' of communication.
318
+ *
319
+ * @private
320
+ */
321
+ var _localSocketF = null;
322
+
323
+ /**
324
+ * The storage used.
325
+ *
326
+ * @private
327
+ */
328
+ var _storageService;
329
+
330
+ /**
331
+ * Local communication
332
+ *
333
+ * @private
334
+ */
335
+ var _localStorageService = null;
336
+
337
+ /**
338
+ * A Unique ID
339
+ *
340
+ * @private
341
+ */
342
+ var guid = atmosphere.util.now();
343
+
344
+ /** Trace time */
345
+ var _traceTimer;
346
+
347
+ /** Key for connection sharing */
348
+ var _sharingKey;
349
+
350
+ /**
351
+ * {boolean} If window beforeUnload event has been called.
352
+ * Flag will be reset after 5000 ms
353
+ *
354
+ * @private
355
+ */
356
+ var _beforeUnloadState = false;
357
+
358
+ // Automatic call to subscribe
359
+ _subscribe(options);
360
+
361
+ /**
362
+ * Initialize atmosphere request object.
363
+ *
364
+ * @private
365
+ */
366
+ function _init() {
367
+ _subscribed = true;
368
+ _abortingConnection = false;
369
+ _requestCount = 0;
370
+
371
+ _websocket = null;
372
+ _sse = null;
373
+ _activeRequest = null;
374
+ _ieStream = null;
375
+ }
376
+
377
+ /**
378
+ * Re-initialize atmosphere object.
379
+ *
380
+ * @private
381
+ */
382
+ function _reinit() {
383
+ _clearState();
384
+ _init();
385
+ }
386
+
387
+ /**
388
+ * Returns true if the given level is equal or above the configured log level.
389
+ *
390
+ * @private
391
+ */
392
+ function _canLog(level) {
393
+ if (level == 'debug') {
394
+ return _request.logLevel === 'debug';
395
+ } else if (level == 'info') {
396
+ return _request.logLevel === 'info' || _request.logLevel === 'debug';
397
+ } else if (level == 'warn') {
398
+ return _request.logLevel === 'warn' || _request.logLevel === 'info' || _request.logLevel === 'debug';
399
+ } else if (level == 'error') {
400
+ return _request.logLevel === 'error' || _request.logLevel === 'warn' || _request.logLevel === 'info' || _request.logLevel === 'debug';
401
+ } else {
402
+ return false;
403
+ }
404
+ }
405
+
406
+ function _debug(msg) {
407
+ if (_canLog('debug')) {
408
+ atmosphere.util.debug(new Date() + " Atmosphere: " + msg);
409
+ }
410
+ }
411
+
412
+ /**
413
+ *
414
+ * @private
415
+ */
416
+ function _verifyStreamingLength(ajaxRequest, rq) {
417
+ // Wait to be sure we have the full message before closing.
418
+ if (_response.partialMessage === "" && (rq.transport === 'streaming') && (ajaxRequest.responseText.length > rq.maxStreamingLength)) {
419
+ return true;
420
+ }
421
+ return false;
422
+ }
423
+
424
+ /**
425
+ * Disconnect
426
+ *
427
+ * @private
428
+ */
429
+ function _disconnect() {
430
+ if (_request.enableProtocol && !_request.disableDisconnect && !_request.firstMessage) {
431
+ var query = "X-Atmosphere-Transport=close&X-Atmosphere-tracking-id=" + _request.uuid;
432
+
433
+ atmosphere.util.each(_request.headers, function (name, value) {
434
+ var h = atmosphere.util.isFunction(value) ? value.call(this, _request, _request, _response) : value;
435
+ if (h != null) {
436
+ query += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(h);
437
+ }
438
+ });
439
+
440
+ var url = _request.url.replace(/([?&])_=[^&]*/, query);
441
+ url = url + (url === _request.url ? (/\?/.test(_request.url) ? "&" : "?") + query : "");
442
+
443
+ var rq = {
444
+ connected: false
445
+ };
446
+ var closeR = new atmosphere.AtmosphereRequest(rq);
447
+ closeR.connectTimeout = _request.connectTimeout;
448
+ closeR.attachHeadersAsQueryString = false;
449
+ closeR.dropHeaders = true;
450
+ closeR.url = url;
451
+ closeR.contentType = "text/plain";
452
+ closeR.transport = 'polling';
453
+ closeR.method = 'GET';
454
+ closeR.data = '';
455
+ closeR.heartbeat = null;
456
+ if (_request.enableXDR) {
457
+ closeR.enableXDR = _request.enableXDR
458
+ }
459
+ closeR.async = _request.closeAsync;
460
+ _pushOnClose("", closeR);
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Close request.
466
+ *
467
+ * @private
468
+ */
469
+ function _close() {
470
+ _debug("Closing (AtmosphereRequest._close() called)");
471
+
472
+ _abortingConnection = true;
473
+ if (_request.reconnectId) {
474
+ clearTimeout(_request.reconnectId);
475
+ delete _request.reconnectId;
476
+ }
477
+
478
+ if (_request.heartbeatTimer) {
479
+ clearTimeout(_request.heartbeatTimer);
480
+ }
481
+
482
+ _request.reconnect = false;
483
+ _response.request = _request;
484
+ _response.state = 'unsubscribe';
485
+ _response.responseBody = "";
486
+ _response.status = 408;
487
+ _response.partialMessage = "";
488
+ _invokeCallback();
489
+ _disconnect();
490
+ _clearState();
491
+ }
492
+
493
+ function _clearState() {
494
+ _response.partialMessage = "";
495
+ if (_request.id) {
496
+ clearTimeout(_request.id);
497
+ }
498
+
499
+ if (_request.heartbeatTimer) {
500
+ clearTimeout(_request.heartbeatTimer);
501
+ }
502
+
503
+ // https://github.com/Atmosphere/atmosphere/issues/1860#issuecomment-74707226
504
+ if(_request.reconnectId) {
505
+ clearTimeout(_request.reconnectId);
506
+ delete _request.reconnectId;
507
+ }
508
+
509
+ if (_ieStream != null) {
510
+ _ieStream.close();
511
+ _ieStream = null;
512
+ }
513
+ if (_jqxhr != null) {
514
+ _jqxhr.abort();
515
+ _jqxhr = null;
516
+ }
517
+ if (_activeRequest != null) {
518
+ _activeRequest.abort();
519
+ _activeRequest = null;
520
+ }
521
+ if (_websocket != null) {
522
+ if (_websocket.canSendMessage) {
523
+ _debug("invoking .close() on WebSocket object");
524
+ _websocket.close();
525
+ }
526
+ _websocket = null;
527
+ }
528
+ if (_sse != null) {
529
+ _sse.close();
530
+ _sse = null;
531
+ }
532
+ _clearStorage();
533
+ }
534
+
535
+ function _clearStorage() {
536
+ // Stop sharing a connection
537
+ if (_storageService != null) {
538
+ // Clears trace timer
539
+ clearInterval(_traceTimer);
540
+ // Removes the trace
541
+ document.cookie = _sharingKey + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
542
+ // The heir is the parent unless unloading
543
+ _storageService.signal("close", {
544
+ reason: "",
545
+ heir: !_abortingConnection ? guid : (_storageService.get("children") || [])[0]
546
+ });
547
+ _storageService.close();
548
+ }
549
+ if (_localStorageService != null) {
550
+ _localStorageService.close();
551
+ }
552
+ }
553
+
554
+ /**
555
+ * Subscribe request using request transport. <br>
556
+ * If request is currently opened, this one will be closed.
557
+ *
558
+ * @param {Object} Request parameters.
559
+ * @private
560
+ */
561
+ function _subscribe(options) {
562
+ _reinit();
563
+
564
+ _request = atmosphere.util.extend(_request, options);
565
+ // Allow at least 1 request
566
+ _request.mrequest = _request.reconnect;
567
+ if (!_request.reconnect) {
568
+ _request.reconnect = true;
569
+ }
570
+ }
571
+
572
+ /**
573
+ * Check if web socket is supported (check for custom implementation provided by request object or browser implementation).
574
+ *
575
+ * @returns {boolean} True if web socket is supported, false otherwise.
576
+ * @private
577
+ */
578
+ function _supportWebsocket() {
579
+ return _request.webSocketImpl != null || window.WebSocket || window.MozWebSocket;
580
+ }
581
+
582
+ /**
583
+ * Check if server side events (SSE) is supported (check for custom implementation provided by request object or browser implementation).
584
+ *
585
+ * @returns {boolean} True if web socket is supported, false otherwise.
586
+ * @private
587
+ */
588
+ function _supportSSE() {
589
+ // Origin parts
590
+ var url = atmosphere.util.getAbsoluteURL(_request.url.toLowerCase());
591
+ var parts = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/.exec(url);
592
+ var crossOrigin = !!(parts && (
593
+ // protocol
594
+ parts[1] != window.location.protocol ||
595
+ // hostname
596
+ parts[2] != window.location.hostname ||
597
+ // port
598
+ (parts[3] || (parts[1] === "http:" ? 80 : 443)) != (window.location.port || (window.location.protocol === "http:" ? 80 : 443))
599
+ ));
600
+ return window.EventSource && (!crossOrigin || !atmosphere.util.browser.safari || atmosphere.util.browser.vmajor >= 7);
601
+ }
602
+
603
+ /**
604
+ * Open request using request transport. <br>
605
+ * If request transport is 'websocket' but websocket can't be opened, request will automatically reconnect using fallback transport.
606
+ *
607
+ * @private
608
+ */
609
+ function _execute() {
610
+ // Shared across multiple tabs/windows.
611
+ if (_request.shared) {
612
+ _localStorageService = _local(_request);
613
+ if (_localStorageService != null) {
614
+ if (_canLog('debug')) {
615
+ atmosphere.util.debug("Storage service available. All communication will be local");
616
+ }
617
+
618
+ if (_localStorageService.open(_request)) {
619
+ // Local connection.
620
+ return;
621
+ }
622
+ }
623
+
624
+ if (_canLog('debug')) {
625
+ atmosphere.util.debug("No Storage service available.");
626
+ }
627
+ // Wasn't local or an error occurred
628
+ _localStorageService = null;
629
+ }
630
+
631
+ // Protocol
632
+ _request.firstMessage = uuid == 0 ? true : false;
633
+ _request.isOpen = false;
634
+ _request.ctime = atmosphere.util.now();
635
+
636
+ // We carry any UUID set by the user or from a previous connection.
637
+ if (_request.uuid === 0) {
638
+ _request.uuid = uuid;
639
+ }
640
+ _response.closedByClientTimeout = false;
641
+
642
+ if (_request.transport !== 'websocket' && _request.transport !== 'sse') {
643
+ _executeRequest(_request);
644
+
645
+ } else if (_request.transport === 'websocket') {
646
+ if (!_supportWebsocket()) {
647
+ _reconnectWithFallbackTransport("Websocket is not supported, using request.fallbackTransport (" + _request.fallbackTransport
648
+ + ")");
649
+ } else {
650
+ _executeWebSocket(false);
651
+ }
652
+ } else if (_request.transport === 'sse') {
653
+ if (!_supportSSE()) {
654
+ _reconnectWithFallbackTransport("Server Side Events(SSE) is not supported, using request.fallbackTransport ("
655
+ + _request.fallbackTransport + ")");
656
+ } else {
657
+ _executeSSE(false);
658
+ }
659
+ }
660
+ }
661
+
662
+ function _local(request) {
663
+ var trace, connector, orphan, name = "atmosphere-" + request.url, connectors = {
664
+ storage: function () {
665
+ function onstorage(event) {
666
+ if (event.key === name && event.newValue) {
667
+ listener(event.newValue);
668
+ }
669
+ }
670
+
671
+ if (!atmosphere.util.storage) {
672
+ return;
673
+ }
674
+
675
+ var storage = window.localStorage,
676
+ get = function (key) {
677
+ return atmosphere.util.parseJSON(storage.getItem(name + "-" + key));
678
+ },
679
+ set = function (key, value) {
680
+ storage.setItem(name + "-" + key, atmosphere.util.stringifyJSON(value));
681
+ };
682
+
683
+ return {
684
+ init: function () {
685
+ set("children", get("children").concat([guid]));
686
+ atmosphere.util.on(window, "storage", onstorage);
687
+ return get("opened");
688
+ },
689
+ signal: function (type, data) {
690
+ storage.setItem(name, atmosphere.util.stringifyJSON({
691
+ target: "p",
692
+ type: type,
693
+ data: data
694
+ }));
695
+ },
696
+ close: function () {
697
+ var children = get("children");
698
+
699
+ atmosphere.util.off(window, "storage", onstorage);
700
+ if (children) {
701
+ if (removeFromArray(children, request.id)) {
702
+ set("children", children);
703
+ }
704
+ }
705
+ }
706
+ };
707
+ },
708
+ windowref: function () {
709
+ var win = window.open("", name.replace(/\W/g, ""));
710
+
711
+ if (!win || win.closed || !win.callbacks) {
712
+ return;
713
+ }
714
+
715
+ return {
716
+ init: function () {
717
+ win.callbacks.push(listener);
718
+ win.children.push(guid);
719
+ return win.opened;
720
+ },
721
+ signal: function (type, data) {
722
+ if (!win.closed && win.fire) {
723
+ win.fire(atmosphere.util.stringifyJSON({
724
+ target: "p",
725
+ type: type,
726
+ data: data
727
+ }));
728
+ }
729
+ },
730
+ close: function () {
731
+ // Removes traces only if the parent is alive
732
+ if (!orphan) {
733
+ removeFromArray(win.callbacks, listener);
734
+ removeFromArray(win.children, guid);
735
+ }
736
+ }
737
+
738
+ };
739
+ }
740
+ };
741
+
742
+ function removeFromArray(array, val) {
743
+ var i, length = array.length;
744
+
745
+ for (i = 0; i < length; i++) {
746
+ if (array[i] === val) {
747
+ array.splice(i, 1);
748
+ }
749
+ }
750
+
751
+ return length !== array.length;
752
+ }
753
+
754
+ // Receives open, close and message command from the parent
755
+ function listener(string) {
756
+ var command = atmosphere.util.parseJSON(string), data = command.data;
757
+
758
+ if (command.target === "c") {
759
+ switch (command.type) {
760
+ case "open":
761
+ _open("opening", 'local', _request);
762
+ break;
763
+ case "close":
764
+ if (!orphan) {
765
+ orphan = true;
766
+ if (data.reason === "aborted") {
767
+ _close();
768
+ } else {
769
+ // Gives the heir some time to reconnect
770
+ if (data.heir === guid) {
771
+ _execute();
772
+ } else {
773
+ setTimeout(function () {
774
+ _execute();
775
+ }, 100);
776
+ }
777
+ }
778
+ }
779
+ break;
780
+ case "message":
781
+ _prepareCallback(data, "messageReceived", 200, request.transport);
782
+ break;
783
+ case "localMessage":
784
+ _localMessage(data);
785
+ break;
786
+ }
787
+ }
788
+ }
789
+
790
+ function findTrace() {
791
+ var matcher = new RegExp("(?:^|; )(" + encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie);
792
+ if (matcher) {
793
+ return atmosphere.util.parseJSON(decodeURIComponent(matcher[2]));
794
+ }
795
+ }
796
+
797
+ // Finds and validates the parent socket's trace from the cookie
798
+ trace = findTrace();
799
+ if (!trace || atmosphere.util.now() - trace.ts > 1000) {
800
+ return;
801
+ }
802
+
803
+ // Chooses a connector
804
+ connector = connectors.storage() || connectors.windowref();
805
+ if (!connector) {
806
+ return;
807
+ }
808
+
809
+ return {
810
+ open: function () {
811
+ var parentOpened;
812
+
813
+ // Checks the shared one is alive
814
+ _traceTimer = setInterval(function () {
815
+ var oldTrace = trace;
816
+ trace = findTrace();
817
+ if (!trace || oldTrace.ts === trace.ts) {
818
+ // Simulates a close signal
819
+ listener(atmosphere.util.stringifyJSON({
820
+ target: "c",
821
+ type: "close",
822
+ data: {
823
+ reason: "error",
824
+ heir: oldTrace.heir
825
+ }
826
+ }));
827
+ }
828
+ }, 1000);
829
+
830
+ parentOpened = connector.init();
831
+ if (parentOpened) {
832
+ // Firing the open event without delay robs the user of the opportunity to bind connecting event handlers
833
+ setTimeout(function () {
834
+ _open("opening", 'local', request);
835
+ }, 50);
836
+ }
837
+ return parentOpened;
838
+ },
839
+ send: function (event) {
840
+ connector.signal("send", event);
841
+ },
842
+ localSend: function (event) {
843
+ connector.signal("localSend", atmosphere.util.stringifyJSON({
844
+ id: guid,
845
+ event: event
846
+ }));
847
+ },
848
+ close: function () {
849
+ // Do not signal the parent if this method is executed by the unload event handler
850
+ if (!_abortingConnection) {
851
+ clearInterval(_traceTimer);
852
+ connector.signal("close");
853
+ connector.close();
854
+ }
855
+ }
856
+ };
857
+ }
858
+
859
+ function share() {
860
+ var storageService, name = "atmosphere-" + _request.url, servers = {
861
+ // Powered by the storage event and the localStorage
862
+ // http://www.w3.org/TR/webstorage/#event-storage
863
+ storage: function () {
864
+ function onstorage(event) {
865
+ // When a deletion, newValue initialized to null
866
+ if (event.key === name && event.newValue) {
867
+ listener(event.newValue);
868
+ }
869
+ }
870
+
871
+ if (!atmosphere.util.storage) {
872
+ return;
873
+ }
874
+
875
+ var storage = window.localStorage;
876
+
877
+ return {
878
+ init: function () {
879
+ // Handles the storage event
880
+ atmosphere.util.on(window, "storage", onstorage);
881
+ },
882
+ signal: function (type, data) {
883
+ storage.setItem(name, atmosphere.util.stringifyJSON({
884
+ target: "c",
885
+ type: type,
886
+ data: data
887
+ }));
888
+ },
889
+ get: function (key) {
890
+ return atmosphere.util.parseJSON(storage.getItem(name + "-" + key));
891
+ },
892
+ set: function (key, value) {
893
+ storage.setItem(name + "-" + key, atmosphere.util.stringifyJSON(value));
894
+ },
895
+ close: function () {
896
+ atmosphere.util.off(window, "storage", onstorage);
897
+ storage.removeItem(name);
898
+ storage.removeItem(name + "-opened");
899
+ storage.removeItem(name + "-children");
900
+ }
901
+
902
+ };
903
+ },
904
+ // Powered by the window.open method
905
+ // https://developer.mozilla.org/en/DOM/window.open
906
+ windowref: function () {
907
+ // Internet Explorer raises an invalid argument error
908
+ // when calling the window.open method with the name containing non-word characters
909
+ var neim = name.replace(/\W/g, ""), container = document.getElementById(neim), win;
910
+
911
+ if (!container) {
912
+ container = document.createElement("div");
913
+ container.id = neim;
914
+ container.style.display = "none";
915
+ container.innerHTML = '<iframe name="' + neim + '" />';
916
+ document.body.appendChild(container);
917
+ }
918
+
919
+ win = container.firstChild.contentWindow;
920
+
921
+ return {
922
+ init: function () {
923
+ // Callbacks from different windows
924
+ win.callbacks = [listener];
925
+ // In IE 8 and less, only string argument can be safely passed to the function in other window
926
+ win.fire = function (string) {
927
+ var i;
928
+
929
+ for (i = 0; i < win.callbacks.length; i++) {
930
+ win.callbacks[i](string);
931
+ }
932
+ };
933
+ },
934
+ signal: function (type, data) {
935
+ if (!win.closed && win.fire) {
936
+ win.fire(atmosphere.util.stringifyJSON({
937
+ target: "c",
938
+ type: type,
939
+ data: data
940
+ }));
941
+ }
942
+ },
943
+ get: function (key) {
944
+ return !win.closed ? win[key] : null;
945
+ },
946
+ set: function (key, value) {
947
+ if (!win.closed) {
948
+ win[key] = value;
949
+ }
950
+ },
951
+ close: function () {
952
+ }
953
+ };
954
+ }
955
+ };
956
+
957
+ // Receives send and close command from the children
958
+ function listener(string) {
959
+ var command = atmosphere.util.parseJSON(string), data = command.data;
960
+
961
+ if (command.target === "p") {
962
+ switch (command.type) {
963
+ case "send":
964
+ _push(data);
965
+ break;
966
+ case "localSend":
967
+ _localMessage(data);
968
+ break;
969
+ case "close":
970
+ _close();
971
+ break;
972
+ }
973
+ }
974
+ }
975
+
976
+ _localSocketF = function propagateMessageEvent(context) {
977
+ storageService.signal("message", context);
978
+ };
979
+
980
+ function leaveTrace() {
981
+ document.cookie = _sharingKey + "=" +
982
+ // Opera's JSON implementation ignores a number whose a last digit of 0 strangely
983
+ // but has no problem with a number whose a last digit of 9 + 1
984
+ encodeURIComponent(atmosphere.util.stringifyJSON({
985
+ ts: atmosphere.util.now() + 1,
986
+ heir: (storageService.get("children") || [])[0]
987
+ })) + "; path=/";
988
+ }
989
+
990
+ // Chooses a storageService
991
+ storageService = servers.storage() || servers.windowref();
992
+ storageService.init();
993
+
994
+ if (_canLog('debug')) {
995
+ atmosphere.util.debug("Installed StorageService " + storageService);
996
+ }
997
+
998
+ // List of children sockets
999
+ storageService.set("children", []);
1000
+
1001
+ if (storageService.get("opened") != null && !storageService.get("opened")) {
1002
+ // Flag indicating the parent socket is opened
1003
+ storageService.set("opened", false);
1004
+ }
1005
+ // Leaves traces
1006
+ _sharingKey = encodeURIComponent(name);
1007
+ leaveTrace();
1008
+ _traceTimer = setInterval(leaveTrace, 1000);
1009
+
1010
+ _storageService = storageService;
1011
+ }
1012
+
1013
+ /**
1014
+ * @private
1015
+ */
1016
+ function _open(state, transport, request) {
1017
+ if (_request.shared && transport !== 'local') {
1018
+ share();
1019
+ }
1020
+
1021
+ if (_storageService != null) {
1022
+ _storageService.set("opened", true);
1023
+ }
1024
+
1025
+ request.close = function () {
1026
+ _close();
1027
+ };
1028
+
1029
+ if (_requestCount > 0 && state === 're-connecting') {
1030
+ request.isReopen = true;
1031
+ _tryingToReconnect(_response);
1032
+ } else if (_response.error == null) {
1033
+ _response.request = request;
1034
+ var prevState = _response.state;
1035
+ _response.state = state;
1036
+ var prevTransport = _response.transport;
1037
+ _response.transport = transport;
1038
+
1039
+ var _body = _response.responseBody;
1040
+ _invokeCallback();
1041
+ _response.responseBody = _body;
1042
+
1043
+ _response.state = prevState;
1044
+ _response.transport = prevTransport;
1045
+ }
1046
+ }
1047
+
1048
+ /**
1049
+ * Execute request using jsonp transport.
1050
+ *
1051
+ * @param request {Object} request Request parameters, if undefined _request object will be used.
1052
+ * @private
1053
+ */
1054
+ function _jsonp(request) {
1055
+ // When CORS is enabled, make sure we force the proper transport.
1056
+ request.transport = "jsonp";
1057
+
1058
+ var rq = _request, script;
1059
+ if ((request != null) && (typeof (request) !== 'undefined')) {
1060
+ rq = request;
1061
+ }
1062
+
1063
+ _jqxhr = {
1064
+ open: function () {
1065
+ var callback = "atmosphere" + (++guid);
1066
+
1067
+ function _reconnectOnFailure() {
1068
+ rq.lastIndex = 0;
1069
+
1070
+ if (rq.openId) {
1071
+ clearTimeout(rq.openId);
1072
+ }
1073
+
1074
+ if (rq.heartbeatTimer) {
1075
+ clearTimeout(rq.heartbeatTimer);
1076
+ }
1077
+
1078
+ if (rq.reconnect && _requestCount++ < rq.maxReconnectOnClose) {
1079
+ _open('re-connecting', rq.transport, rq);
1080
+ _reconnect(_jqxhr, rq, request.reconnectInterval);
1081
+ rq.openId = setTimeout(function () {
1082
+ _triggerOpen(rq);
1083
+ }, rq.reconnectInterval + 1000);
1084
+ } else {
1085
+ _onError(0, "maxReconnectOnClose reached");
1086
+ }
1087
+ }
1088
+
1089
+ function poll() {
1090
+ var url = rq.url;
1091
+ if (rq.dispatchUrl != null) {
1092
+ url += rq.dispatchUrl;
1093
+ }
1094
+
1095
+ var data = rq.data;
1096
+ if (rq.attachHeadersAsQueryString) {
1097
+ url = _attachHeaders(rq);
1098
+ if (data !== '') {
1099
+ url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data);
1100
+ }
1101
+ data = '';
1102
+ }
1103
+
1104
+ var head = document.head || document.getElementsByTagName("head")[0] || document.documentElement;
1105
+
1106
+ script = document.createElement("script");
1107
+ script.src = url + "&jsonpTransport=" + callback;
1108
+ //script.async = rq.async;
1109
+ script.clean = function () {
1110
+ script.clean = script.onerror = script.onload = script.onreadystatechange = null;
1111
+ if (script.parentNode) {
1112
+ script.parentNode.removeChild(script);
1113
+ }
1114
+
1115
+ if (++request.scriptCount === 2) {
1116
+ request.scriptCount = 1;
1117
+ _reconnectOnFailure();
1118
+ }
1119
+
1120
+ };
1121
+ script.onload = script.onreadystatechange = function () {
1122
+ _debug("jsonp.onload");
1123
+ if (!script.readyState || /loaded|complete/.test(script.readyState)) {
1124
+ script.clean();
1125
+ }
1126
+ };
1127
+
1128
+ script.onerror = function () {
1129
+ _debug("jsonp.onerror");
1130
+ request.scriptCount = 1;
1131
+ script.clean();
1132
+ };
1133
+
1134
+ head.insertBefore(script, head.firstChild);
1135
+ }
1136
+
1137
+ // Attaches callback
1138
+ window[callback] = function (msg) {
1139
+ _debug("jsonp.window");
1140
+ request.scriptCount = 0;
1141
+ if (rq.reconnect && rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest) {
1142
+
1143
+ // _readHeaders(_jqxhr, rq);
1144
+ if (!rq.executeCallbackBeforeReconnect) {
1145
+ _reconnect(_jqxhr, rq, rq.pollingInterval);
1146
+ }
1147
+
1148
+ if (msg != null && typeof msg !== 'string') {
1149
+ try {
1150
+ msg = msg.message;
1151
+ } catch (err) {
1152
+ // The message was partial
1153
+ }
1154
+ }
1155
+ var skipCallbackInvocation = _trackMessageSize(msg, rq, _response);
1156
+ if (!skipCallbackInvocation) {
1157
+ _prepareCallback(_response.responseBody, "messageReceived", 200, rq.transport);
1158
+ }
1159
+
1160
+ if (rq.executeCallbackBeforeReconnect) {
1161
+ _reconnect(_jqxhr, rq, rq.pollingInterval);
1162
+ }
1163
+ _timeout(rq);
1164
+ } else {
1165
+ atmosphere.util.log(_request.logLevel, ["JSONP reconnect maximum try reached " + _request.requestCount]);
1166
+ _onError(0, "maxRequest reached");
1167
+ }
1168
+ };
1169
+ setTimeout(function () {
1170
+ poll();
1171
+ }, 50);
1172
+ },
1173
+ abort: function () {
1174
+ if (script && script.clean) {
1175
+ script.clean();
1176
+ }
1177
+ }
1178
+ };
1179
+ _jqxhr.open();
1180
+ }
1181
+
1182
+ /**
1183
+ * Build websocket object.
1184
+ *
1185
+ * @param location {string} Web socket url.
1186
+ * @returns {websocket} Web socket object.
1187
+ * @private
1188
+ */
1189
+ function _getWebSocket(location) {
1190
+ if (_request.webSocketImpl != null) {
1191
+ return _request.webSocketImpl;
1192
+ } else {
1193
+ if (window.WebSocket) {
1194
+ return new WebSocket(location);
1195
+ } else {
1196
+ return new MozWebSocket(location);
1197
+ }
1198
+ }
1199
+ }
1200
+
1201
+ /**
1202
+ * Build web socket url from request url.
1203
+ *
1204
+ * @return {string} Web socket url (start with "ws" or "wss" for secure web socket).
1205
+ * @private
1206
+ */
1207
+ function _buildWebSocketUrl() {
1208
+ return _attachHeaders(_request, atmosphere.util.getAbsoluteURL(_request.webSocketUrl || _request.url)).replace(/^http/, "ws");
1209
+ }
1210
+
1211
+ /**
1212
+ * Build SSE url from request url.
1213
+ *
1214
+ * @return a url with Atmosphere's headers
1215
+ * @private
1216
+ */
1217
+ function _buildSSEUrl() {
1218
+ var url = _attachHeaders(_request);
1219
+ return url;
1220
+ }
1221
+
1222
+ /**
1223
+ * Open SSE. <br>
1224
+ * Automatically use fallback transport if SSE can't be opened.
1225
+ *
1226
+ * @private
1227
+ */
1228
+ function _executeSSE(sseOpened) {
1229
+
1230
+ _response.transport = "sse";
1231
+
1232
+ var location = _buildSSEUrl();
1233
+
1234
+ if (_canLog('debug')) {
1235
+ atmosphere.util.debug("Invoking executeSSE");
1236
+ atmosphere.util.debug("Using URL: " + location);
1237
+ }
1238
+
1239
+ if (sseOpened && !_request.reconnect) {
1240
+ if (_sse != null) {
1241
+ _clearState();
1242
+ }
1243
+ return;
1244
+ }
1245
+
1246
+ try {
1247
+ _sse = new EventSource(location, {
1248
+ withCredentials: _request.withCredentials
1249
+ });
1250
+ } catch (e) {
1251
+ _onError(0, e);
1252
+ _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending");
1253
+ return;
1254
+ }
1255
+
1256
+ if (_request.connectTimeout > 0) {
1257
+ _request.id = setTimeout(function () {
1258
+ if (!sseOpened) {
1259
+ _clearState();
1260
+ }
1261
+ }, _request.connectTimeout);
1262
+ }
1263
+
1264
+ _sse.onopen = function (event) {
1265
+ _debug("sse.onopen");
1266
+ _timeout(_request);
1267
+ if (_canLog('debug')) {
1268
+ atmosphere.util.debug("SSE successfully opened");
1269
+ }
1270
+
1271
+ if (!_request.enableProtocol) {
1272
+ if (!sseOpened) {
1273
+ _open('opening', "sse", _request);
1274
+ } else {
1275
+ _open('re-opening', "sse", _request);
1276
+ }
1277
+ } else if (_request.isReopen) {
1278
+ _request.isReopen = false;
1279
+ _open('re-opening', _request.transport, _request);
1280
+ }
1281
+
1282
+ sseOpened = true;
1283
+
1284
+ if (_request.method === 'POST') {
1285
+ _response.state = "messageReceived";
1286
+ _sse.send(_request.data);
1287
+ }
1288
+ };
1289
+
1290
+ _sse.onmessage = function (message) {
1291
+ _debug("sse.onmessage");
1292
+ _timeout(_request);
1293
+
1294
+ if (!_request.enableXDR && window.location.host && message.origin && message.origin !== window.location.protocol + "//" + window.location.host) {
1295
+ atmosphere.util.log(_request.logLevel, ["Origin was not " + window.location.protocol + "//" + window.location.host]);
1296
+ return;
1297
+ }
1298
+
1299
+ _response.state = 'messageReceived';
1300
+ _response.status = 200;
1301
+
1302
+ message = message.data;
1303
+ var skipCallbackInvocation = _trackMessageSize(message, _request, _response);
1304
+
1305
+ // https://github.com/remy/polyfills/blob/master/EventSource.js
1306
+ // Since we polling.
1307
+ /* if (_sse.URL) {
1308
+ _sse.interval = 100;
1309
+ _sse.URL = _buildSSEUrl();
1310
+ } */
1311
+
1312
+ if (!skipCallbackInvocation) {
1313
+ _invokeCallback();
1314
+ _response.responseBody = '';
1315
+ _response.messages = [];
1316
+ }
1317
+ };
1318
+
1319
+ _sse.onerror = function (message) {
1320
+ _debug("sse.onerror");
1321
+ clearTimeout(_request.id);
1322
+
1323
+ if (_request.heartbeatTimer) {
1324
+ clearTimeout(_request.heartbeatTimer);
1325
+ }
1326
+
1327
+ if (_response.closedByClientTimeout) {
1328
+ return;
1329
+ }
1330
+
1331
+ _invokeClose(sseOpened);
1332
+ _clearState();
1333
+
1334
+ if (_abortingConnection) {
1335
+ atmosphere.util.log(_request.logLevel, ["SSE closed normally"]);
1336
+ } else if (!sseOpened) {
1337
+ _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending");
1338
+ } else if (_request.reconnect && (_response.transport === 'sse')) {
1339
+ if (_requestCount++ < _request.maxReconnectOnClose) {
1340
+ _open('re-connecting', _request.transport, _request);
1341
+ if (_request.reconnectInterval > 0) {
1342
+ _request.reconnectId = setTimeout(function () {
1343
+ _executeSSE(true);
1344
+ }, _request.reconnectInterval);
1345
+ } else {
1346
+ _executeSSE(true);
1347
+ }
1348
+ _response.responseBody = "";
1349
+ _response.messages = [];
1350
+ } else {
1351
+ atmosphere.util.log(_request.logLevel, ["SSE reconnect maximum try reached " + _requestCount]);
1352
+ _onError(0, "maxReconnectOnClose reached");
1353
+ }
1354
+ }
1355
+ };
1356
+ }
1357
+
1358
+ /**
1359
+ * Open web socket. <br>
1360
+ * Automatically use fallback transport if web socket can't be opened.
1361
+ *
1362
+ * @private
1363
+ */
1364
+ function _executeWebSocket(webSocketOpened) {
1365
+
1366
+ _response.transport = "websocket";
1367
+
1368
+ var location = _buildWebSocketUrl(_request.url);
1369
+ if (_canLog('debug')) {
1370
+ atmosphere.util.debug("Invoking executeWebSocket, using URL: " + location);
1371
+ }
1372
+
1373
+ if (webSocketOpened && !_request.reconnect) {
1374
+ if (_websocket != null) {
1375
+ _clearState();
1376
+ }
1377
+ return;
1378
+ }
1379
+
1380
+ _websocket = _getWebSocket(location);
1381
+ if (_request.webSocketBinaryType != null) {
1382
+ _websocket.binaryType = _request.webSocketBinaryType;
1383
+ }
1384
+
1385
+ if (_request.connectTimeout > 0) {
1386
+ _request.id = setTimeout(function () {
1387
+ if (!webSocketOpened) {
1388
+ var _message = {
1389
+ code: 1002,
1390
+ reason: "",
1391
+ wasClean: false
1392
+ };
1393
+ _websocket.onclose(_message);
1394
+ // Close it anyway
1395
+ try {
1396
+ _clearState();
1397
+ } catch (e) {
1398
+ }
1399
+ return;
1400
+ }
1401
+
1402
+ }, _request.connectTimeout);
1403
+ }
1404
+
1405
+ _websocket.onopen = function (message) {
1406
+ _debug("websocket.onopen");
1407
+ _timeout(_request);
1408
+ offline = false;
1409
+
1410
+ if (_canLog('debug')) {
1411
+ atmosphere.util.debug("Websocket successfully opened");
1412
+ }
1413
+
1414
+ var reopening = webSocketOpened;
1415
+
1416
+ if (_websocket != null) {
1417
+ _websocket.canSendMessage = true;
1418
+ }
1419
+
1420
+ if (!_request.enableProtocol) {
1421
+ webSocketOpened = true;
1422
+ if (reopening) {
1423
+ _open('re-opening', "websocket", _request);
1424
+ } else {
1425
+ _open('opening', "websocket", _request);
1426
+ }
1427
+ }
1428
+
1429
+ if (_websocket != null) {
1430
+ if (_request.method === 'POST') {
1431
+ _response.state = "messageReceived";
1432
+ _websocket.send(_request.data);
1433
+ }
1434
+ }
1435
+ };
1436
+
1437
+ _websocket.onmessage = function (message) {
1438
+ _debug("websocket.onmessage");
1439
+ _timeout(_request);
1440
+
1441
+ // We only consider it opened if we get the handshake data
1442
+ // https://github.com/Atmosphere/atmosphere-javascript/issues/74
1443
+ if (_request.enableProtocol) {
1444
+ webSocketOpened = true;
1445
+ }
1446
+
1447
+ _response.state = 'messageReceived';
1448
+ _response.status = 200;
1449
+
1450
+ message = message.data;
1451
+ var isString = typeof (message) === 'string';
1452
+ if (isString) {
1453
+ var skipCallbackInvocation = _trackMessageSize(message, _request, _response);
1454
+ if (!skipCallbackInvocation) {
1455
+ _invokeCallback();
1456
+ _response.responseBody = '';
1457
+ _response.messages = [];
1458
+ }
1459
+ } else {
1460
+ message = _handleProtocol(_request, message);
1461
+ if (message === "")
1462
+ return;
1463
+
1464
+ _response.responseBody = message;
1465
+ _invokeCallback();
1466
+ _response.responseBody = null;
1467
+ }
1468
+ };
1469
+
1470
+ _websocket.onerror = function (message) {
1471
+ _debug("websocket.onerror");
1472
+ clearTimeout(_request.id);
1473
+
1474
+ if (_request.heartbeatTimer) {
1475
+ clearTimeout(_request.heartbeatTimer);
1476
+ }
1477
+ };
1478
+
1479
+ _websocket.onclose = function (message) {
1480
+ _debug("websocket.onclose");
1481
+ clearTimeout(_request.id);
1482
+ if (_response.state === 'closed')
1483
+ return;
1484
+
1485
+ var reason = message.reason;
1486
+ if (reason === "") {
1487
+ switch (message.code) {
1488
+ case 1000:
1489
+ reason = "Normal closure; the connection successfully completed whatever purpose for which it was created.";
1490
+ break;
1491
+ case 1001:
1492
+ reason = "The endpoint is going away, either because of a server failure or because the "
1493
+ + "browser is navigating away from the page that opened the connection.";
1494
+ break;
1495
+ case 1002:
1496
+ reason = "The endpoint is terminating the connection due to a protocol error.";
1497
+ break;
1498
+ case 1003:
1499
+ reason = "The connection is being terminated because the endpoint received data of a type it "
1500
+ + "cannot accept (for example, a text-only endpoint received binary data).";
1501
+ break;
1502
+ case 1004:
1503
+ reason = "The endpoint is terminating the connection because a data frame was received that is too large.";
1504
+ break;
1505
+ case 1005:
1506
+ reason = "Unknown: no status code was provided even though one was expected.";
1507
+ break;
1508
+ case 1006:
1509
+ reason = "Connection was closed abnormally (that is, with no close frame being sent).";
1510
+ break;
1511
+ }
1512
+ }
1513
+
1514
+ if (_canLog('warn')) {
1515
+ atmosphere.util.warn("Websocket closed, reason: " + reason + ' - wasClean: ' + message.wasClean);
1516
+ }
1517
+
1518
+ if (_response.closedByClientTimeout || (_request.handleOnlineOffline && offline)) {
1519
+ // IFF online/offline events are handled and we happen to be offline, we stop all reconnect attempts and
1520
+ // resume them in the "online" event (if we get here in that case, something else went wrong as the
1521
+ // offline handler should stop any reconnect attempt).
1522
+ //
1523
+ // On the other hand, if we DO NOT handle online/offline events, we continue as before with reconnecting
1524
+ // even if we are offline. Failing to do so would stop all reconnect attemps forever.
1525
+ if (_request.reconnectId) {
1526
+ clearTimeout(_request.reconnectId);
1527
+ delete _request.reconnectId;
1528
+ }
1529
+ return;
1530
+ }
1531
+
1532
+ _invokeClose(webSocketOpened);
1533
+
1534
+ _response.state = 'closed';
1535
+
1536
+ if (_abortingConnection) {
1537
+ atmosphere.util.log(_request.logLevel, ["Websocket closed normally"]);
1538
+ } else if (!webSocketOpened) {
1539
+ _reconnectWithFallbackTransport("Websocket failed on first connection attempt. Downgrading to " + _request.fallbackTransport + " and resending");
1540
+
1541
+ } else if (_request.reconnect && _response.transport === 'websocket' ) {
1542
+ _clearState();
1543
+ if (_requestCount++ < _request.maxReconnectOnClose) {
1544
+ _open('re-connecting', _request.transport, _request);
1545
+ if (_request.reconnectInterval > 0) {
1546
+ _request.reconnectId = setTimeout(function () {
1547
+ _response.responseBody = "";
1548
+ _response.messages = [];
1549
+ _executeWebSocket(true);
1550
+ }, _request.reconnectInterval);
1551
+ } else {
1552
+ _response.responseBody = "";
1553
+ _response.messages = [];
1554
+ _executeWebSocket(true);
1555
+ }
1556
+ } else {
1557
+ atmosphere.util.log(_request.logLevel, ["Websocket reconnect maximum try reached " + _requestCount]);
1558
+ if (_canLog('warn')) {
1559
+ atmosphere.util.warn("Websocket error, reason: " + message.reason);
1560
+ }
1561
+ _onError(0, "maxReconnectOnClose reached");
1562
+ }
1563
+ }
1564
+ };
1565
+
1566
+ var ua = navigator.userAgent.toLowerCase();
1567
+ var isAndroid = ua.indexOf("android") > -1;
1568
+ if (isAndroid && _websocket.url === undefined) {
1569
+ // Android 4.1 does not really support websockets and fails silently
1570
+ _websocket.onclose({
1571
+ reason: "Android 4.1 does not support websockets.",
1572
+ wasClean: false
1573
+ });
1574
+ }
1575
+ }
1576
+
1577
+ function _handleProtocol(request, message) {
1578
+
1579
+ var nMessage = message;
1580
+ if (request.transport === 'polling') return nMessage;
1581
+
1582
+ if (request.enableProtocol && request.firstMessage && atmosphere.util.trim(message).length !== 0) {
1583
+ var pos = request.trackMessageLength ? 1 : 0;
1584
+ var messages = message.split(request.messageDelimiter);
1585
+
1586
+ if (messages.length <= pos + 1) {
1587
+ // Something went wrong, normally with IE or when a message is written before the
1588
+ // handshake has been received.
1589
+ return nMessage;
1590
+ }
1591
+
1592
+ request.firstMessage = false;
1593
+ request.uuid = atmosphere.util.trim(messages[pos]);
1594
+
1595
+ if (messages.length <= pos + 2) {
1596
+ atmosphere.util.log('error', ["Protocol data not sent by the server. " +
1597
+ "If you enable protocol on client side, be sure to install JavascriptProtocol interceptor on server side." +
1598
+ "Also note that atmosphere-runtime 2.2+ should be used."]);
1599
+ }
1600
+
1601
+ _heartbeatInterval = parseInt(atmosphere.util.trim(messages[pos + 1]), 10);
1602
+ _heartbeatPadding = messages[pos + 2];
1603
+
1604
+ if (request.transport !== 'long-polling') {
1605
+ _triggerOpen(request);
1606
+ }
1607
+ uuid = request.uuid;
1608
+ nMessage = "";
1609
+
1610
+ // We have trailing messages
1611
+ pos = request.trackMessageLength ? 4 : 3;
1612
+ if (messages.length > pos + 1) {
1613
+ for (var i = pos; i < messages.length; i++) {
1614
+ nMessage += messages[i];
1615
+ if (i + 1 !== messages.length) {
1616
+ nMessage += request.messageDelimiter;
1617
+ }
1618
+ }
1619
+ }
1620
+
1621
+ if (request.ackInterval !== 0) {
1622
+ setTimeout(function () {
1623
+ _push("...ACK...");
1624
+ }, request.ackInterval);
1625
+ }
1626
+ } else if (request.enableProtocol && request.firstMessage && atmosphere.util.browser.msie && +atmosphere.util.browser.version.split(".")[0] < 10) {
1627
+ // In case we are getting some junk from IE
1628
+ atmosphere.util.log(_request.logLevel, ["Receiving unexpected data from IE"]);
1629
+ } else {
1630
+ _triggerOpen(request);
1631
+ }
1632
+ return nMessage;
1633
+ }
1634
+
1635
+ function _timeout(_request) {
1636
+ clearTimeout(_request.id);
1637
+ if (_request.timeout > 0 && _request.transport !== 'polling') {
1638
+ _request.id = setTimeout(function () {
1639
+ _onClientTimeout(_request);
1640
+ _disconnect();
1641
+ _clearState();
1642
+ }, _request.timeout);
1643
+ }
1644
+ }
1645
+
1646
+ function _onClientTimeout(_request) {
1647
+ _response.closedByClientTimeout = true;
1648
+ _response.state = 'closedByClient';
1649
+ _response.responseBody = "";
1650
+ _response.status = 408;
1651
+ _response.messages = [];
1652
+ _invokeCallback();
1653
+ }
1654
+
1655
+ function _onError(code, reason) {
1656
+ _clearState();
1657
+ clearTimeout(_request.id);
1658
+ _response.state = 'error';
1659
+ _response.reasonPhrase = reason;
1660
+ _response.responseBody = "";
1661
+ _response.status = code;
1662
+ _response.messages = [];
1663
+ _invokeCallback();
1664
+ }
1665
+
1666
+ /**
1667
+ * Track received message and make sure callbacks/functions are only invoked when the complete message has been received.
1668
+ *
1669
+ * @param message
1670
+ * @param request
1671
+ * @param response
1672
+ */
1673
+ function _trackMessageSize(message, request, response) {
1674
+ message = _handleProtocol(request, message);
1675
+ if (message.length === 0)
1676
+ return true;
1677
+
1678
+ response.responseBody = message;
1679
+
1680
+ if (request.trackMessageLength) {
1681
+ // prepend partialMessage if any
1682
+ message = response.partialMessage + message;
1683
+
1684
+ var messages = [];
1685
+ var messageStart = message.indexOf(request.messageDelimiter);
1686
+ if (messageStart != -1) {
1687
+ while (messageStart !== -1) {
1688
+ var str = message.substring(0, messageStart);
1689
+ var messageLength = +str;
1690
+ if (isNaN(messageLength))
1691
+ throw new Error('message length "' + str + '" is not a number');
1692
+ messageStart += request.messageDelimiter.length;
1693
+ if (messageStart + messageLength > message.length) {
1694
+ // message not complete, so there is no trailing messageDelimiter
1695
+ messageStart = -1;
1696
+ } else {
1697
+ // message complete, so add it
1698
+ messages.push(message.substring(messageStart, messageStart + messageLength));
1699
+ // remove consumed characters
1700
+ message = message.substring(messageStart + messageLength, message.length);
1701
+ messageStart = message.indexOf(request.messageDelimiter);
1702
+ }
1703
+ }
1704
+
1705
+ /* keep any remaining data */
1706
+ response.partialMessage = message;
1707
+
1708
+ if (messages.length !== 0) {
1709
+ response.responseBody = messages.join(request.messageDelimiter);
1710
+ response.messages = messages;
1711
+ return false;
1712
+ } else {
1713
+ response.responseBody = "";
1714
+ response.messages = [];
1715
+ return true;
1716
+ }
1717
+ }
1718
+ }
1719
+ response.responseBody = message;
1720
+ response.messages = [message];
1721
+ return false;
1722
+ }
1723
+
1724
+ /**
1725
+ * Reconnect request with fallback transport. <br>
1726
+ * Used in case websocket can't be opened.
1727
+ *
1728
+ * @private
1729
+ */
1730
+ function _reconnectWithFallbackTransport(errorMessage) {
1731
+ atmosphere.util.log(_request.logLevel, [errorMessage]);
1732
+
1733
+ if (typeof (_request.onTransportFailure) !== 'undefined') {
1734
+ _request.onTransportFailure(errorMessage, _request);
1735
+ } else if (typeof (atmosphere.util.onTransportFailure) !== 'undefined') {
1736
+ atmosphere.util.onTransportFailure(errorMessage, _request);
1737
+ }
1738
+
1739
+ _request.transport = _request.fallbackTransport;
1740
+ var reconnectInterval = _request.connectTimeout === -1 ? 0 : _request.connectTimeout;
1741
+ if (_request.reconnect && _request.transport !== 'none' || _request.transport == null) {
1742
+ _request.method = _request.fallbackMethod;
1743
+ _response.transport = _request.fallbackTransport;
1744
+ _request.fallbackTransport = 'none';
1745
+ if (reconnectInterval > 0) {
1746
+ _request.reconnectId = setTimeout(function () {
1747
+ _execute();
1748
+ }, reconnectInterval);
1749
+ } else {
1750
+ _execute();
1751
+ }
1752
+ } else {
1753
+ _onError(500, "Unable to reconnect with fallback transport");
1754
+ }
1755
+ }
1756
+
1757
+ /**
1758
+ * Get url from request and attach headers to it.
1759
+ *
1760
+ * @param request {Object} request Request parameters, if undefined _request object will be used.
1761
+ *
1762
+ * @returns {Object} Request object, if undefined, _request object will be used.
1763
+ * @private
1764
+ */
1765
+ function _attachHeaders(request, url) {
1766
+ var rq = _request;
1767
+ if ((request != null) && (typeof (request) !== 'undefined')) {
1768
+ rq = request;
1769
+ }
1770
+
1771
+ if (url == null) {
1772
+ url = rq.url;
1773
+ }
1774
+
1775
+ // If not enabled
1776
+ if (!rq.attachHeadersAsQueryString)
1777
+ return url;
1778
+
1779
+ // If already added
1780
+ if (url.indexOf("X-Atmosphere-Framework") !== -1) {
1781
+ return url;
1782
+ }
1783
+
1784
+ url += (url.indexOf('?') !== -1) ? '&' : '?';
1785
+ url += "X-Atmosphere-tracking-id=" + rq.uuid;
1786
+ url += "&X-Atmosphere-Framework=" + atmosphere.version;
1787
+ url += "&X-Atmosphere-Transport=" + rq.transport;
1788
+
1789
+ if (rq.trackMessageLength) {
1790
+ url += "&X-Atmosphere-TrackMessageSize=" + "true";
1791
+ }
1792
+
1793
+ if (rq.heartbeat !== null && rq.heartbeat.server !== null) {
1794
+ url += "&X-Heartbeat-Server=" + rq.heartbeat.server;
1795
+ }
1796
+
1797
+ if (rq.contentType !== '') {
1798
+ //Eurk!
1799
+ url += "&Content-Type=" + (rq.transport === 'websocket' ? rq.contentType : encodeURIComponent(rq.contentType));
1800
+ }
1801
+
1802
+ if (rq.enableProtocol) {
1803
+ url += "&X-atmo-protocol=true";
1804
+ }
1805
+
1806
+ atmosphere.util.each(rq.headers, function (name, value) {
1807
+ var h = atmosphere.util.isFunction(value) ? value.call(this, rq, request, _response) : value;
1808
+ if (h != null) {
1809
+ url += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(h);
1810
+ }
1811
+ });
1812
+
1813
+ return url;
1814
+ }
1815
+
1816
+ function _triggerOpen(rq) {
1817
+ if (!rq.isOpen) {
1818
+ rq.isOpen = true;
1819
+ _open('opening', rq.transport, rq);
1820
+ } else if (rq.isReopen) {
1821
+ rq.isReopen = false;
1822
+ _open('re-opening', rq.transport, rq);
1823
+ } else if (_response.state === 'messageReceived' && (rq.transport === 'jsonp' || rq.transport === 'long-polling')) {
1824
+ _openAfterResume(_response);
1825
+ } else {
1826
+ return;
1827
+ }
1828
+
1829
+ _startHeartbeat(rq);
1830
+ }
1831
+
1832
+ function _startHeartbeat(rq) {
1833
+ if (rq.heartbeatTimer != null) {
1834
+ clearTimeout(rq.heartbeatTimer);
1835
+ }
1836
+
1837
+ if (!isNaN(_heartbeatInterval) && _heartbeatInterval > 0) {
1838
+ var _pushHeartbeat = function () {
1839
+ if (_canLog('debug')) {
1840
+ atmosphere.util.debug("Sending heartbeat");
1841
+ }
1842
+ _push(_heartbeatPadding);
1843
+ rq.heartbeatTimer = setTimeout(_pushHeartbeat, _heartbeatInterval);
1844
+ };
1845
+ rq.heartbeatTimer = setTimeout(_pushHeartbeat, _heartbeatInterval);
1846
+ }
1847
+ }
1848
+
1849
+ /**
1850
+ * Execute ajax request. <br>
1851
+ *
1852
+ * @param request {Object} request Request parameters, if undefined _request object will be used.
1853
+ * @private
1854
+ */
1855
+ function _executeRequest(request) {
1856
+ var rq = _request;
1857
+ if ((request != null) || (typeof (request) !== 'undefined')) {
1858
+ rq = request;
1859
+ }
1860
+
1861
+ rq.lastIndex = 0;
1862
+ rq.readyState = 0;
1863
+
1864
+ // CORS fake using JSONP
1865
+ if ((rq.transport === 'jsonp') || ((rq.enableXDR) && (atmosphere.util.checkCORSSupport()))) {
1866
+ _jsonp(rq);
1867
+ return;
1868
+ }
1869
+
1870
+ if (atmosphere.util.browser.msie && +atmosphere.util.browser.version.split(".")[0] < 10) {
1871
+ if ((rq.transport === 'streaming')) {
1872
+ if (rq.enableXDR && window.XDomainRequest) {
1873
+ _ieXDR(rq);
1874
+ } else {
1875
+ _ieStreaming(rq);
1876
+ }
1877
+ return;
1878
+ }
1879
+
1880
+ if ((rq.enableXDR) && (window.XDomainRequest)) {
1881
+ _ieXDR(rq);
1882
+ return;
1883
+ }
1884
+ }
1885
+
1886
+ var reconnectFExec = function (force) {
1887
+ rq.lastIndex = 0;
1888
+ _requestCount++; // Increase also when forcing reconnect as _open checks _requestCount
1889
+ if (force || (rq.reconnect && _requestCount <= rq.maxReconnectOnClose)) {
1890
+ var delay = force ? 0 : request.reconnectInterval; // Reconnect immediately if the server resumed the connection (timeout)
1891
+ _response.ffTryingReconnect = true;
1892
+ _open('re-connecting', request.transport, request);
1893
+ _reconnect(ajaxRequest, rq, delay);
1894
+ } else {
1895
+ _onError(0, "maxReconnectOnClose reached");
1896
+ }
1897
+ };
1898
+
1899
+ var reconnectF = function (force){
1900
+ if(atmosphere._beforeUnloadState){
1901
+ // ATMOSPHERE-JAVASCRIPT-143: Delay reconnect to avoid reconnect attempts before an actual unload (we don't know if an unload will happen, yet)
1902
+ atmosphere.util.debug(new Date() + " Atmosphere: reconnectF: execution delayed due to _beforeUnloadState flag");
1903
+ setTimeout(function () {
1904
+ reconnectFExec(force);
1905
+ }, 5000);
1906
+ }else {
1907
+ reconnectFExec(force);
1908
+ }
1909
+ };
1910
+
1911
+ var disconnected = function () {
1912
+ // Prevent onerror callback to be called
1913
+ _response.errorHandled = true;
1914
+ _clearState();
1915
+ reconnectF(false);
1916
+ };
1917
+
1918
+ if (rq.force || (rq.reconnect && (rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest))) {
1919
+ rq.force = false;
1920
+
1921
+ var ajaxRequest = atmosphere.util.xhr();
1922
+ ajaxRequest.hasData = false;
1923
+
1924
+ _doRequest(ajaxRequest, rq, true);
1925
+
1926
+ if (rq.suspend) {
1927
+ _activeRequest = ajaxRequest;
1928
+ }
1929
+
1930
+ if (rq.transport !== 'polling') {
1931
+ _response.transport = rq.transport;
1932
+
1933
+ ajaxRequest.onabort = function () {
1934
+ _debug("ajaxrequest.onabort")
1935
+ _invokeClose(true);
1936
+ };
1937
+
1938
+ ajaxRequest.onerror = function () {
1939
+ _debug("ajaxrequest.onerror")
1940
+ _response.error = true;
1941
+ _response.ffTryingReconnect = true;
1942
+ try {
1943
+ _response.status = XMLHttpRequest.status;
1944
+ } catch (e) {
1945
+ _response.status = 500;
1946
+ }
1947
+
1948
+ if (!_response.status) {
1949
+ _response.status = 500;
1950
+ }
1951
+ if (!_response.errorHandled) {
1952
+ _clearState();
1953
+ reconnectF(false);
1954
+ }
1955
+ };
1956
+ }
1957
+
1958
+ ajaxRequest.onreadystatechange = function () {
1959
+ _debug("ajaxRequest.onreadystatechange, new state: " + ajaxRequest.readyState);
1960
+ if (_abortingConnection) {
1961
+ _debug("onreadystatechange has been ignored due to _abortingConnection flag");
1962
+ return;
1963
+ }
1964
+
1965
+ _response.error = null;
1966
+ var skipCallbackInvocation = false;
1967
+ var update = false;
1968
+
1969
+ if (rq.transport === 'streaming' && rq.readyState > 2 && ajaxRequest.readyState === 4) {
1970
+ _clearState();
1971
+ reconnectF(false);
1972
+ return;
1973
+ }
1974
+
1975
+ rq.readyState = ajaxRequest.readyState;
1976
+
1977
+ if (rq.transport === 'streaming' && ajaxRequest.readyState >= 3) {
1978
+ update = true;
1979
+ } else if (rq.transport === 'long-polling' && ajaxRequest.readyState === 4) {
1980
+ update = true;
1981
+ }
1982
+ _timeout(_request);
1983
+
1984
+ if (rq.transport !== 'polling') {
1985
+ // MSIE 9 and lower status can be higher than 1000, Chrome can be 0
1986
+ var status = 200;
1987
+ if (ajaxRequest.readyState === 4) {
1988
+ status = ajaxRequest.status > 1000 ? 0 : ajaxRequest.status;
1989
+ }
1990
+
1991
+ if (!rq.reconnectOnServerError && (status >= 300 && status < 600)) {
1992
+ _onError(status, ajaxRequest.statusText);
1993
+ return;
1994
+ }
1995
+
1996
+ if (status >= 300 || status === 0) {
1997
+ disconnected();
1998
+ return;
1999
+ }
2000
+
2001
+ // Firefox incorrectly send statechange 0->2 when a reconnect attempt fails. The above checks ensure that onopen is not called for these
2002
+ if ((!rq.enableProtocol || !request.firstMessage) && ajaxRequest.readyState === 2) {
2003
+ // Firefox incorrectly send statechange 0->2 when a reconnect attempt fails. The above checks ensure that onopen is not called for these
2004
+ // In that case, ajaxRequest.onerror will be called just after onreadystatechange is called, so we delay the trigger until we are
2005
+ // guarantee the connection is well established.
2006
+ if (atmosphere.util.browser.mozilla && _response.ffTryingReconnect) {
2007
+ _response.ffTryingReconnect = false;
2008
+ setTimeout(function () {
2009
+ if (!_response.ffTryingReconnect) {
2010
+ _triggerOpen(rq);
2011
+ }
2012
+ }, 500);
2013
+ } else {
2014
+ _triggerOpen(rq);
2015
+ }
2016
+ }
2017
+
2018
+ } else if (ajaxRequest.readyState === 4) {
2019
+ update = true;
2020
+ }
2021
+
2022
+ if (update) {
2023
+ var responseText = ajaxRequest.responseText;
2024
+ _response.errorHandled = false;
2025
+
2026
+ // IE behave the same way when resuming long-polling or when the server goes down.
2027
+ if (rq.transport === 'long-polling' && atmosphere.util.trim(responseText).length === 0) {
2028
+ // For browser that aren't support onabort
2029
+ if (!ajaxRequest.hasData) {
2030
+ reconnectF(true);
2031
+ } else {
2032
+ ajaxRequest.hasData = false;
2033
+ }
2034
+ return;
2035
+ }
2036
+ ajaxRequest.hasData = true;
2037
+
2038
+ _readHeaders(ajaxRequest, _request);
2039
+
2040
+ if (rq.transport === 'streaming') {
2041
+ if (!atmosphere.util.browser.opera) {
2042
+ var message = responseText.substring(rq.lastIndex, responseText.length);
2043
+ skipCallbackInvocation = _trackMessageSize(message, rq, _response);
2044
+
2045
+ rq.lastIndex = responseText.length;
2046
+ if (skipCallbackInvocation) {
2047
+ return;
2048
+ }
2049
+ } else {
2050
+ atmosphere.util.iterate(function () {
2051
+ if (_response.status !== 500 && ajaxRequest.responseText.length > rq.lastIndex) {
2052
+ try {
2053
+ _response.status = ajaxRequest.status;
2054
+ _response.headers = atmosphere.util.parseHeaders(ajaxRequest.getAllResponseHeaders());
2055
+
2056
+ _readHeaders(ajaxRequest, _request);
2057
+
2058
+ } catch (e) {
2059
+ _response.status = 404;
2060
+ }
2061
+ _timeout(_request);
2062
+
2063
+ _response.state = "messageReceived";
2064
+ var message = ajaxRequest.responseText.substring(rq.lastIndex);
2065
+ rq.lastIndex = ajaxRequest.responseText.length;
2066
+
2067
+ skipCallbackInvocation = _trackMessageSize(message, rq, _response);
2068
+ if (!skipCallbackInvocation) {
2069
+ _invokeCallback();
2070
+ }
2071
+
2072
+ if (_verifyStreamingLength(ajaxRequest, rq)) {
2073
+ _reconnectOnMaxStreamingLength(ajaxRequest, rq);
2074
+ return;
2075
+ }
2076
+ } else if (_response.status > 400) {
2077
+ // Prevent replaying the last message.
2078
+ rq.lastIndex = ajaxRequest.responseText.length;
2079
+ return false;
2080
+ }
2081
+ }, 0);
2082
+ }
2083
+ } else {
2084
+ skipCallbackInvocation = _trackMessageSize(responseText, rq, _response);
2085
+ }
2086
+ var closeStream = _verifyStreamingLength(ajaxRequest, rq);
2087
+
2088
+ try {
2089
+ _response.status = ajaxRequest.status;
2090
+ _response.headers = atmosphere.util.parseHeaders(ajaxRequest.getAllResponseHeaders());
2091
+
2092
+ _readHeaders(ajaxRequest, rq);
2093
+ } catch (e) {
2094
+ _response.status = 404;
2095
+ }
2096
+
2097
+ if (rq.suspend) {
2098
+ _response.state = _response.status === 0 ? "closed" : "messageReceived";
2099
+ } else {
2100
+ _response.state = "messagePublished";
2101
+ }
2102
+
2103
+ var isAllowedToReconnect = !closeStream && request.transport !== 'streaming' && request.transport !== 'polling';
2104
+ if (isAllowedToReconnect && !rq.executeCallbackBeforeReconnect) {
2105
+ _reconnect(ajaxRequest, rq, rq.pollingInterval);
2106
+ }
2107
+
2108
+ if (_response.responseBody.length !== 0 && !skipCallbackInvocation)
2109
+ _invokeCallback();
2110
+
2111
+ if (isAllowedToReconnect && rq.executeCallbackBeforeReconnect) {
2112
+ _reconnect(ajaxRequest, rq, rq.pollingInterval);
2113
+ }
2114
+
2115
+ if (closeStream) {
2116
+ _reconnectOnMaxStreamingLength(ajaxRequest, rq);
2117
+ }
2118
+ }
2119
+ };
2120
+
2121
+ try {
2122
+ ajaxRequest.send(rq.data);
2123
+ _subscribed = true;
2124
+ } catch (e) {
2125
+ atmosphere.util.log(rq.logLevel, ["Unable to connect to " + rq.url]);
2126
+ _onError(0, e);
2127
+ }
2128
+
2129
+ } else {
2130
+ if (rq.logLevel === 'debug') {
2131
+ atmosphere.util.log(rq.logLevel, ["Max re-connection reached."]);
2132
+ }
2133
+ _onError(0, "maxRequest reached");
2134
+ }
2135
+ }
2136
+
2137
+ function _reconnectOnMaxStreamingLength(ajaxRequest, rq) {
2138
+ _response.messages = [];
2139
+ rq.isReopen = true;
2140
+ _close();
2141
+ _abortingConnection = false;
2142
+ _reconnect(ajaxRequest, rq, 500);
2143
+ }
2144
+
2145
+ /**
2146
+ * Do ajax request.
2147
+ *
2148
+ * @param ajaxRequest Ajax request.
2149
+ * @param request Request parameters.
2150
+ * @param create If ajax request has to be open.
2151
+ */
2152
+ function _doRequest(ajaxRequest, request, create) {
2153
+ // Prevent Android to cache request
2154
+ var url = request.url;
2155
+ if (request.dispatchUrl != null && request.method === 'POST') {
2156
+ url += request.dispatchUrl;
2157
+ }
2158
+ url = _attachHeaders(request, url);
2159
+ url = atmosphere.util.prepareURL(url);
2160
+
2161
+ if (create) {
2162
+ ajaxRequest.open(request.method, url, request.async);
2163
+ if (request.connectTimeout > 0) {
2164
+ request.id = setTimeout(function () {
2165
+ if (request.requestCount === 0) {
2166
+ _clearState();
2167
+ _prepareCallback("Connect timeout", "closed", 200, request.transport);
2168
+ }
2169
+ }, request.connectTimeout);
2170
+ }
2171
+ }
2172
+
2173
+ if (_request.withCredentials && _request.transport !== 'websocket') {
2174
+ if ("withCredentials" in ajaxRequest) {
2175
+ ajaxRequest.withCredentials = true;
2176
+ }
2177
+ }
2178
+
2179
+ if (!_request.dropHeaders) {
2180
+ ajaxRequest.setRequestHeader("X-Atmosphere-Framework", atmosphere.version);
2181
+ ajaxRequest.setRequestHeader("X-Atmosphere-Transport", request.transport);
2182
+
2183
+ if (request.heartbeat !== null && request.heartbeat.server !== null) {
2184
+ ajaxRequest.setRequestHeader("X-Heartbeat-Server", ajaxRequest.heartbeat.server);
2185
+ }
2186
+
2187
+ if (request.trackMessageLength) {
2188
+ ajaxRequest.setRequestHeader("X-Atmosphere-TrackMessageSize", "true");
2189
+ }
2190
+ ajaxRequest.setRequestHeader("X-Atmosphere-tracking-id", request.uuid);
2191
+
2192
+ atmosphere.util.each(request.headers, function (name, value) {
2193
+ var h = atmosphere.util.isFunction(value) ? value.call(this, ajaxRequest, request, create, _response) : value;
2194
+ if (h != null) {
2195
+ ajaxRequest.setRequestHeader(name, h);
2196
+ }
2197
+ });
2198
+ }
2199
+
2200
+ if (request.contentType !== '') {
2201
+ ajaxRequest.setRequestHeader("Content-Type", request.contentType);
2202
+ }
2203
+ }
2204
+
2205
+ function _reconnect(ajaxRequest, request, delay) {
2206
+
2207
+ if (_response.closedByClientTimeout) {
2208
+ return;
2209
+ }
2210
+
2211
+ if (request.reconnect || (request.suspend && _subscribed)) {
2212
+ var status = 0;
2213
+ if (ajaxRequest && ajaxRequest.readyState > 1) {
2214
+ status = ajaxRequest.status > 1000 ? 0 : ajaxRequest.status;
2215
+ }
2216
+ _response.status = status === 0 ? 204 : status;
2217
+ _response.reason = status === 0 ? "Server resumed the connection or down." : "OK";
2218
+
2219
+ clearTimeout(request.id);
2220
+ if (request.reconnectId) {
2221
+ clearTimeout(request.reconnectId);
2222
+ delete request.reconnectId;
2223
+ }
2224
+
2225
+ if (delay > 0) {
2226
+ // For whatever reason, never cancel a reconnect timeout as it is mandatory to reconnect.
2227
+ _request.reconnectId = setTimeout(function () {
2228
+ _executeRequest(request);
2229
+ }, delay);
2230
+ } else {
2231
+ _executeRequest(request);
2232
+ }
2233
+ }
2234
+ }
2235
+
2236
+ function _tryingToReconnect(response) {
2237
+ response.state = 're-connecting';
2238
+ _invokeFunction(response);
2239
+ }
2240
+
2241
+ function _openAfterResume(response) {
2242
+ response.state = 'openAfterResume';
2243
+ _invokeFunction(response);
2244
+ response.state = 'messageReceived';
2245
+ }
2246
+
2247
+ function _ieXDR(request) {
2248
+ if (request.transport !== "polling") {
2249
+ _ieStream = _configureXDR(request);
2250
+ _ieStream.open();
2251
+ } else {
2252
+ _configureXDR(request).open();
2253
+ }
2254
+ }
2255
+
2256
+ function _configureXDR(request) {
2257
+ var rq = _request;
2258
+ if ((request != null) && (typeof (request) !== 'undefined')) {
2259
+ rq = request;
2260
+ }
2261
+
2262
+ var transport = rq.transport;
2263
+ var lastIndex = 0;
2264
+ var xdr = new window.XDomainRequest();
2265
+ var reconnect = function () {
2266
+ if (rq.transport === "long-polling" && (rq.reconnect && (rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest))) {
2267
+ xdr.status = 200;
2268
+ _ieXDR(rq);
2269
+ }
2270
+ };
2271
+
2272
+ var rewriteURL = rq.rewriteURL || function (url) {
2273
+ // Maintaining session by rewriting URL
2274
+ // http://stackoverflow.com/questions/6453779/maintaining-session-by-rewriting-url
2275
+ var match = /(?:^|;\s*)(JSESSIONID|PHPSESSID)=([^;]*)/.exec(document.cookie);
2276
+
2277
+ switch (match && match[1]) {
2278
+ case "JSESSIONID":
2279
+ return url.replace(/;jsessionid=[^\?]*|(\?)|$/, ";jsessionid=" + match[2] + "$1");
2280
+ case "PHPSESSID":
2281
+ return url.replace(/\?PHPSESSID=[^&]*&?|\?|$/, "?PHPSESSID=" + match[2] + "&").replace(/&$/, "");
2282
+ }
2283
+ return url;
2284
+ };
2285
+
2286
+ // Handles open and message event
2287
+ xdr.onprogress = function () {
2288
+ handle(xdr);
2289
+ };
2290
+ // Handles error event
2291
+ xdr.onerror = function () {
2292
+ // If the server doesn't send anything back to XDR will fail with polling
2293
+ if (rq.transport !== 'polling') {
2294
+ _clearState();
2295
+ if (_requestCount++ < rq.maxReconnectOnClose) {
2296
+ if (rq.reconnectInterval > 0) {
2297
+ rq.reconnectId = setTimeout(function () {
2298
+ _open('re-connecting', request.transport, request);
2299
+ _ieXDR(rq);
2300
+ }, rq.reconnectInterval);
2301
+ } else {
2302
+ _open('re-connecting', request.transport, request);
2303
+ _ieXDR(rq);
2304
+ }
2305
+ } else {
2306
+ _onError(0, "maxReconnectOnClose reached");
2307
+ }
2308
+ }
2309
+ };
2310
+
2311
+ // Handles close event
2312
+ xdr.onload = function () {
2313
+ };
2314
+
2315
+ var handle = function (xdr) {
2316
+ clearTimeout(rq.id);
2317
+ var message = xdr.responseText;
2318
+
2319
+ message = message.substring(lastIndex);
2320
+ lastIndex += message.length;
2321
+
2322
+ if (transport !== 'polling') {
2323
+ _timeout(rq);
2324
+
2325
+ var skipCallbackInvocation = _trackMessageSize(message, rq, _response);
2326
+
2327
+ if (transport === 'long-polling' && atmosphere.util.trim(message).length === 0)
2328
+ return;
2329
+
2330
+ if (rq.executeCallbackBeforeReconnect) {
2331
+ reconnect();
2332
+ }
2333
+
2334
+ if (!skipCallbackInvocation) {
2335
+ _prepareCallback(_response.responseBody, "messageReceived", 200, transport);
2336
+ }
2337
+
2338
+ if (!rq.executeCallbackBeforeReconnect) {
2339
+ reconnect();
2340
+ }
2341
+ }
2342
+ };
2343
+
2344
+ return {
2345
+ open: function () {
2346
+ var url = rq.url;
2347
+ if (rq.dispatchUrl != null) {
2348
+ url += rq.dispatchUrl;
2349
+ }
2350
+ url = _attachHeaders(rq, url);
2351
+ xdr.open(rq.method, rewriteURL(url));
2352
+ if (rq.method === 'GET') {
2353
+ xdr.send();
2354
+ } else {
2355
+ xdr.send(rq.data);
2356
+ }
2357
+
2358
+ if (rq.connectTimeout > 0) {
2359
+ rq.id = setTimeout(function () {
2360
+ if (rq.requestCount === 0) {
2361
+ _clearState();
2362
+ _prepareCallback("Connect timeout", "closed", 200, rq.transport);
2363
+ }
2364
+ }, rq.connectTimeout);
2365
+ }
2366
+ },
2367
+ close: function () {
2368
+ xdr.abort();
2369
+ }
2370
+ };
2371
+ }
2372
+
2373
+ function _ieStreaming(request) {
2374
+ _ieStream = _configureIE(request);
2375
+ _ieStream.open();
2376
+ }
2377
+
2378
+ function _configureIE(request) {
2379
+ var rq = _request;
2380
+ if ((request != null) && (typeof (request) !== 'undefined')) {
2381
+ rq = request;
2382
+ }
2383
+
2384
+ var stop;
2385
+ var doc = new window.ActiveXObject("htmlfile");
2386
+
2387
+ doc.open();
2388
+ doc.close();
2389
+
2390
+ var url = rq.url;
2391
+ if (rq.dispatchUrl != null) {
2392
+ url += rq.dispatchUrl;
2393
+ }
2394
+
2395
+ if (rq.transport !== 'polling') {
2396
+ _response.transport = rq.transport;
2397
+ }
2398
+
2399
+ return {
2400
+ open: function () {
2401
+ var iframe = doc.createElement("iframe");
2402
+
2403
+ url = _attachHeaders(rq);
2404
+ if (rq.data !== '') {
2405
+ url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(rq.data);
2406
+ }
2407
+
2408
+ // Finally attach a timestamp to prevent Android and IE caching.
2409
+ url = atmosphere.util.prepareURL(url);
2410
+
2411
+ iframe.src = url;
2412
+ doc.body.appendChild(iframe);
2413
+
2414
+ // For the server to respond in a consistent format regardless of user agent, we polls response text
2415
+ var cdoc = iframe.contentDocument || iframe.contentWindow.document;
2416
+
2417
+ stop = atmosphere.util.iterate(function () {
2418
+ try {
2419
+ if (!cdoc.firstChild) {
2420
+ return;
2421
+ }
2422
+
2423
+ var res = cdoc.body ? cdoc.body.lastChild : cdoc;
2424
+ var readResponse = function () {
2425
+ // Clones the element not to disturb the original one
2426
+ var clone = res.cloneNode(true);
2427
+
2428
+ // If the last character is a carriage return or a line feed, IE ignores it in the innerText property
2429
+ // therefore, we add another non-newline character to preserve it
2430
+ clone.appendChild(cdoc.createTextNode("."));
2431
+
2432
+ var text = clone.innerText;
2433
+
2434
+ text = text.substring(0, text.length - 1);
2435
+ return text;
2436
+
2437
+ };
2438
+
2439
+ // To support text/html content type
2440
+ if (!cdoc.body || !cdoc.body.firstChild || cdoc.body.firstChild.nodeName.toLowerCase() !== "pre") {
2441
+ // Injects a plaintext element which renders text without interpreting the HTML and cannot be stopped
2442
+ // it is deprecated in HTML5, but still works
2443
+ var head = cdoc.head || cdoc.getElementsByTagName("head")[0] || cdoc.documentElement || cdoc;
2444
+ var script = cdoc.createElement("script");
2445
+
2446
+ script.text = "document.write('<plaintext>')";
2447
+
2448
+ head.insertBefore(script, head.firstChild);
2449
+ head.removeChild(script);
2450
+
2451
+ // The plaintext element will be the response container
2452
+ res = cdoc.body.lastChild;
2453
+ }
2454
+
2455
+ if (rq.closed) {
2456
+ rq.isReopen = true;
2457
+ }
2458
+
2459
+ // Handles message and close event
2460
+ stop = atmosphere.util.iterate(function () {
2461
+ var text = readResponse();
2462
+ if (text.length > rq.lastIndex) {
2463
+ _timeout(_request);
2464
+
2465
+ _response.status = 200;
2466
+ _response.error = null;
2467
+
2468
+ // Empties response every time that it is handled
2469
+ res.innerText = "";
2470
+ var skipCallbackInvocation = _trackMessageSize(text, rq, _response);
2471
+ if (skipCallbackInvocation) {
2472
+ return "";
2473
+ }
2474
+
2475
+ _prepareCallback(_response.responseBody, "messageReceived", 200, rq.transport);
2476
+ }
2477
+
2478
+ rq.lastIndex = 0;
2479
+
2480
+ if (cdoc.readyState === "complete") {
2481
+ _invokeClose(true);
2482
+ _open('re-connecting', rq.transport, rq);
2483
+ if (rq.reconnectInterval > 0) {
2484
+ rq.reconnectId = setTimeout(function () {
2485
+ _ieStreaming(rq);
2486
+ }, rq.reconnectInterval);
2487
+ } else {
2488
+ _ieStreaming(rq);
2489
+ }
2490
+ return false;
2491
+ }
2492
+ }, null);
2493
+
2494
+ return false;
2495
+ } catch (err) {
2496
+ _response.error = true;
2497
+ _open('re-connecting', rq.transport, rq);
2498
+ if (_requestCount++ < rq.maxReconnectOnClose) {
2499
+ if (rq.reconnectInterval > 0) {
2500
+ rq.reconnectId = setTimeout(function () {
2501
+ _ieStreaming(rq);
2502
+ }, rq.reconnectInterval);
2503
+ } else {
2504
+ _ieStreaming(rq);
2505
+ }
2506
+ } else {
2507
+ _onError(0, "maxReconnectOnClose reached");
2508
+ }
2509
+ doc.execCommand("Stop");
2510
+ doc.close();
2511
+ return false;
2512
+ }
2513
+ });
2514
+ },
2515
+
2516
+ close: function () {
2517
+ if (stop) {
2518
+ stop();
2519
+ }
2520
+
2521
+ doc.execCommand("Stop");
2522
+ _invokeClose(true);
2523
+ }
2524
+ };
2525
+ }
2526
+
2527
+ /**
2528
+ * Send message. <br>
2529
+ * Will be automatically dispatch to other connected.
2530
+ *
2531
+ * @param {Object, string} Message to send.
2532
+ * @private
2533
+ */
2534
+ function _push(message) {
2535
+
2536
+ if (_localStorageService != null) {
2537
+ _pushLocal(message);
2538
+ } else if (_activeRequest != null || _sse != null) {
2539
+ _pushAjaxMessage(message);
2540
+ } else if (_ieStream != null) {
2541
+ _pushIE(message);
2542
+ } else if (_jqxhr != null) {
2543
+ _pushJsonp(message);
2544
+ } else if (_websocket != null) {
2545
+ _pushWebSocket(message);
2546
+ } else {
2547
+ _onError(0, "No suspended connection available");
2548
+ atmosphere.util.error("No suspended connection available. Make sure atmosphere.subscribe has been called and request.onOpen invoked before trying to push data");
2549
+ }
2550
+ }
2551
+
2552
+ function _pushOnClose(message, rq) {
2553
+ if (!rq) {
2554
+ rq = _getPushRequest(message);
2555
+ }
2556
+ rq.transport = "polling";
2557
+ rq.method = "GET";
2558
+ rq.withCredentials = false;
2559
+ rq.reconnect = false;
2560
+ rq.force = true;
2561
+ rq.suspend = false;
2562
+ rq.timeout = 1000;
2563
+ _executeRequest(rq);
2564
+ }
2565
+
2566
+ function _pushLocal(message) {
2567
+ _localStorageService.send(message);
2568
+ }
2569
+
2570
+ function _intraPush(message) {
2571
+ // IE 9 will crash if not.
2572
+ if (message.length === 0)
2573
+ return;
2574
+
2575
+ try {
2576
+ if (_localStorageService) {
2577
+ _localStorageService.localSend(message);
2578
+ } else if (_storageService) {
2579
+ _storageService.signal("localMessage", atmosphere.util.stringifyJSON({
2580
+ id: guid,
2581
+ event: message
2582
+ }));
2583
+ }
2584
+ } catch (err) {
2585
+ atmosphere.util.error(err);
2586
+ }
2587
+ }
2588
+
2589
+ /**
2590
+ * Send a message using currently opened ajax request (using http-streaming or long-polling). <br>
2591
+ *
2592
+ * @param {string, Object} Message to send. This is an object, string message is saved in data member.
2593
+ * @private
2594
+ */
2595
+ function _pushAjaxMessage(message) {
2596
+ var rq = _getPushRequest(message);
2597
+ _executeRequest(rq);
2598
+ }
2599
+
2600
+ /**
2601
+ * Send a message using currently opened ie streaming (using http-streaming or long-polling). <br>
2602
+ *
2603
+ * @param {string, Object} Message to send. This is an object, string message is saved in data member.
2604
+ * @private
2605
+ */
2606
+ function _pushIE(message) {
2607
+ if (_request.enableXDR && atmosphere.util.checkCORSSupport()) {
2608
+ var rq = _getPushRequest(message);
2609
+ // Do not reconnect since we are pushing.
2610
+ rq.reconnect = false;
2611
+ _jsonp(rq);
2612
+ } else {
2613
+ _pushAjaxMessage(message);
2614
+ }
2615
+ }
2616
+
2617
+ /**
2618
+ * Send a message using jsonp transport. <br>
2619
+ *
2620
+ * @param {string, Object} Message to send. This is an object, string message is saved in data member.
2621
+ * @private
2622
+ */
2623
+ function _pushJsonp(message) {
2624
+ _pushAjaxMessage(message);
2625
+ }
2626
+
2627
+ function _getStringMessage(message) {
2628
+ var msg = message;
2629
+ if (typeof (msg) === 'object') {
2630
+ msg = message.data;
2631
+ }
2632
+ return msg;
2633
+ }
2634
+
2635
+ /**
2636
+ * Build request use to push message using method 'POST' <br>. Transport is defined as 'polling' and 'suspend' is set to false.
2637
+ *
2638
+ * @return {Object} Request object use to push message.
2639
+ * @private
2640
+ */
2641
+ function _getPushRequest(message) {
2642
+ var msg = _getStringMessage(message);
2643
+
2644
+ var rq = {
2645
+ connected: false,
2646
+ timeout: 60000,
2647
+ method: 'POST',
2648
+ url: _request.url,
2649
+ contentType: _request.contentType,
2650
+ headers: _request.headers,
2651
+ reconnect: true,
2652
+ callback: null,
2653
+ data: msg,
2654
+ suspend: false,
2655
+ maxRequest: -1,
2656
+ logLevel: 'info',
2657
+ requestCount: 0,
2658
+ withCredentials: _request.withCredentials,
2659
+ async: _request.async,
2660
+ transport: 'polling',
2661
+ isOpen: true,
2662
+ attachHeadersAsQueryString: true,
2663
+ enableXDR: _request.enableXDR,
2664
+ uuid: _request.uuid,
2665
+ dispatchUrl: _request.dispatchUrl,
2666
+ enableProtocol: false,
2667
+ messageDelimiter: '|',
2668
+ trackMessageLength: _request.trackMessageLength,
2669
+ maxReconnectOnClose: _request.maxReconnectOnClose,
2670
+ heartbeatTimer: _request.heartbeatTimer,
2671
+ heartbeat: _request.heartbeat
2672
+ };
2673
+
2674
+ if (typeof (message) === 'object') {
2675
+ rq = atmosphere.util.extend(rq, message);
2676
+ }
2677
+
2678
+ return rq;
2679
+ }
2680
+
2681
+ /**
2682
+ * Send a message using currently opened websocket. <br>
2683
+ *
2684
+ */
2685
+ function _pushWebSocket(message) {
2686
+ var msg = atmosphere.util.isBinary(message) ? message : _getStringMessage(message);
2687
+ var data;
2688
+ try {
2689
+ if (_request.dispatchUrl != null) {
2690
+ data = _request.webSocketPathDelimiter + _request.dispatchUrl + _request.webSocketPathDelimiter + msg;
2691
+ } else {
2692
+ data = msg;
2693
+ }
2694
+
2695
+ if (!_websocket.canSendMessage) {
2696
+ atmosphere.util.error("WebSocket not connected.");
2697
+ return;
2698
+ }
2699
+
2700
+ _websocket.send(data);
2701
+
2702
+ } catch (e) {
2703
+ _websocket.onclose = function (message) {
2704
+ };
2705
+ _clearState();
2706
+
2707
+ _reconnectWithFallbackTransport("Websocket failed. Downgrading to " + _request.fallbackTransport + " and resending " + message);
2708
+ _pushAjaxMessage(message);
2709
+ }
2710
+ }
2711
+
2712
+ function _localMessage(message) {
2713
+ var m = atmosphere.util.parseJSON(message);
2714
+ if (m.id !== guid) {
2715
+ if (typeof (_request.onLocalMessage) !== 'undefined') {
2716
+ _request.onLocalMessage(m.event);
2717
+ } else if (typeof (atmosphere.util.onLocalMessage) !== 'undefined') {
2718
+ atmosphere.util.onLocalMessage(m.event);
2719
+ }
2720
+ }
2721
+ }
2722
+
2723
+ function _prepareCallback(messageBody, state, errorCode, transport) {
2724
+
2725
+ _response.responseBody = messageBody;
2726
+ _response.transport = transport;
2727
+ _response.status = errorCode;
2728
+ _response.state = state;
2729
+
2730
+ _invokeCallback();
2731
+ }
2732
+
2733
+ function _readHeaders(xdr, request) {
2734
+ if (!request.readResponsesHeaders) {
2735
+ if (!request.enableProtocol) {
2736
+ request.uuid = guid;
2737
+ }
2738
+ }
2739
+ else {
2740
+ try {
2741
+
2742
+ var tempUUID = xdr.getResponseHeader('X-Atmosphere-tracking-id');
2743
+ if (tempUUID && tempUUID != null) {
2744
+ request.uuid = tempUUID.split(" ").pop();
2745
+ }
2746
+ } catch (e) {
2747
+ }
2748
+ }
2749
+ }
2750
+
2751
+ function _invokeFunction(response) {
2752
+ _f(response, _request);
2753
+ // Global
2754
+ _f(response, atmosphere.util);
2755
+ }
2756
+
2757
+ function _f(response, f) {
2758
+ switch (response.state) {
2759
+ case "messageReceived":
2760
+ _debug("Firing onMessage");
2761
+ _requestCount = 0;
2762
+ if (typeof (f.onMessage) !== 'undefined')
2763
+ f.onMessage(response);
2764
+
2765
+ if (typeof (f.onmessage) !== 'undefined')
2766
+ f.onmessage(response);
2767
+ break;
2768
+ case "error":
2769
+ var dbgReasonPhrase = (typeof(response.reasonPhrase) != 'undefined') ? response.reasonPhrase : 'n/a';
2770
+ _debug("Firing onError, reasonPhrase: " + dbgReasonPhrase);
2771
+ if (typeof (f.onError) !== 'undefined')
2772
+ f.onError(response);
2773
+
2774
+ if (typeof (f.onerror) !== 'undefined')
2775
+ f.onerror(response);
2776
+ break;
2777
+ case "opening":
2778
+ delete _request.closed;
2779
+ _debug("Firing onOpen");
2780
+ if (typeof (f.onOpen) !== 'undefined')
2781
+ f.onOpen(response);
2782
+
2783
+ if (typeof (f.onopen) !== 'undefined')
2784
+ f.onopen(response);
2785
+ break;
2786
+ case "messagePublished":
2787
+ _debug("Firing messagePublished");
2788
+ if (typeof (f.onMessagePublished) !== 'undefined')
2789
+ f.onMessagePublished(response);
2790
+ break;
2791
+ case "re-connecting":
2792
+ _debug("Firing onReconnect");
2793
+ if (typeof (f.onReconnect) !== 'undefined')
2794
+ f.onReconnect(_request, response);
2795
+ break;
2796
+ case "closedByClient":
2797
+ _debug("Firing closedByClient");
2798
+ if (typeof (f.onClientTimeout) !== 'undefined')
2799
+ f.onClientTimeout(_request);
2800
+ break;
2801
+ case "re-opening":
2802
+ delete _request.closed;
2803
+ _debug("Firing onReopen");
2804
+ if (typeof (f.onReopen) !== 'undefined')
2805
+ f.onReopen(_request, response);
2806
+ break;
2807
+ case "fail-to-reconnect":
2808
+ _debug("Firing onFailureToReconnect");
2809
+ if (typeof (f.onFailureToReconnect) !== 'undefined')
2810
+ f.onFailureToReconnect(_request, response);
2811
+ break;
2812
+ case "unsubscribe":
2813
+ case "closed":
2814
+ var closed = typeof (_request.closed) !== 'undefined' ? _request.closed : false;
2815
+
2816
+ if (!closed) {
2817
+ _debug("Firing onClose (" + response.state + " case)");
2818
+ if (typeof (f.onClose) !== 'undefined') {
2819
+ f.onClose(response);
2820
+ }
2821
+
2822
+ if (typeof (f.onclose) !== 'undefined') {
2823
+ f.onclose(response);
2824
+ }
2825
+ } else {
2826
+ _debug("Request already closed, not firing onClose (" + response.state + " case)");
2827
+ }
2828
+ _request.closed = true;
2829
+ break;
2830
+ case "openAfterResume":
2831
+ if (typeof (f.onOpenAfterResume) !== 'undefined')
2832
+ f.onOpenAfterResume(_request);
2833
+ break;
2834
+ }
2835
+ }
2836
+
2837
+ function _invokeClose(wasOpen) {
2838
+ if (_response.state !== 'closed') {
2839
+ _response.state = 'closed';
2840
+ _response.responseBody = "";
2841
+ _response.messages = [];
2842
+ _response.status = !wasOpen ? 501 : 200;
2843
+ _invokeCallback();
2844
+ }
2845
+ }
2846
+
2847
+ /**
2848
+ * Invoke request callbacks.
2849
+ *
2850
+ * @private
2851
+ */
2852
+ function _invokeCallback() {
2853
+ var call = function (index, func) {
2854
+ func(_response);
2855
+ };
2856
+
2857
+ if (_localStorageService == null && _localSocketF != null) {
2858
+ _localSocketF(_response.responseBody);
2859
+ }
2860
+
2861
+ _request.reconnect = _request.mrequest;
2862
+
2863
+ var isString = typeof (_response.responseBody) === 'string';
2864
+ var messages = (isString && _request.trackMessageLength) ? (_response.messages.length > 0 ? _response.messages : ['']) : new Array(
2865
+ _response.responseBody);
2866
+ for (var i = 0; i < messages.length; i++) {
2867
+
2868
+ if (messages.length > 1 && messages[i].length === 0) {
2869
+ continue;
2870
+ }
2871
+ _response.responseBody = (isString) ? atmosphere.util.trim(messages[i]) : messages[i];
2872
+
2873
+ if (_localStorageService == null && _localSocketF != null) {
2874
+ _localSocketF(_response.responseBody);
2875
+ }
2876
+
2877
+ if ((_response.responseBody.length === 0 ||
2878
+ (isString && _heartbeatPadding === _response.responseBody)) && _response.state === "messageReceived") {
2879
+ continue;
2880
+ }
2881
+
2882
+ _invokeFunction(_response);
2883
+
2884
+ // Invoke global callbacks
2885
+ if (callbacks.length > 0) {
2886
+ if (_canLog('debug')) {
2887
+ atmosphere.util.debug("Invoking " + callbacks.length + " global callbacks: " + _response.state);
2888
+ }
2889
+ try {
2890
+ atmosphere.util.each(callbacks, call);
2891
+ } catch (e) {
2892
+ atmosphere.util.log(_request.logLevel, ["Callback exception" + e]);
2893
+ }
2894
+ }
2895
+
2896
+ // Invoke request callback
2897
+ if (typeof (_request.callback) === 'function') {
2898
+ if (_canLog('debug')) {
2899
+ atmosphere.util.debug("Invoking request callbacks");
2900
+ }
2901
+ try {
2902
+ _request.callback(_response);
2903
+ } catch (e) {
2904
+ atmosphere.util.log(_request.logLevel, ["Callback exception" + e]);
2905
+ }
2906
+ }
2907
+ }
2908
+ }
2909
+
2910
+ this.subscribe = function (options) {
2911
+ _subscribe(options);
2912
+ _execute();
2913
+ };
2914
+
2915
+ this.execute = function () {
2916
+ _execute();
2917
+ };
2918
+
2919
+ this.close = function () {
2920
+ _close();
2921
+ };
2922
+
2923
+ this.disconnect = function () {
2924
+ _disconnect();
2925
+ };
2926
+
2927
+ this.getUrl = function () {
2928
+ return _request.url;
2929
+ };
2930
+
2931
+ this.push = function (message, dispatchUrl) {
2932
+ if (dispatchUrl != null) {
2933
+ var originalDispatchUrl = _request.dispatchUrl;
2934
+ _request.dispatchUrl = dispatchUrl;
2935
+ _push(message);
2936
+ _request.dispatchUrl = originalDispatchUrl;
2937
+ } else {
2938
+ _push(message);
2939
+ }
2940
+ };
2941
+
2942
+ this.getUUID = function () {
2943
+ return _request.uuid;
2944
+ };
2945
+
2946
+ this.pushLocal = function (message) {
2947
+ _intraPush(message);
2948
+ };
2949
+
2950
+ this.enableProtocol = function (message) {
2951
+ return _request.enableProtocol;
2952
+ };
2953
+
2954
+ this.init = function () {
2955
+ _init();
2956
+ };
2957
+
2958
+ this.request = _request;
2959
+ this.response = _response;
2960
+ }
2961
+ };
2962
+
2963
+ atmosphere.subscribe = function (url, callback, request) {
2964
+ if (typeof (callback) === 'function') {
2965
+ atmosphere.addCallback(callback);
2966
+ }
2967
+
2968
+ if (typeof (url) !== "string") {
2969
+ request = url;
2970
+ } else {
2971
+ request.url = url;
2972
+ }
2973
+
2974
+ // https://github.com/Atmosphere/atmosphere-javascript/issues/58
2975
+ uuid = ((typeof (request) !== 'undefined') && typeof (request.uuid) !== 'undefined') ? request.uuid : 0;
2976
+
2977
+ var rq = new atmosphere.AtmosphereRequest(request);
2978
+ rq.execute();
2979
+
2980
+ requests[requests.length] = rq;
2981
+ return rq;
2982
+ };
2983
+
2984
+ atmosphere.unsubscribe = function () {
2985
+ if (requests.length > 0) {
2986
+ var requestsClone = [].concat(requests);
2987
+ for (var i = 0; i < requestsClone.length; i++) {
2988
+ var rq = requestsClone[i];
2989
+ rq.close();
2990
+ clearTimeout(rq.response.request.id);
2991
+
2992
+ if (rq.heartbeatTimer) {
2993
+ clearTimeout(rq.heartbeatTimer);
2994
+ }
2995
+ }
2996
+ }
2997
+ requests = [];
2998
+ callbacks = [];
2999
+ };
3000
+
3001
+ atmosphere.unsubscribeUrl = function (url) {
3002
+ var idx = -1;
3003
+ if (requests.length > 0) {
3004
+ for (var i = 0; i < requests.length; i++) {
3005
+ var rq = requests[i];
3006
+
3007
+ // Suppose you can subscribe once to an url
3008
+ if (rq.getUrl() === url) {
3009
+ rq.close();
3010
+ clearTimeout(rq.response.request.id);
3011
+
3012
+ if (rq.heartbeatTimer) {
3013
+ clearTimeout(rq.heartbeatTimer);
3014
+ }
3015
+
3016
+ idx = i;
3017
+ break;
3018
+ }
3019
+ }
3020
+ }
3021
+ if (idx >= 0) {
3022
+ requests.splice(idx, 1);
3023
+ }
3024
+ };
3025
+
3026
+ atmosphere.addCallback = function (func) {
3027
+ if (atmosphere.util.inArray(func, callbacks) === -1) {
3028
+ callbacks.push(func);
3029
+ }
3030
+ };
3031
+
3032
+ atmosphere.removeCallback = function (func) {
3033
+ var index = atmosphere.util.inArray(func, callbacks);
3034
+ if (index !== -1) {
3035
+ callbacks.splice(index, 1);
3036
+ }
3037
+ };
3038
+
3039
+ atmosphere.util = {
3040
+ browser: {},
3041
+
3042
+ parseHeaders: function (headerString) {
3043
+ var match, rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, headers = {};
3044
+ while (match = rheaders.exec(headerString)) {
3045
+ headers[match[1]] = match[2];
3046
+ }
3047
+ return headers;
3048
+ },
3049
+
3050
+ now: function () {
3051
+ return new Date().getTime();
3052
+ },
3053
+
3054
+ isArray: function (array) {
3055
+ return Object.prototype.toString.call(array) === "[object Array]";
3056
+ },
3057
+
3058
+ inArray: function (elem, array) {
3059
+ if (!Array.prototype.indexOf) {
3060
+ var len = array.length;
3061
+ for (var i = 0; i < len; ++i) {
3062
+ if (array[i] === elem) {
3063
+ return i;
3064
+ }
3065
+ }
3066
+ return -1;
3067
+ }
3068
+ return array.indexOf(elem);
3069
+ },
3070
+
3071
+ isBinary: function (data) {
3072
+ // True if data is an instance of Blob, ArrayBuffer or ArrayBufferView
3073
+ return /^\[object\s(?:Blob|ArrayBuffer|.+Array)\]$/.test(Object.prototype.toString.call(data));
3074
+ },
3075
+
3076
+ isFunction: function (fn) {
3077
+ return Object.prototype.toString.call(fn) === "[object Function]";
3078
+ },
3079
+
3080
+ getAbsoluteURL: function (url) {
3081
+ if (typeof (document.createElement) === 'undefined') {
3082
+ // assuming the url to be already absolute when DOM is not supported
3083
+ return url;
3084
+ }
3085
+ var div = document.createElement("div");
3086
+
3087
+ // Uses an innerHTML property to obtain an absolute URL
3088
+ div.innerHTML = '<a href="' + url + '"/>';
3089
+
3090
+ // encodeURI and decodeURI are needed to normalize URL between IE and non-IE,
3091
+ // since IE doesn't encode the href property value and return it - http://jsfiddle.net/Yq9M8/1/
3092
+ return encodeURI(decodeURI(div.firstChild.href));
3093
+ },
3094
+
3095
+ prepareURL: function (url) {
3096
+ // Attaches a time stamp to prevent caching
3097
+ var ts = atmosphere.util.now();
3098
+ var ret = url.replace(/([?&])_=[^&]*/, "$1_=" + ts);
3099
+
3100
+ return ret + (ret === url ? (/\?/.test(url) ? "&" : "?") + "_=" + ts : "");
3101
+ },
3102
+
3103
+ trim: function (str) {
3104
+ if (!String.prototype.trim) {
3105
+ return str.toString().replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g, "").replace(/\s+/g, " ");
3106
+ } else {
3107
+ return str.toString().trim();
3108
+ }
3109
+ },
3110
+
3111
+ param: function (params) {
3112
+ var prefix, s = [];
3113
+
3114
+ function add(key, value) {
3115
+ value = atmosphere.util.isFunction(value) ? value() : (value == null ? "" : value);
3116
+ s.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
3117
+ }
3118
+
3119
+ function buildParams(prefix, obj) {
3120
+ var name;
3121
+
3122
+ if (atmosphere.util.isArray(obj)) {
3123
+ atmosphere.util.each(obj, function (i, v) {
3124
+ if (/\[\]$/.test(prefix)) {
3125
+ add(prefix, v);
3126
+ } else {
3127
+ buildParams(prefix + "[" + (typeof v === "object" ? i : "") + "]", v);
3128
+ }
3129
+ });
3130
+ } else if (Object.prototype.toString.call(obj) === "[object Object]") {
3131
+ for (name in obj) {
3132
+ buildParams(prefix + "[" + name + "]", obj[name]);
3133
+ }
3134
+ } else {
3135
+ add(prefix, obj);
3136
+ }
3137
+ }
3138
+
3139
+ for (prefix in params) {
3140
+ buildParams(prefix, params[prefix]);
3141
+ }
3142
+
3143
+ return s.join("&").replace(/%20/g, "+");
3144
+ },
3145
+
3146
+ storage: function () {
3147
+ try {
3148
+ return !!(window.localStorage && window.StorageEvent);
3149
+ } catch (e) {
3150
+ //Firefox throws an exception here, see
3151
+ //https://bugzilla.mozilla.org/show_bug.cgi?id=748620
3152
+ return false;
3153
+ }
3154
+ },
3155
+
3156
+ iterate: function (fn, interval) {
3157
+ var timeoutId;
3158
+
3159
+ // Though the interval is 0 for real-time application, there is a delay between setTimeout calls
3160
+ // For detail, see https://developer.mozilla.org/en/window.setTimeout#Minimum_delay_and_timeout_nesting
3161
+ interval = interval || 0;
3162
+
3163
+ (function loop() {
3164
+ timeoutId = setTimeout(function () {
3165
+ if (fn() === false) {
3166
+ return;
3167
+ }
3168
+
3169
+ loop();
3170
+ }, interval);
3171
+ })();
3172
+
3173
+ return function () {
3174
+ clearTimeout(timeoutId);
3175
+ };
3176
+ },
3177
+
3178
+ each: function (obj, callback, args) {
3179
+ if (!obj) return;
3180
+ var value, i = 0, length = obj.length, isArray = atmosphere.util.isArray(obj);
3181
+
3182
+ if (args) {
3183
+ if (isArray) {
3184
+ for (; i < length; i++) {
3185
+ value = callback.apply(obj[i], args);
3186
+
3187
+ if (value === false) {
3188
+ break;
3189
+ }
3190
+ }
3191
+ } else {
3192
+ for (i in obj) {
3193
+ value = callback.apply(obj[i], args);
3194
+
3195
+ if (value === false) {
3196
+ break;
3197
+ }
3198
+ }
3199
+ }
3200
+
3201
+ // A special, fast, case for the most common use of each
3202
+ } else {
3203
+ if (isArray) {
3204
+ for (; i < length; i++) {
3205
+ value = callback.call(obj[i], i, obj[i]);
3206
+
3207
+ if (value === false) {
3208
+ break;
3209
+ }
3210
+ }
3211
+ } else {
3212
+ for (i in obj) {
3213
+ value = callback.call(obj[i], i, obj[i]);
3214
+
3215
+ if (value === false) {
3216
+ break;
3217
+ }
3218
+ }
3219
+ }
3220
+ }
3221
+
3222
+ return obj;
3223
+ },
3224
+
3225
+ extend: function (target) {
3226
+ var i, options, name;
3227
+
3228
+ for (i = 1; i < arguments.length; i++) {
3229
+ if ((options = arguments[i]) != null) {
3230
+ for (name in options) {
3231
+ target[name] = options[name];
3232
+ }
3233
+ }
3234
+ }
3235
+
3236
+ return target;
3237
+ },
3238
+ on: function (elem, type, fn) {
3239
+ if (elem.addEventListener) {
3240
+ elem.addEventListener(type, fn, false);
3241
+ } else if (elem.attachEvent) {
3242
+ elem.attachEvent("on" + type, fn);
3243
+ }
3244
+ },
3245
+ off: function (elem, type, fn) {
3246
+ if (elem.removeEventListener) {
3247
+ elem.removeEventListener(type, fn, false);
3248
+ } else if (elem.detachEvent) {
3249
+ elem.detachEvent("on" + type, fn);
3250
+ }
3251
+ },
3252
+
3253
+ log: function (level, args) {
3254
+ if (window.console) {
3255
+ var logger = window.console[level];
3256
+ if (typeof logger === 'function') {
3257
+ logger.apply(window.console, args);
3258
+ }
3259
+ }
3260
+ },
3261
+
3262
+ warn: function () {
3263
+ atmosphere.util.log('warn', arguments);
3264
+ },
3265
+
3266
+ info: function () {
3267
+ atmosphere.util.log('info', arguments);
3268
+ },
3269
+
3270
+ debug: function () {
3271
+ atmosphere.util.log('debug', arguments);
3272
+ },
3273
+
3274
+ error: function () {
3275
+ atmosphere.util.log('error', arguments);
3276
+ },
3277
+ xhr: function () {
3278
+ try {
3279
+ return new window.XMLHttpRequest();
3280
+ } catch (e1) {
3281
+ try {
3282
+ return new window.ActiveXObject("Microsoft.XMLHTTP");
3283
+ } catch (e2) {
3284
+ }
3285
+ }
3286
+ },
3287
+ parseJSON: function (data) {
3288
+ return !data ? null : window.JSON && window.JSON.parse ? window.JSON.parse(data) : new Function("return " + data)();
3289
+ },
3290
+ // http://github.com/flowersinthesand/stringifyJSON
3291
+ stringifyJSON: function (value) {
3292
+ var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, meta = {
3293
+ '\b': '\\b',
3294
+ '\t': '\\t',
3295
+ '\n': '\\n',
3296
+ '\f': '\\f',
3297
+ '\r': '\\r',
3298
+ '"': '\\"',
3299
+ '\\': '\\\\'
3300
+ };
3301
+
3302
+ function quote(string) {
3303
+ return '"' + string.replace(escapable, function (a) {
3304
+ var c = meta[a];
3305
+ return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
3306
+ }) + '"';
3307
+ }
3308
+
3309
+ function f(n) {
3310
+ return n < 10 ? "0" + n : n;
3311
+ }
3312
+
3313
+ return window.JSON && window.JSON.stringify ? window.JSON.stringify(value) : (function str(key, holder) {
3314
+ var i, v, len, partial, value = holder[key], type = typeof value;
3315
+
3316
+ if (value && typeof value === "object" && typeof value.toJSON === "function") {
3317
+ value = value.toJSON(key);
3318
+ type = typeof value;
3319
+ }
3320
+
3321
+ switch (type) {
3322
+ case "string":
3323
+ return quote(value);
3324
+ case "number":
3325
+ return isFinite(value) ? String(value) : "null";
3326
+ case "boolean":
3327
+ return String(value);
3328
+ case "object":
3329
+ if (!value) {
3330
+ return "null";
3331
+ }
3332
+
3333
+ switch (Object.prototype.toString.call(value)) {
3334
+ case "[object Date]":
3335
+ return isFinite(value.valueOf()) ? '"' + value.getUTCFullYear() + "-" + f(value.getUTCMonth() + 1) + "-"
3336
+ + f(value.getUTCDate()) + "T" + f(value.getUTCHours()) + ":" + f(value.getUTCMinutes()) + ":" + f(value.getUTCSeconds())
3337
+ + "Z" + '"' : "null";
3338
+ case "[object Array]":
3339
+ len = value.length;
3340
+ partial = [];
3341
+ for (i = 0; i < len; i++) {
3342
+ partial.push(str(i, value) || "null");
3343
+ }
3344
+
3345
+ return "[" + partial.join(",") + "]";
3346
+ default:
3347
+ partial = [];
3348
+ for (i in value) {
3349
+ if (hasOwn.call(value, i)) {
3350
+ v = str(i, value);
3351
+ if (v) {
3352
+ partial.push(quote(i) + ":" + v);
3353
+ }
3354
+ }
3355
+ }
3356
+
3357
+ return "{" + partial.join(",") + "}";
3358
+ }
3359
+ }
3360
+ })("", {
3361
+ "": value
3362
+ });
3363
+ },
3364
+
3365
+ checkCORSSupport: function () {
3366
+ if (atmosphere.util.browser.msie && !window.XDomainRequest && +atmosphere.util.browser.version.split(".")[0] < 11) {
3367
+ return true;
3368
+ } else if (atmosphere.util.browser.opera && +atmosphere.util.browser.version.split(".") < 12.0) {
3369
+ return true;
3370
+ }
3371
+
3372
+ // KreaTV 4.1 -> 4.4
3373
+ else if (atmosphere.util.trim(navigator.userAgent).slice(0, 16) === "KreaTVWebKit/531") {
3374
+ return true;
3375
+ }
3376
+ // KreaTV 3.8
3377
+ else if (atmosphere.util.trim(navigator.userAgent).slice(-7).toLowerCase() === "kreatel") {
3378
+ return true;
3379
+ }
3380
+
3381
+ // Force older Android versions to use CORS as some version like 2.2.3 fail otherwise
3382
+ var ua = navigator.userAgent.toLowerCase();
3383
+ var androidVersionMatches = ua.match(/.+android ([0-9]{1,2})/i),
3384
+ majorVersion = parseInt((androidVersionMatches && androidVersionMatches[0]) || -1, 10);
3385
+ if (!isNaN(majorVersion) && majorVersion > -1 && majorVersion < 3) {
3386
+ return true;
3387
+ }
3388
+ return false;
3389
+ }
3390
+ };
3391
+
3392
+ guid = atmosphere.util.now();
3393
+
3394
+ // Browser sniffing
3395
+ (function () {
3396
+ var ua = navigator.userAgent.toLowerCase(),
3397
+ match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
3398
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
3399
+ /(msie) ([\w.]+)/.exec(ua) ||
3400
+ /(trident)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
3401
+ ua.indexOf("android") < 0 && /version\/(.+) (safari)/.exec(ua) ||
3402
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
3403
+ [];
3404
+
3405
+ // Swaps variables
3406
+ if (match[2] === "safari") {
3407
+ match[2] = match[1];
3408
+ match[1] = "safari";
3409
+ }
3410
+ atmosphere.util.browser[match[1] || ""] = true;
3411
+ atmosphere.util.browser.version = match[2] || "0";
3412
+ atmosphere.util.browser.vmajor = atmosphere.util.browser.version.split(".")[0];
3413
+
3414
+ // Trident is the layout engine of the Internet Explorer
3415
+ // IE 11 has no "MSIE: 11.0" token
3416
+ if (atmosphere.util.browser.trident) {
3417
+ atmosphere.util.browser.msie = true;
3418
+ }
3419
+
3420
+ // The storage event of Internet Explorer and Firefox 3 works strangely
3421
+ if (atmosphere.util.browser.msie || (atmosphere.util.browser.mozilla && +atmosphere.util.browser.version.split(".")[0] === 1)) {
3422
+ atmosphere.util.storage = false;
3423
+ }
3424
+ })();
3425
+
3426
+ atmosphere.util.on(window, "unload", function (event) {
3427
+ atmosphere.util.debug(new Date() + " Atmosphere: " + "unload event");
3428
+ atmosphere.unsubscribe();
3429
+ });
3430
+
3431
+ atmosphere.util.on(window, "beforeunload", function (event) {
3432
+ atmosphere.util.debug(new Date() + " Atmosphere: " + "beforeunload event");
3433
+
3434
+ // ATMOSPHERE-JAVASCRIPT-143: Delay reconnect to avoid reconnect attempts before an actual unload (we don't know if an unload will happen, yet)
3435
+ atmosphere._beforeUnloadState = true;
3436
+ setTimeout(function () {
3437
+ atmosphere.util.debug(new Date() + " Atmosphere: " + "beforeunload event timeout reached. Reset _beforeUnloadState flag");
3438
+ atmosphere._beforeUnloadState = false;
3439
+ }, 5000);
3440
+ });
3441
+
3442
+ // Pressing ESC key in Firefox kills the connection
3443
+ // for your information, this is fixed in Firefox 20
3444
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=614304
3445
+ atmosphere.util.on(window, "keypress", function (event) {
3446
+ if (event.charCode === 27 || event.keyCode === 27) {
3447
+ if (event.preventDefault) {
3448
+ event.preventDefault();
3449
+ }
3450
+ }
3451
+ });
3452
+
3453
+ atmosphere.util.on(window, "offline", function () {
3454
+ atmosphere.util.debug(new Date() + " Atmosphere: offline event");
3455
+ offline = true;
3456
+ if (requests.length > 0) {
3457
+ var requestsClone = [].concat(requests);
3458
+ for (var i = 0; i < requestsClone.length; i++) {
3459
+ var rq = requestsClone[i];
3460
+ if(rq.request.handleOnlineOffline) {
3461
+ rq.close();
3462
+ clearTimeout(rq.response.request.id);
3463
+
3464
+ if (rq.heartbeatTimer) {
3465
+ clearTimeout(rq.heartbeatTimer);
3466
+ }
3467
+ }
3468
+ }
3469
+ }
3470
+ });
3471
+
3472
+ atmosphere.util.on(window, "online", function () {
3473
+ atmosphere.util.debug(new Date() + " Atmosphere: online event");
3474
+ if (requests.length > 0) {
3475
+ for (var i = 0; i < requests.length; i++) {
3476
+ if(requests[i].request.handleOnlineOffline) {
3477
+ requests[i].init();
3478
+ requests[i].execute();
3479
+ }
3480
+ }
3481
+ }
3482
+ offline = false;
3483
+ });
3484
+
3485
+ return atmosphere;
3486
+ }));
3487
+ /* jshint eqnull:true, noarg:true, noempty:true, eqeqeq:true, evil:true, laxbreak:true, undef:true, browser:true, indent:false, maxerr:50 */