binnacle 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 */