rhet-butler 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/bin/rhet-butler +5 -0
  2. data/default-configuration/assets/apple-touch-icon-precomposed.png +0 -0
  3. data/default-configuration/assets/apple-touch-icon.png +0 -0
  4. data/default-configuration/assets/favicon.ico +0 -0
  5. data/default-configuration/assets/fonts/opensans/v6/MTP_ySUJH_bn48VBG8sNSonF5uFdDttMLvmWuJdhhgs.ttf +0 -0
  6. data/default-configuration/assets/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxi8cqLH4MEiSE0ROcU-qHOA.ttf +0 -0
  7. data/default-configuration/assets/fonts/opensans/v6/cJZKeOuBrn4kERxqtaUH3aCWcynf_cDxXwCLxiixG1c.ttf +0 -0
  8. data/default-configuration/assets/fonts/opensans/v6/xjAJXh38I15wypJXxuGMBp0EAVxt0G0biEntp43Qt6E.ttf +0 -0
  9. data/default-configuration/assets/fonts/ptsans/v5/0XxGQsSc1g4rdRdjJKZrNC3USBnSvpkopQaUR-2r7iU.ttf +0 -0
  10. data/default-configuration/assets/fonts/ptsans/v5/FUDHvzEKSJww3kCxuiAo2A.ttf +0 -0
  11. data/default-configuration/assets/fonts/ptsans/v5/PIPMHY90P7jtyjpXuZ2cLKCWcynf_cDxXwCLxiixG1c.ttf +0 -0
  12. data/default-configuration/assets/fonts/ptsans/v5/lILlYDvubYemzYzN7GbLkInF5uFdDttMLvmWuJdhhgs.ttf +0 -0
  13. data/default-configuration/assets/fonts/ptserif/v5/03aPdn7fFF3H6ngCgAlQzC3USBnSvpkopQaUR-2r7iU.ttf +0 -0
  14. data/default-configuration/assets/fonts/ptserif/v5/EgBlzoNBIHxNPCMwXaAhYPesZW2xOQ-xsNqO47m55DA.ttf +0 -0
  15. data/default-configuration/assets/fonts/ptserif/v5/Foydq9xJp--nfYIx2TBz9fEr6Hm6RMS0v1dtXsGir4g.ttf +0 -0
  16. data/default-configuration/assets/fonts/ptserif/v5/QABk9IxT-LFTJ_dQzv7xpJ0EAVxt0G0biEntp43Qt6E.ttf +0 -0
  17. data/default-configuration/assets/javascript/highlight.js/LICENSE +24 -0
  18. data/default-configuration/assets/javascript/highlight.js/README.md +143 -0
  19. data/default-configuration/assets/javascript/highlight.js/README.ru.md +149 -0
  20. data/default-configuration/assets/javascript/highlight.js/classref.txt +568 -0
  21. data/default-configuration/assets/javascript/highlight.pack.js +1 -0
  22. data/default-configuration/assets/javascript/impress.js +800 -0
  23. data/default-configuration/assets/javascript/sockjs-0.2.1.js +2014 -0
  24. data/default-configuration/assets/javascript/sockjs-0.3.js +2314 -0
  25. data/default-configuration/assets/rhet-butler.jpg +0 -0
  26. data/default-configuration/assets/stylesheets/google-open-sans.css +72 -0
  27. data/default-configuration/assets/stylesheets/highlight/solarized_dark.css +88 -0
  28. data/default-configuration/assets/stylesheets/presentation.css +483 -0
  29. data/default-configuration/assets/stylesheets/presenter/presentation.css +477 -0
  30. data/default-configuration/assets/stylesheets/setup.css +0 -0
  31. data/default-configuration/common/config.yaml +4 -0
  32. data/default-configuration/common/templates/header-javascript.html +3 -0
  33. data/default-configuration/common/templates/presentation.html.erb +44 -0
  34. data/default-configuration/common/templates/presenter-qr.html.erb +77 -0
  35. data/default-configuration/common/templates/stylesheets.html.erb +3 -0
  36. data/default-configuration/presenter/config.yaml +7 -0
  37. data/default-configuration/presenter/templates/live-javascript.html.erb +26 -0
  38. data/default-configuration/presenter/templates/slide-notes.html.erb +3 -0
  39. data/default-configuration/presenter/templates/stylesheets.html.erb +3 -0
  40. data/default-configuration/viewer/config.yaml +7 -0
  41. data/default-configuration/viewer/templates/live-javascript.html.erb +16 -0
  42. data/default-configuration/viewer/templates/slide-notes.html.erb +0 -0
  43. data/lib/rhet-butler/arrangement.rb +78 -0
  44. data/lib/rhet-butler/command-line.rb +49 -0
  45. data/lib/rhet-butler/configuration.rb +76 -0
  46. data/lib/rhet-butler/file-manager.rb +126 -0
  47. data/lib/rhet-butler/html-generator.rb +29 -0
  48. data/lib/rhet-butler/layout-rule.rb +57 -0
  49. data/lib/rhet-butler/messaging.rb +58 -0
  50. data/lib/rhet-butler/slide-group.rb +56 -0
  51. data/lib/rhet-butler/slide-includer.rb +34 -0
  52. data/lib/rhet-butler/slide-loader.rb +361 -0
  53. data/lib/rhet-butler/slide.rb +164 -0
  54. data/lib/rhet-butler/static-generator.rb +44 -0
  55. data/lib/rhet-butler/web/assets-app.rb +40 -0
  56. data/lib/rhet-butler/web/main-app.rb +139 -0
  57. data/lib/rhet-butler/web/presentation-app.rb +50 -0
  58. data/lib/rhet-butler/web/qr-display-app.rb +37 -0
  59. data/lib/rhet-butler/yaml-schema.rb +9 -0
  60. data/lib/rhet-butler/yaml-type.rb +23 -0
  61. data/lib/rhet-butler.rb +2 -0
  62. data/spec/arrangements.rb +76 -0
  63. data/spec/html-generation.rb +48 -0
  64. data/spec/presentation-view.rb +72 -0
  65. data/spec/rhet-butler.rb +59 -0
  66. data/spec/slide-loader.rb +51 -0
  67. data/spec/slide-processing.rb +73 -0
  68. data/spec_support/gem_test_suite.rb +17 -0
  69. metadata +324 -0
@@ -0,0 +1,2014 @@
1
+ /* SockJS client, version 0.2.1, http://sockjs.org, MIT License
2
+
3
+ Copyright (C) 2011 VMware, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+ */
23
+
24
+ // JSON2 by Douglas Crockford (minified).
25
+ var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c<f;c+=1)h[c]=str(c,i)||"null";e=h.length===0?"[]":gap?"[\n"+gap+h.join(",\n"+gap)+"\n"+g+"]":"["+h.join(",")+"]",gap=g;return e}if(rep&&typeof rep=="object"){f=rep.length;for(c=0;c<f;c+=1)typeof rep[c]=="string"&&(d=rep[c],e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e))}else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));e=h.length===0?"{}":gap?"{\n"+gap+h.join(",\n"+gap)+"\n"+g+"}":"{"+h.join(",")+"}",gap=g;return e}}function quote(a){escapable.lastIndex=0;return escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return typeof b=="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function f(a){return a<10?"0"+a:a}"use strict",typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(a){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()});var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;typeof JSON.stringify!="function"&&(JSON.stringify=function(a,b,c){var d;gap="",indent="";if(typeof c=="number")for(d=0;d<c;d+=1)indent+=" ";else typeof c=="string"&&(indent=c);rep=b;if(!b||typeof b=="function"||typeof b=="object"&&typeof b.length=="number")return str("",{"":a});throw new Error("JSON.stringify")}),typeof JSON.parse!="function"&&(JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&typeof e=="object")for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver=="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")})}()
26
+
27
+
28
+ // [*] Including lib/index.js
29
+ // Public object
30
+ SockJS = (function(){
31
+ var _document = document;
32
+ var _window = window;
33
+ var utils = {};
34
+
35
+
36
+ // [*] Including lib/reventtarget.js
37
+ /* Simplified implementation of DOM2 EventTarget.
38
+ * http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget
39
+ */
40
+ var REventTarget = function() {};
41
+ REventTarget.prototype.addEventListener = function (eventType, listener) {
42
+ if(!this._listeners) {
43
+ this._listeners = {};
44
+ }
45
+ if(!(eventType in this._listeners)) {
46
+ this._listeners[eventType] = [];
47
+ }
48
+ var arr = this._listeners[eventType];
49
+ if(utils.arrIndexOf(arr, listener) === -1) {
50
+ arr.push(listener);
51
+ }
52
+ return;
53
+ };
54
+
55
+ REventTarget.prototype.removeEventListener = function (eventType, listener) {
56
+ if(!(this._listeners && (eventType in this._listeners))) {
57
+ return;
58
+ }
59
+ var arr = this._listeners[eventType];
60
+ var idx = utils.arrIndexOf(arr, listener);
61
+ if (idx !== -1) {
62
+ if(arr.length > 1) {
63
+ this._listeners[eventType] = arr.slice(0, idx).concat( arr.slice(idx+1) );
64
+ } else {
65
+ delete this._listeners[eventType];
66
+ }
67
+ return;
68
+ }
69
+ return;
70
+ };
71
+
72
+ REventTarget.prototype.dispatchEvent = function (event) {
73
+ var t = event.type;
74
+ var args = Array.prototype.slice.call(arguments, 0);
75
+ if (this['on'+t]) {
76
+ this['on'+t].apply(this, args);
77
+ }
78
+ if (this._listeners && t in this._listeners) {
79
+ for(var i=0; i < this._listeners[t].length; i++) {
80
+ this._listeners[t][i].apply(this, args);
81
+ }
82
+ }
83
+ };
84
+ // [*] End of lib/reventtarget.js
85
+
86
+
87
+ // [*] Including lib/simpleevent.js
88
+ var SimpleEvent = function(type, obj) {
89
+ this.type = type;
90
+ if (typeof obj !== 'undefined') {
91
+ for(var k in obj) {
92
+ if (!obj.hasOwnProperty(k)) continue;
93
+ this[k] = obj[k];
94
+ }
95
+ }
96
+ };
97
+
98
+ SimpleEvent.prototype.toString = function() {
99
+ var r = [];
100
+ for(var k in this) {
101
+ if (!this.hasOwnProperty(k)) continue;
102
+ var v = this[k];
103
+ if (typeof v === 'function') v = '[function]';
104
+ r.push(k + '=' + v);
105
+ }
106
+ return 'SimpleEvent(' + r.join(', ') + ')';
107
+ };
108
+ // [*] End of lib/simpleevent.js
109
+
110
+
111
+ // [*] Including lib/eventemitter.js
112
+ var EventEmitter = function(events) {
113
+ this.events = events || [];
114
+ };
115
+ EventEmitter.prototype.emit = function(type) {
116
+ var that = this;
117
+ var args = Array.prototype.slice.call(arguments, 1);
118
+ if (!that.nuked && that['on'+type]) {
119
+ that['on'+type].apply(that, args);
120
+ }
121
+ if (utils.arrIndexOf(that.events, type) === -1) {
122
+ utils.log('Event ' + JSON.stringify(type) +
123
+ ' not listed ' + JSON.stringify(that.events) +
124
+ ' in ' + that);
125
+ }
126
+ };
127
+
128
+ EventEmitter.prototype.nuke = function(type) {
129
+ var that = this;
130
+ that.nuked = true;
131
+ for(var i=0; i<that.events.length; i++) {
132
+ delete that[that.events[i]];
133
+ }
134
+ };
135
+ // [*] End of lib/eventemitter.js
136
+
137
+
138
+ // [*] Including lib/utils.js
139
+ var random_string_chars = 'abcdefghijklmnopqrstuvwxyz0123456789_';
140
+ utils.random_string = function(length, max) {
141
+ max = max || random_string_chars.length;
142
+ var i, ret = [];
143
+ for(i=0; i < length; i++) {
144
+ ret.push( random_string_chars.substr(Math.floor(Math.random() * max),1) );
145
+ }
146
+ return ret.join('');
147
+ };
148
+ utils.random_number = function(max) {
149
+ return Math.floor(Math.random() * max);
150
+ };
151
+ utils.random_number_string = function(max) {
152
+ var t = (''+(max - 1)).length;
153
+ var p = Array(t+1).join('0');
154
+ return (p + utils.random_number(max)).slice(-t);
155
+ };
156
+
157
+ // Assuming that url looks like: http://asdasd:111/asd
158
+ utils.getOrigin = function(url) {
159
+ url += '/';
160
+ var parts = url.split('/').slice(0, 3);
161
+ return parts.join('/');
162
+ };
163
+
164
+ utils.isSameOriginUrl = function(url) {
165
+ // location.origin would do, but it's not available in some
166
+ // browsers.
167
+ var o = _window.location.href.split('/').slice(0,3).join('/');
168
+ return url.slice(0, o.length) === o;
169
+ };
170
+
171
+ utils.getParentDomain = function(url) {
172
+ // ipv4 ip address
173
+ if (/^[0-9.]*$/.test(url)) return url;
174
+ // ipv6 ip address
175
+ if (/^\[/.test(url)) return url;
176
+ // no dots
177
+ if (!(/[.]/.test(url))) return url;
178
+
179
+ var parts = url.split('.').slice(1);
180
+ return parts.join('.');
181
+ };
182
+
183
+ utils.objectExtend = function(dst, src) {
184
+ for(var k in src) {
185
+ if (src.hasOwnProperty(k)) {
186
+ dst[k] = src[k];
187
+ }
188
+ }
189
+ return dst;
190
+ };
191
+
192
+ var WPrefix = '_jp';
193
+
194
+ utils.polluteGlobalNamespace = function() {
195
+ if (!(WPrefix in _window)) {
196
+ _window[WPrefix] = {};
197
+ }
198
+ };
199
+
200
+ utils.closeFrame = function (code, reason) {
201
+ return 'c'+JSON.stringify([code, reason]);
202
+ };
203
+
204
+ utils.userSetCode = function (code) {
205
+ return code === 1000 || (code >= 3000 && code <= 4999);
206
+ };
207
+
208
+ // See: http://www.erg.abdn.ac.uk/~gerrit/dccp/notes/ccid2/rto_estimator/
209
+ // and RFC 2988.
210
+ utils.countRTO = function (rtt) {
211
+ var rto;
212
+ if (rtt > 100) {
213
+ rto = 3 * rtt; // rto > 300msec
214
+ } else {
215
+ rto = rtt + 200; // 200msec < rto <= 300msec
216
+ }
217
+ return rto;
218
+ }
219
+
220
+ utils.log = function() {
221
+ if (_window.console && console.log && console.log.apply) {
222
+ console.log.apply(console, arguments);
223
+ }
224
+ };
225
+
226
+ utils.bind = function(fun, that) {
227
+ if (fun.bind) {
228
+ return fun.bind(that);
229
+ } else {
230
+ return function() {
231
+ return fun.apply(that, arguments);
232
+ };
233
+ }
234
+ };
235
+
236
+ utils.amendUrl = function(url) {
237
+ var dl = _document.location;
238
+ if (!url) {
239
+ throw new Error('Wrong url for SockJS');
240
+ }
241
+ // '//abc' --> 'http://abc'
242
+ if (url.indexOf('//') === 0) {
243
+ url = dl.protocol + url;
244
+ }
245
+ // '/abc' --> 'http://localhost:80/abc'
246
+ if (url.indexOf('/') === 0) {
247
+ url = dl.protocol + '//' + dl.host + url;
248
+ }
249
+ // strip trailing slashes
250
+ url = url.replace(/[/]+$/,'');
251
+ return url;
252
+ };
253
+
254
+ // IE doesn't support [].indexOf.
255
+ utils.arrIndexOf = function(arr, obj){
256
+ for(var i=0; i < arr.length; i++){
257
+ if(arr[i] === obj){
258
+ return i;
259
+ }
260
+ }
261
+ return -1;
262
+ };
263
+
264
+ utils.arrSkip = function(arr, obj) {
265
+ var idx = utils.arrIndexOf(arr, obj);
266
+ if (idx === -1) {
267
+ return arr.slice();
268
+ } else {
269
+ var dst = arr.slice(0, idx);
270
+ return dst.concat(arr.slice(idx+1));
271
+ }
272
+ };
273
+
274
+ // Via: https://gist.github.com/1133122/2121c601c5549155483f50be3da5305e83b8c5df
275
+ utils.isArray = Array.isArray || function(value) {
276
+ return {}.toString.call(value).indexOf('Array') >= 0
277
+ };
278
+
279
+ utils.delay = function(t, fun) {
280
+ if(typeof t === 'function') {
281
+ fun = t;
282
+ t = 0;
283
+ }
284
+ return setTimeout(fun, t);
285
+ };
286
+
287
+
288
+ // Chars worth escaping, as defined by Douglas Crockford:
289
+ // https://github.com/douglascrockford/JSON-js/blob/47a9882cddeb1e8529e07af9736218075372b8ac/json2.js#L196
290
+ var json_escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
291
+ json_lookup = {
292
+ "\u0000":"\\u0000","\u0001":"\\u0001","\u0002":"\\u0002","\u0003":"\\u0003",
293
+ "\u0004":"\\u0004","\u0005":"\\u0005","\u0006":"\\u0006","\u0007":"\\u0007",
294
+ "\b":"\\b","\t":"\\t","\n":"\\n","\u000b":"\\u000b","\f":"\\f","\r":"\\r",
295
+ "\u000e":"\\u000e","\u000f":"\\u000f","\u0010":"\\u0010","\u0011":"\\u0011",
296
+ "\u0012":"\\u0012","\u0013":"\\u0013","\u0014":"\\u0014","\u0015":"\\u0015",
297
+ "\u0016":"\\u0016","\u0017":"\\u0017","\u0018":"\\u0018","\u0019":"\\u0019",
298
+ "\u001a":"\\u001a","\u001b":"\\u001b","\u001c":"\\u001c","\u001d":"\\u001d",
299
+ "\u001e":"\\u001e","\u001f":"\\u001f","\"":"\\\"","\\":"\\\\",
300
+ "\u007f":"\\u007f","\u0080":"\\u0080","\u0081":"\\u0081","\u0082":"\\u0082",
301
+ "\u0083":"\\u0083","\u0084":"\\u0084","\u0085":"\\u0085","\u0086":"\\u0086",
302
+ "\u0087":"\\u0087","\u0088":"\\u0088","\u0089":"\\u0089","\u008a":"\\u008a",
303
+ "\u008b":"\\u008b","\u008c":"\\u008c","\u008d":"\\u008d","\u008e":"\\u008e",
304
+ "\u008f":"\\u008f","\u0090":"\\u0090","\u0091":"\\u0091","\u0092":"\\u0092",
305
+ "\u0093":"\\u0093","\u0094":"\\u0094","\u0095":"\\u0095","\u0096":"\\u0096",
306
+ "\u0097":"\\u0097","\u0098":"\\u0098","\u0099":"\\u0099","\u009a":"\\u009a",
307
+ "\u009b":"\\u009b","\u009c":"\\u009c","\u009d":"\\u009d","\u009e":"\\u009e",
308
+ "\u009f":"\\u009f","\u00ad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601",
309
+ "\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f",
310
+ "\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d",
311
+ "\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029",
312
+ "\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d",
313
+ "\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061",
314
+ "\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065",
315
+ "\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069",
316
+ "\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d",
317
+ "\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0",
318
+ "\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4",
319
+ "\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8",
320
+ "\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc",
321
+ "\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"};
322
+
323
+ // Some extra characters that Chrome gets wrong, and substitutes with
324
+ // something else on the wire.
325
+ var extra_escapable = /[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,
326
+ extra_lookup;
327
+
328
+ // JSON Quote string. Use native implementation when possible.
329
+ var JSONQuote = (JSON && JSON.stringify) || function(string) {
330
+ json_escapable.lastIndex = 0;
331
+ if (json_escapable.test(string)) {
332
+ string = string.replace(json_escapable, function(a) {
333
+ return json_lookup[a];
334
+ });
335
+ }
336
+ return '"' + string + '"';
337
+ };
338
+
339
+ // This may be quite slow, so let's delay until user actually uses bad
340
+ // characters.
341
+ var unroll_lookup = function(escapable) {
342
+ var i;
343
+ var unrolled = {}
344
+ var c = []
345
+ for(i=0; i<65536; i++) {
346
+ c.push( String.fromCharCode(i) );
347
+ }
348
+ escapable.lastIndex = 0;
349
+ c.join('').replace(escapable, function (a) {
350
+ unrolled[ a ] = '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
351
+ return '';
352
+ });
353
+ escapable.lastIndex = 0;
354
+ return unrolled;
355
+ };
356
+
357
+ // Quote string, also taking care of unicode characters that browsers
358
+ // often break. Especially, take care of unicode surrogates:
359
+ // http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates
360
+ utils.quote = function(string) {
361
+ var quoted = JSONQuote(string);
362
+
363
+ // In most cases this should be very fast and good enough.
364
+ extra_escapable.lastIndex = 0;
365
+ if(!extra_escapable.test(quoted)) {
366
+ return quoted;
367
+ }
368
+
369
+ if(!extra_lookup) extra_lookup = unroll_lookup(extra_escapable);
370
+
371
+ return quoted.replace(extra_escapable, function(a) {
372
+ return extra_lookup[a];
373
+ });
374
+ }
375
+
376
+ var _all_protocols = ['websocket',
377
+ 'xdr-streaming',
378
+ 'xhr-streaming',
379
+ 'iframe-eventsource',
380
+ 'iframe-htmlfile',
381
+ 'xdr-polling',
382
+ 'xhr-polling',
383
+ 'iframe-xhr-polling',
384
+ 'jsonp-polling'];
385
+
386
+ utils.probeProtocols = function() {
387
+ var probed = {};
388
+ for(var i=0; i<_all_protocols.length; i++) {
389
+ var protocol = _all_protocols[i];
390
+ // User can have a typo in protocol name.
391
+ probed[protocol] = SockJS[protocol] &&
392
+ SockJS[protocol].enabled();
393
+ }
394
+ return probed;
395
+ };
396
+
397
+ utils.detectProtocols = function(probed, protocols_whitelist, info) {
398
+ var pe = {},
399
+ protocols = [];
400
+ if (!protocols_whitelist) protocols_whitelist = _all_protocols;
401
+ for(var i=0; i<protocols_whitelist.length; i++) {
402
+ var protocol = protocols_whitelist[i];
403
+ pe[protocol] = probed[protocol];
404
+ }
405
+ var maybe_push = function(protos) {
406
+ var proto = protos.shift();
407
+ if (pe[proto]) {
408
+ protocols.push(proto);
409
+ } else {
410
+ if (protos.length > 0) {
411
+ maybe_push(protos);
412
+ }
413
+ }
414
+ }
415
+
416
+ // 1. Websocket
417
+ if (info.websocket !== false) {
418
+ maybe_push(['websocket']);
419
+ }
420
+
421
+ // 2. Streaming
422
+ if (pe['xdr-streaming'] && !info.cookie_needed) {
423
+ protocols.push('xdr-streaming');
424
+ } else {
425
+ maybe_push(['xhr-streaming',
426
+ 'iframe-eventsource',
427
+ 'iframe-htmlfile']);
428
+ }
429
+
430
+ // 3. Polling
431
+ if (pe['xdr-polling'] && !info.cookie_needed) {
432
+ protocols.push('xdr-polling');
433
+ } else {
434
+ maybe_push(['xhr-polling',
435
+ 'iframe-xhr-polling',
436
+ 'jsonp-polling']);
437
+ }
438
+ return protocols;
439
+ }
440
+ // [*] End of lib/utils.js
441
+
442
+
443
+ // [*] Including lib/dom.js
444
+ // May be used by htmlfile jsonp and transports.
445
+ var MPrefix = '_sockjs_global';
446
+ utils.createHook = function() {
447
+ var window_id = 'a' + utils.random_string(8);
448
+ if (!(MPrefix in _window)) {
449
+ var map = {};
450
+ _window[MPrefix] = function(window_id) {
451
+ if (!(window_id in map)) {
452
+ map[window_id] = {
453
+ id: window_id,
454
+ del: function() {delete map[window_id];}
455
+ };
456
+ }
457
+ return map[window_id];
458
+ }
459
+ }
460
+ return _window[MPrefix](window_id);
461
+ };
462
+
463
+
464
+
465
+ utils.attachMessage = function(listener) {
466
+ utils.attachEvent('message', listener);
467
+ };
468
+ utils.attachEvent = function(event, listener) {
469
+ if (typeof _window.addEventListener !== 'undefined') {
470
+ _window.addEventListener(event, listener, false);
471
+ } else {
472
+ // IE quirks.
473
+ // According to: http://stevesouders.com/misc/test-postmessage.php
474
+ // the message gets delivered only to 'document', not 'window'.
475
+ _document.attachEvent("on" + event, listener);
476
+ // I get 'window' for ie8.
477
+ _window.attachEvent("on" + event, listener);
478
+ }
479
+ };
480
+
481
+ utils.detachMessage = function(listener) {
482
+ utils.detachEvent('message', listener);
483
+ };
484
+ utils.detachEvent = function(event, listener) {
485
+ if (typeof _window.addEventListener !== 'undefined') {
486
+ _window.removeEventListener(event, listener, false);
487
+ } else {
488
+ _document.detachEvent("on" + event, listener);
489
+ _window.detachEvent("on" + event, listener);
490
+ }
491
+ };
492
+
493
+ var on_unload = {};
494
+ utils.unload_add = function(listener) {
495
+ var ref = utils.random_string(8);
496
+ on_unload[ref] = listener;
497
+ return ref;
498
+ };
499
+ utils.unload_del = function(ref) {
500
+ if (ref in on_unload)
501
+ delete on_unload[ref];
502
+ };
503
+
504
+ utils.attachEvent('unload', function() {
505
+ for(var k in on_unload) {
506
+ on_unload[k]();
507
+ };
508
+ });
509
+
510
+ utils.createIframe = function (iframe_url, error_callback) {
511
+ var iframe = _document.createElement('iframe');
512
+ var tref;
513
+ var unattach = function() {
514
+ clearTimeout(tref);
515
+ // Explorer had problems with that.
516
+ try {iframe.onload = null;} catch (x) {}
517
+ iframe.onerror = null;
518
+ };
519
+ var cleanup = function() {
520
+ if (iframe) {
521
+ unattach();
522
+ // This is required, in order to force ie7 to fire the
523
+ // onunload event. Setting .src must happen before the
524
+ // removeChild step.
525
+ iframe.src = "about:blank";
526
+ // This timeout makes chrome fire onbeforeunload event
527
+ // within iframe. Without the timeout it goes straight to
528
+ // onunload.
529
+ setTimeout(function() {
530
+ if(iframe) {
531
+ iframe.parentNode.removeChild(iframe);
532
+ }
533
+ iframe = null;
534
+ }, 0);
535
+ utils.detachEvent('unload', cleanup);
536
+ }
537
+ };
538
+ var onerror = function(r) {
539
+ if (iframe) {
540
+ cleanup();
541
+ error_callback(r);
542
+ }
543
+ };
544
+ iframe.src = iframe_url;
545
+ iframe.style.display = 'none';
546
+ iframe.style.position = 'absolute';
547
+ iframe.onerror = function(){onerror('onerror');};
548
+ iframe.onload = function() {
549
+ // `onload` is triggered before scripts on the iframe are
550
+ // executed. Give it few seconds to actually load stuff.
551
+ clearTimeout(tref);
552
+ tref = setTimeout(function(){onerror('onload timeout');}, 2000);
553
+ };
554
+ _document.body.appendChild(iframe);
555
+ tref = setTimeout(function(){onerror('timeout');}, 5000);
556
+ utils.attachEvent('unload', cleanup);
557
+ return {
558
+ iframe: iframe,
559
+ cleanup: cleanup,
560
+ loaded: unattach
561
+ };
562
+ };
563
+
564
+ utils.createHtmlfile = function (iframe_url, error_callback) {
565
+ var doc = new ActiveXObject('htmlfile');
566
+ var tref;
567
+ var iframe;
568
+ var unattach = function() {
569
+ clearTimeout(tref);
570
+ };
571
+ var cleanup = function() {
572
+ if (doc) {
573
+ unattach();
574
+ utils.detachEvent('unload', cleanup);
575
+ try {
576
+ iframe.src = "about:blank";
577
+ } catch (x) {}
578
+ iframe.parentNode.removeChild(iframe);
579
+ iframe = doc = null;
580
+ CollectGarbage();
581
+ }
582
+ };
583
+ var onerror = function(r) {
584
+ if (doc) {
585
+ cleanup();
586
+ error_callback(r);
587
+ }
588
+ };
589
+
590
+ doc.open();
591
+ doc.write('<html><s' + 'cript>' +
592
+ 'document.domain="' + document.domain + '";' +
593
+ '</s' + 'cript></html>');
594
+ doc.close();
595
+ doc.parentWindow[WPrefix] = _window[WPrefix];
596
+ var c = doc.createElement('div');
597
+ doc.body.appendChild(c);
598
+ iframe = doc.createElement('iframe');
599
+ c.appendChild(iframe);
600
+ iframe.src = iframe_url;
601
+ tref = setTimeout(function(){onerror('timeout');}, 5000);
602
+ utils.attachEvent('unload', cleanup);
603
+ return {
604
+ iframe: iframe,
605
+ cleanup: cleanup,
606
+ loaded: unattach
607
+ };
608
+ };
609
+ // [*] End of lib/dom.js
610
+
611
+
612
+ // [*] Including lib/dom2.js
613
+ var XHRObject = utils.XHRObject = function(method, url, payload) {
614
+ var that = this;
615
+ utils.delay(function(){that._start(method, url, payload);});
616
+ };
617
+
618
+ XHRObject.prototype = new EventEmitter(['chunk', 'finish']);
619
+
620
+ XHRObject.prototype._start = function(method, url, payload) {
621
+ var that = this;
622
+ try {
623
+ that.xhr = new _window.ActiveXObject('Microsoft.XMLHTTP');
624
+ // IE caches POSTs
625
+ url += ((url.indexOf('?') === -1) ? '?' : '&') + 't='+(+new Date);
626
+ } catch(x) {};
627
+ if (!that.xhr) {
628
+ that.xhr = new XMLHttpRequest();
629
+ }
630
+
631
+ // Explorer tends to keep connection open, even after the
632
+ // tab gets closed: http://bugs.jquery.com/ticket/5280
633
+ that.unload_ref = utils.unload_add(function(){that._cleanup(true);});
634
+ try {
635
+ that.xhr.open(method, url, true);
636
+ } catch(e) {
637
+ // IE raises an exception on wrong port.
638
+ that.emit('finish', 0, '');
639
+ that._cleanup();
640
+ return;
641
+ };
642
+
643
+ if ('withCredentials' in that.xhr) {
644
+ that.xhr.withCredentials = 'true';
645
+ }
646
+
647
+ that.xhr.onreadystatechange = function() {
648
+ if (that.xhr) {
649
+ var x = that.xhr;
650
+ switch (x.readyState) {
651
+ case 3:
652
+ // IE doesn't like peeking into responseText or status on
653
+ // XHR and readystate=3
654
+ try {
655
+ var status = x.status;
656
+ var text = x.responseText;
657
+ that.emit('chunk', status, text);
658
+ } catch (x) {};
659
+ break;
660
+ case 4:
661
+ that.emit('finish', x.status, x.responseText);
662
+ that._cleanup(false);
663
+ break;
664
+ }
665
+ }
666
+ };
667
+ that.xhr.send(payload);
668
+ };
669
+
670
+ XHRObject.prototype._cleanup = function(abort) {
671
+ var that = this;
672
+ if (!that.xhr) return;
673
+ utils.unload_del(that.unload_ref);
674
+
675
+ // IE needs this field to be a function
676
+ that.xhr.onreadystatechange = function(){};
677
+
678
+ if (abort) {
679
+ try {
680
+ that.xhr.abort();
681
+ } catch(x) {};
682
+ }
683
+ that.unload_ref = that.xhr = null;
684
+ };
685
+
686
+ XHRObject.prototype.close = function() {
687
+ var that = this;
688
+ that.nuke();
689
+ that._cleanup(true);
690
+ };
691
+
692
+
693
+ // References:
694
+ // http://ajaxian.com/archives/100-line-ajax-wrapper
695
+ // http://msdn.microsoft.com/en-us/library/cc288060(v=VS.85).aspx
696
+ var XDRObject = utils.XDRObject = function(method, url, payload) {
697
+ var that = this;
698
+ utils.delay(function(){that._start(method, url, payload);});
699
+ };
700
+ XDRObject.prototype = new EventEmitter(['chunk', 'finish']);
701
+ XDRObject.prototype._start = function(method, url, payload) {
702
+ var that = this;
703
+ var xdr = new XDomainRequest();
704
+ // IE caches even POSTs
705
+ url += ((url.indexOf('?') === -1) ? '?' : '&') + 't='+(+new Date);
706
+
707
+ var onerror = xdr.ontimeout = xdr.onerror = function() {
708
+ that.emit('finish', 0, '');
709
+ that._cleanup(false);
710
+ };
711
+ xdr.onprogress = function() {
712
+ that.emit('chunk', 200, xdr.responseText);
713
+ };
714
+ xdr.onload = function() {
715
+ that.emit('finish', 200, xdr.responseText);
716
+ that._cleanup(false);
717
+ };
718
+ that.xdr = xdr;
719
+ that.unload_ref = utils.unload_add(function(){that._cleanup(true);});
720
+ try {
721
+ // Fails with AccessDenied if port number is bogus
722
+ that.xdr.open(method, url);
723
+ that.xdr.send(payload);
724
+ } catch(x) {
725
+ onerror();
726
+ }
727
+ };
728
+
729
+ XDRObject.prototype._cleanup = function(abort) {
730
+ var that = this;
731
+ if (!that.xdr) return;
732
+ utils.unload_del(that.unload_ref);
733
+
734
+ that.xdr.ontimeout = that.xdr.onerror = that.xdr.onprogress =
735
+ that.xdr.onload = null;
736
+ if (abort) {
737
+ try {
738
+ that.xdr.abort();
739
+ } catch(x) {};
740
+ }
741
+ that.unload_ref = that.xdr = null;
742
+ };
743
+
744
+ XDRObject.prototype.close = function() {
745
+ var that = this;
746
+ that.nuke();
747
+ that._cleanup(true);
748
+ };
749
+
750
+ // 1. Is natively via XHR
751
+ // 2. Is natively via XDR
752
+ // 3. Nope, but postMessage is there so it should work via the Iframe.
753
+ // 4. Nope, sorry.
754
+ utils.isXHRCorsCapable = function() {
755
+ if (_window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()) {
756
+ return 1;
757
+ }
758
+ if (_window.XDomainRequest) {
759
+ return 2;
760
+ }
761
+ if (IframeTransport.enabled()) {
762
+ return 3;
763
+ }
764
+ return 4;
765
+ };
766
+ // [*] End of lib/dom2.js
767
+
768
+
769
+ // [*] Including lib/sockjs.js
770
+ var SockJS = function(url, dep_protocols_whitelist, options) {
771
+ var that = this, protocols_whitelist;
772
+ that._options = {devel: false, debug: false, protocols_whitelist: [],
773
+ info: undefined, rtt: undefined};
774
+ if (options) {
775
+ utils.objectExtend(that._options, options);
776
+ }
777
+ that._base_url = utils.amendUrl(url);
778
+ that._server = that._options.server || utils.random_number_string(1000);
779
+ if (that._options.protocols_whitelist &&
780
+ that._options.protocols_whitelist.length) {
781
+ protocols_whitelist = that._options.protocols_whitelist;
782
+ } else {
783
+ // Deprecated API
784
+ if (typeof dep_protocols_whitelist === 'string' &&
785
+ dep_protocols_whitelist.length > 0) {
786
+ protocols_whitelist = [dep_protocols_whitelist];
787
+ } else if (utils.isArray(dep_protocols_whitelist)) {
788
+ protocols_whitelist = dep_protocols_whitelist
789
+ } else {
790
+ protocols_whitelist = null;
791
+ }
792
+ if (protocols_whitelist) {
793
+ that._debug('Deprecated API: Use "protocols_whitelist" option ' +
794
+ 'instead of supplying protocol list as a second ' +
795
+ 'parameter to SockJS constructor.');
796
+ }
797
+ }
798
+ that._protocols = [];
799
+ that.protocol = null;
800
+ that.readyState = SockJS.CONNECTING;
801
+ var ir = createInfoReceiver(that._base_url);
802
+ ir.onfinish = function(info, rtt) {
803
+ if (info) {
804
+ if (that._options.info) {
805
+ // Override if user supplies the option
806
+ info = utils.objectExtend(info, that._options.info);
807
+ }
808
+ if (that._options.rtt) {
809
+ rtt = that._options.rtt;
810
+ }
811
+ that._applyInfo(info, rtt, protocols_whitelist);
812
+ that._didClose();
813
+ } else {
814
+ that._didClose(1002, 'Can\'t connect to server', true);
815
+ }
816
+ };
817
+ };
818
+ // Inheritance
819
+ SockJS.prototype = new REventTarget();
820
+
821
+ SockJS.version = "0.2.1";
822
+
823
+ SockJS.CONNECTING = 0;
824
+ SockJS.OPEN = 1;
825
+ SockJS.CLOSING = 2;
826
+ SockJS.CLOSED = 3;
827
+
828
+ SockJS.prototype._debug = function() {
829
+ if (this._options.debug)
830
+ utils.log.apply(utils, arguments);
831
+ };
832
+
833
+ SockJS.prototype._dispatchOpen = function() {
834
+ var that = this;
835
+ if (that.readyState === SockJS.CONNECTING) {
836
+ if (that._transport_tref) {
837
+ clearTimeout(that._transport_tref);
838
+ that._transport_tref = null;
839
+ }
840
+ that.readyState = SockJS.OPEN;
841
+ that.dispatchEvent(new SimpleEvent("open"));
842
+ } else {
843
+ // The server might have been restarted, and lost track of our
844
+ // connection.
845
+ that._didClose(1006, "Server lost session");
846
+ }
847
+ };
848
+
849
+ SockJS.prototype._dispatchMessage = function(data) {
850
+ var that = this;
851
+ if (that.readyState !== SockJS.OPEN)
852
+ return;
853
+ that.dispatchEvent(new SimpleEvent("message", {data: data}));
854
+ };
855
+
856
+ SockJS.prototype._dispatchHeartbeat = function(data) {
857
+ var that = this;
858
+ if (that.readyState !== SockJS.OPEN)
859
+ return;
860
+ that.dispatchEvent(new SimpleEvent('heartbeat', {}));
861
+ };
862
+
863
+ SockJS.prototype._didClose = function(code, reason, force) {
864
+ var that = this;
865
+ if (that.readyState !== SockJS.CONNECTING &&
866
+ that.readyState !== SockJS.OPEN &&
867
+ that.readyState !== SockJS.CLOSING)
868
+ throw new Error('INVALID_STATE_ERR');
869
+ if (that._transport)
870
+ that._transport.doCleanup();
871
+ that._transport = null;
872
+
873
+ var close_event = new SimpleEvent("close", {
874
+ code: code,
875
+ reason: reason,
876
+ wasClean: utils.userSetCode(code)});
877
+
878
+ if (!utils.userSetCode(code) &&
879
+ that.readyState === SockJS.CONNECTING && !force) {
880
+ if (that._try_next_protocol(close_event)) {
881
+ return;
882
+ }
883
+ close_event = new SimpleEvent("close", {code: 2000,
884
+ reason: "All transports failed",
885
+ wasClean: false,
886
+ last_event: close_event});
887
+ }
888
+ that.readyState = SockJS.CLOSED;
889
+
890
+ utils.delay(function() {
891
+ that.dispatchEvent(close_event);
892
+ });
893
+ };
894
+
895
+ SockJS.prototype._didMessage = function(data) {
896
+ var that = this;
897
+ var type = data.slice(0, 1);
898
+ switch(type) {
899
+ case 'o':
900
+ that._dispatchOpen();
901
+ break;
902
+ case 'a':
903
+ var payload = JSON.parse(data.slice(1) || '[]');
904
+ for(var i=0; i < payload.length; i++){
905
+ that._dispatchMessage(payload[i]);
906
+ }
907
+ break;
908
+ case 'm':
909
+ var payload = JSON.parse(data.slice(1) || 'null');
910
+ that._dispatchMessage(payload);
911
+ break;
912
+ case 'c':
913
+ var payload = JSON.parse(data.slice(1) || '[]');
914
+ that._didClose(payload[0], payload[1]);
915
+ break;
916
+ case 'h':
917
+ that._dispatchHeartbeat();
918
+ break;
919
+ }
920
+ };
921
+
922
+ SockJS.prototype._try_next_protocol = function(close_event) {
923
+ var that = this;
924
+ if (that.protocol) {
925
+ that._debug('Closed transport:', that.protocol, ''+close_event);
926
+ that.protocol = null;
927
+ }
928
+ if (that._transport_tref) {
929
+ clearTimeout(that._transport_tref);
930
+ that._transport_tref = null;
931
+ }
932
+
933
+ while(1) {
934
+ var protocol = that.protocol = that._protocols.shift();
935
+ if (!protocol) {
936
+ return false;
937
+ }
938
+ // Some protocols require access to `body`, what if were in
939
+ // the `head`?
940
+ if (SockJS[protocol] &&
941
+ SockJS[protocol].need_body === true &&
942
+ !_document.body) {
943
+ that._protocols.unshift(protocol);
944
+ that.protocol = 'waiting-for-load';
945
+ utils.attachEvent('load', function(){
946
+ that._try_next_protocol();
947
+ });
948
+ return true;
949
+ }
950
+
951
+ if (!SockJS[protocol] ||
952
+ !SockJS[protocol].enabled(that._options)) {
953
+ that._debug('Skipping transport:', protocol);
954
+ } else {
955
+ var roundTrips = SockJS[protocol].roundTrips || 1;
956
+ var to = ((that._options.rto || 0) * roundTrips) || 5000;
957
+ that._transport_tref = utils.delay(to, function() {
958
+ if (that.readyState === SockJS.CONNECTING) {
959
+ // I can't understand how it is possible to run
960
+ // this timer, when the state is CLOSED, but
961
+ // apparently in IE everythin is possible.
962
+ that._didClose(2007, "Transport timeouted");
963
+ }
964
+ });
965
+
966
+ var connid = utils.random_string(8);
967
+ var trans_url = that._base_url + '/' + that._server + '/' + connid;
968
+ that._debug('Opening transport:', protocol, ' url:'+trans_url,
969
+ ' RTO:'+that._options.rto);
970
+ that._transport = new SockJS[protocol](that, trans_url,
971
+ that._base_url);
972
+ return true;
973
+ }
974
+ }
975
+ };
976
+
977
+ SockJS.prototype.close = function(code, reason) {
978
+ var that = this;
979
+ if (code && !utils.userSetCode(code))
980
+ throw new Error("INVALID_ACCESS_ERR");
981
+ if(that.readyState !== SockJS.CONNECTING &&
982
+ that.readyState !== SockJS.OPEN) {
983
+ return false;
984
+ }
985
+ that.readyState = SockJS.CLOSING;
986
+ that._didClose(code || 1000, reason || "Normal closure");
987
+ return true;
988
+ };
989
+
990
+ SockJS.prototype.send = function(data) {
991
+ var that = this;
992
+ if (that.readyState === SockJS.CONNECTING)
993
+ throw new Error('INVALID_STATE_ERR');
994
+ if (that.readyState === SockJS.OPEN) {
995
+ that._transport.doSend(utils.quote('' + data));
996
+ }
997
+ return true;
998
+ };
999
+
1000
+ SockJS.prototype._applyInfo = function(info, rtt, protocols_whitelist) {
1001
+ var that = this;
1002
+ that._options.info = info;
1003
+ that._options.rtt = rtt;
1004
+ that._options.rto = utils.countRTO(rtt);
1005
+ var probed = utils.probeProtocols();
1006
+ that._protocols = utils.detectProtocols(probed, protocols_whitelist, info);
1007
+ };
1008
+ // [*] End of lib/sockjs.js
1009
+
1010
+
1011
+ // [*] Including lib/trans-websocket.js
1012
+ var WebSocketTransport = SockJS.websocket = function(ri, trans_url) {
1013
+ var that = this;
1014
+ var url = trans_url + '/websocket';
1015
+ if (url.slice(0, 5) === 'https') {
1016
+ url = 'wss' + url.slice(5);
1017
+ } else {
1018
+ url = 'ws' + url.slice(4);
1019
+ }
1020
+ that.ri = ri;
1021
+ that.url = url;
1022
+ var Constructor = _window.WebSocket || _window.MozWebSocket;
1023
+
1024
+ that.ws = new Constructor(that.url);
1025
+ that.ws.onmessage = function(e) {
1026
+ that.ri._didMessage(e.data);
1027
+ };
1028
+ // Firefox has an interesting bug. If a websocket connection is
1029
+ // created after onbeforeunload, it stays alive even when user
1030
+ // navigates away from the page. In such situation let's lie -
1031
+ // let's not open the ws connection at all. See:
1032
+ // https://github.com/sockjs/sockjs-client/issues/28
1033
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=696085
1034
+ that.unload_ref = utils.unload_add(function(){that.ws.close()});
1035
+ that.ws.onclose = function() {
1036
+ that.ri._didMessage(utils.closeFrame(1006, "WebSocket connection broken"));
1037
+ };
1038
+ };
1039
+
1040
+ WebSocketTransport.prototype.doSend = function(data) {
1041
+ this.ws.send(data);
1042
+ };
1043
+
1044
+ WebSocketTransport.prototype.doCleanup = function() {
1045
+ var that = this;
1046
+ var ws = that.ws;
1047
+ if (ws) {
1048
+ ws.onmessage = ws.onclose = null;
1049
+ ws.close();
1050
+ utils.unload_del(that.unload_ref);
1051
+ that.unload_ref = that.ri = that.ws = null;
1052
+ }
1053
+ };
1054
+
1055
+ WebSocketTransport.enabled = function() {
1056
+ return !!(_window.WebSocket || _window.MozWebSocket);
1057
+ };
1058
+ // [*] End of lib/trans-websocket.js
1059
+
1060
+
1061
+ // [*] Including lib/trans-sender.js
1062
+ var BufferedSender = function() {};
1063
+ BufferedSender.prototype.send_constructor = function(sender) {
1064
+ var that = this;
1065
+ that.send_buffer = [];
1066
+ that.sender = sender;
1067
+ };
1068
+ BufferedSender.prototype.doSend = function(message) {
1069
+ var that = this;
1070
+ that.send_buffer.push(message);
1071
+ if (!that.send_stop) {
1072
+ that.send_schedule();
1073
+ }
1074
+ };
1075
+
1076
+ // For polling transports in a situation when in the message callback,
1077
+ // new message is being send. If the sending connection was started
1078
+ // before receiving one, it is possible to saturate the network and
1079
+ // timeout due to the lack of receiving socket. To avoid that we delay
1080
+ // sending messages by some small time, in order to let receiving
1081
+ // connection be started beforehand. This is only a halfmeasure and
1082
+ // does not fix the big problem, but it does make the tests go more
1083
+ // stable on slow networks.
1084
+ BufferedSender.prototype.send_schedule_wait = function() {
1085
+ var that = this;
1086
+ var tref;
1087
+ that.send_stop = function() {
1088
+ that.send_stop = null;
1089
+ clearTimeout(tref);
1090
+ };
1091
+ tref = utils.delay(25, function() {
1092
+ that.send_stop = null;
1093
+ that.send_schedule();
1094
+ });
1095
+ };
1096
+
1097
+ BufferedSender.prototype.send_schedule = function() {
1098
+ var that = this;
1099
+ if (that.send_buffer.length > 0) {
1100
+ var payload = '[' + that.send_buffer.join(',') + ']';
1101
+ that.send_stop = that.sender(that.trans_url,
1102
+ payload,
1103
+ function() {
1104
+ that.send_stop = null;
1105
+ that.send_schedule_wait();
1106
+ });
1107
+ that.send_buffer = [];
1108
+ }
1109
+ };
1110
+
1111
+ BufferedSender.prototype.send_destructor = function() {
1112
+ var that = this;
1113
+ if (that._send_stop) {
1114
+ that._send_stop();
1115
+ }
1116
+ that._send_stop = null;
1117
+ };
1118
+
1119
+ var jsonPGenericSender = function(url, payload, callback) {
1120
+ var that = this;
1121
+
1122
+ if (!('_send_form' in that)) {
1123
+ var form = that._send_form = _document.createElement('form');
1124
+ var area = that._send_area = _document.createElement('textarea');
1125
+ area.name = 'd';
1126
+ form.style.display = 'none';
1127
+ form.style.position = 'absolute';
1128
+ form.method = 'POST';
1129
+ form.enctype = 'application/x-www-form-urlencoded';
1130
+ form.acceptCharset = "UTF-8";
1131
+ form.appendChild(area);
1132
+ _document.body.appendChild(form);
1133
+ }
1134
+ var form = that._send_form;
1135
+ var area = that._send_area;
1136
+ var id = 'a' + utils.random_string(8);
1137
+ form.target = id;
1138
+ form.action = url + '/jsonp_send?i=' + id;
1139
+
1140
+ var iframe;
1141
+ try {
1142
+ // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
1143
+ iframe = _document.createElement('<iframe name="'+ id +'">');
1144
+ } catch(x) {
1145
+ iframe = _document.createElement('iframe');
1146
+ iframe.name = id;
1147
+ }
1148
+ iframe.id = id;
1149
+ form.appendChild(iframe);
1150
+ iframe.style.display = 'none';
1151
+
1152
+ try {
1153
+ area.value = payload;
1154
+ } catch(e) {
1155
+ alert('Your browser is seriously broken. Go home! ' + e.message);
1156
+ }
1157
+ form.submit();
1158
+
1159
+ var completed = function(e) {
1160
+ if (!iframe.onerror) return;
1161
+ iframe.onreadystatechange = iframe.onerror = iframe.onload = null;
1162
+ // Opera mini doesn't like if we GC iframe
1163
+ // immediately, thus this timeout.
1164
+ utils.delay(500, function() {
1165
+ iframe.parentNode.removeChild(iframe);
1166
+ iframe = null;
1167
+ });
1168
+ area.value = null;
1169
+ callback();
1170
+ };
1171
+ iframe.onerror = iframe.onload = completed;
1172
+ iframe.onreadystatechange = function(e) {
1173
+ if (iframe.readyState == 'complete') completed();
1174
+ };
1175
+ return completed;
1176
+ };
1177
+
1178
+ var createAjaxSender = function(AjaxObject) {
1179
+ return function(url, payload, callback) {
1180
+ var xo = new AjaxObject('POST', url + '/xhr_send', payload);
1181
+ xo.onfinish = function(status, text) {
1182
+ callback(status);
1183
+ };
1184
+ return function(abort_reason) {
1185
+ callback(0, abort_reason);
1186
+ };
1187
+ };
1188
+ };
1189
+ // [*] End of lib/trans-sender.js
1190
+
1191
+
1192
+ // [*] Including lib/trans-jsonp-receiver.js
1193
+ // Parts derived from Socket.io:
1194
+ // https://github.com/LearnBoost/socket.io/blob/0.6.17/lib/socket.io/transports/jsonp-polling.js
1195
+ // and jQuery-JSONP:
1196
+ // https://code.google.com/p/jquery-jsonp/source/browse/trunk/core/jquery.jsonp.js
1197
+ var jsonPGenericReceiver = function(url, callback) {
1198
+ var tref;
1199
+ var script = _document.createElement('script');
1200
+ var script2; // Opera synchronous load trick.
1201
+ var close_script = function(frame) {
1202
+ if (script2) {
1203
+ script2.parentNode.removeChild(script2);
1204
+ script2 = null;
1205
+ }
1206
+ if (script) {
1207
+ clearTimeout(tref);
1208
+ script.parentNode.removeChild(script);
1209
+ script.onreadystatechange = script.onerror =
1210
+ script.onload = script.onclick = null;
1211
+ script = null;
1212
+ callback(frame);
1213
+ callback = null;
1214
+ }
1215
+ };
1216
+
1217
+ // IE9 fires 'error' event after orsc or before, in random order.
1218
+ var loaded_okay = false;
1219
+ var error_timer = null;
1220
+
1221
+ script.id = 'a' + utils.random_string(8);
1222
+ script.src = url;
1223
+ script.type = 'text/javascript';
1224
+ script.charset = 'UTF-8';
1225
+ script.onerror = function(e) {
1226
+ if (!error_timer) {
1227
+ // Delay firing close_script.
1228
+ error_timer = setTimeout(function() {
1229
+ if (!loaded_okay) {
1230
+ close_script(utils.closeFrame(
1231
+ 1006,
1232
+ "JSONP script loaded abnormally (onerror)"));
1233
+ }
1234
+ }, 1000);
1235
+ }
1236
+ };
1237
+ script.onload = function(e) {
1238
+ close_script(utils.closeFrame(1006, "JSONP script loaded abnormally (onload)"));
1239
+ };
1240
+
1241
+ script.onreadystatechange = function(e) {
1242
+ if (/loaded|closed/.test(script.readyState)) {
1243
+ if (script && script.htmlFor && script.onclick) {
1244
+ loaded_okay = true;
1245
+ try {
1246
+ // In IE, actually execute the script.
1247
+ script.onclick();
1248
+ } catch (x) {}
1249
+ }
1250
+ if (script) {
1251
+ close_script(utils.closeFrame(1006, "JSONP script loaded abnormally (onreadystatechange)"));
1252
+ }
1253
+ }
1254
+ };
1255
+ // IE: event/htmlFor/onclick trick.
1256
+ // One can't rely on proper order for onreadystatechange. In order to
1257
+ // make sure, set a 'htmlFor' and 'event' properties, so that
1258
+ // script code will be installed as 'onclick' handler for the
1259
+ // script object. Later, onreadystatechange, manually execute this
1260
+ // code. FF and Chrome doesn't work with 'event' and 'htmlFor'
1261
+ // set. For reference see:
1262
+ // http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html
1263
+ // Also, read on that about script ordering:
1264
+ // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
1265
+ if (typeof script.async === 'undefined' && _document.attachEvent) {
1266
+ // According to mozilla docs, in recent browsers script.async defaults
1267
+ // to 'true', so we may use it to detect a good browser:
1268
+ // https://developer.mozilla.org/en/HTML/Element/script
1269
+ if (!/opera/i.test(navigator.userAgent)) {
1270
+ // Naively assume we're in IE
1271
+ try {
1272
+ script.htmlFor = script.id;
1273
+ script.event = "onclick";
1274
+ } catch (x) {}
1275
+ script.async = true;
1276
+ } else {
1277
+ // Opera, second sync script hack
1278
+ script2 = _document.createElement('script');
1279
+ script2.text = "try{var a = document.getElementById('"+script.id+"'); if(a)a.onerror();}catch(x){};";
1280
+ script.async = script2.async = false;
1281
+ }
1282
+ }
1283
+ if (typeof script.async !== 'undefined') {
1284
+ script.async = true;
1285
+ }
1286
+
1287
+ // Fallback mostly for Konqueror - stupid timer, 35 seconds shall be plenty.
1288
+ tref = setTimeout(function() {
1289
+ close_script(utils.closeFrame(1006, "JSONP script loaded abnormally (timeout)"));
1290
+ }, 35000);
1291
+
1292
+ var head = _document.getElementsByTagName('head')[0];
1293
+ head.insertBefore(script, head.firstChild);
1294
+ if (script2) {
1295
+ head.insertBefore(script2, head.firstChild);
1296
+ }
1297
+ return close_script;
1298
+ };
1299
+ // [*] End of lib/trans-jsonp-receiver.js
1300
+
1301
+
1302
+ // [*] Including lib/trans-jsonp-polling.js
1303
+ // The simplest and most robust transport, using the well-know cross
1304
+ // domain hack - JSONP. This transport is quite inefficient - one
1305
+ // mssage could use up to one http request. But at least it works almost
1306
+ // everywhere.
1307
+ // Known limitations:
1308
+ // o you will get a spinning cursor
1309
+ // o for Konqueror a dumb timer is needed to detect errors
1310
+
1311
+
1312
+ var JsonPTransport = SockJS['jsonp-polling'] = function(ri, trans_url) {
1313
+ utils.polluteGlobalNamespace();
1314
+ var that = this;
1315
+ that.ri = ri;
1316
+ that.trans_url = trans_url;
1317
+ that.send_constructor(jsonPGenericSender);
1318
+ that._schedule_recv();
1319
+ };
1320
+
1321
+ // Inheritnace
1322
+ JsonPTransport.prototype = new BufferedSender();
1323
+
1324
+ JsonPTransport.prototype._schedule_recv = function() {
1325
+ var that = this;
1326
+ var callback = function(data) {
1327
+ that._recv_stop = null;
1328
+ if (data) {
1329
+ // no data - heartbeat;
1330
+ if (!that._is_closing) {
1331
+ that.ri._didMessage(data);
1332
+ }
1333
+ }
1334
+ // The message can be a close message, and change is_closing state.
1335
+ if (!that._is_closing) {
1336
+ that._schedule_recv();
1337
+ }
1338
+ };
1339
+ that._recv_stop = jsonPReceiverWrapper(that.trans_url + '/jsonp',
1340
+ jsonPGenericReceiver, callback);
1341
+ };
1342
+
1343
+ JsonPTransport.enabled = function() {
1344
+ return true;
1345
+ };
1346
+
1347
+ JsonPTransport.need_body = true;
1348
+
1349
+
1350
+ JsonPTransport.prototype.doCleanup = function() {
1351
+ var that = this;
1352
+ that._is_closing = true;
1353
+ if (that._recv_stop) {
1354
+ that._recv_stop();
1355
+ }
1356
+ that.ri = that._recv_stop = null;
1357
+ that.send_destructor();
1358
+ };
1359
+
1360
+
1361
+ // Abstract away code that handles global namespace pollution.
1362
+ var jsonPReceiverWrapper = function(url, constructReceiver, user_callback) {
1363
+ var id = 'a' + utils.random_string(6);
1364
+ var url_id = url + '?c=' + escape(WPrefix + '.' + id);
1365
+ // Callback will be called exactly once.
1366
+ var callback = function(frame) {
1367
+ delete _window[WPrefix][id];
1368
+ user_callback(frame);
1369
+ };
1370
+
1371
+ var close_script = constructReceiver(url_id, callback);
1372
+ _window[WPrefix][id] = close_script;
1373
+ var stop = function() {
1374
+ if (_window[WPrefix][id]) {
1375
+ _window[WPrefix][id](utils.closeFrame(1000, "JSONP user aborted read"));
1376
+ }
1377
+ };
1378
+ return stop;
1379
+ };
1380
+ // [*] End of lib/trans-jsonp-polling.js
1381
+
1382
+
1383
+ // [*] Including lib/trans-xhr.js
1384
+ var AjaxBasedTransport = function() {};
1385
+ AjaxBasedTransport.prototype = new BufferedSender();
1386
+
1387
+ AjaxBasedTransport.prototype.run = function(ri, trans_url,
1388
+ url_suffix, Receiver, AjaxObject) {
1389
+ var that = this;
1390
+ that.ri = ri;
1391
+ that.trans_url = trans_url;
1392
+ that.send_constructor(createAjaxSender(AjaxObject));
1393
+ that.poll = new Polling(ri, Receiver,
1394
+ trans_url + url_suffix, AjaxObject);
1395
+ };
1396
+
1397
+ AjaxBasedTransport.prototype.doCleanup = function() {
1398
+ var that = this;
1399
+ if (that.poll) {
1400
+ that.poll.abort();
1401
+ that.poll = null;
1402
+ }
1403
+ };
1404
+
1405
+ // xhr-streaming
1406
+ var XhrStreamingTransport = SockJS['xhr-streaming'] = function(ri, trans_url) {
1407
+ this.run(ri, trans_url, '/xhr_streaming', XhrReceiver, utils.XHRObject);
1408
+ };
1409
+
1410
+ XhrStreamingTransport.prototype = new AjaxBasedTransport();
1411
+
1412
+ XhrStreamingTransport.enabled = function() {
1413
+ return (_window.XMLHttpRequest &&
1414
+ 'withCredentials' in new XMLHttpRequest());
1415
+ };
1416
+ XhrStreamingTransport.roundTrips = 2; // preflight, ajax
1417
+
1418
+ // According to:
1419
+ // http://stackoverflow.com/questions/1641507/detect-browser-support-for-cross-domain-xmlhttprequests
1420
+ // http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/
1421
+
1422
+
1423
+ // xdr-streaming
1424
+ var XdrStreamingTransport = SockJS['xdr-streaming'] = function(ri, trans_url) {
1425
+ this.run(ri, trans_url, '/xhr_streaming', XhrReceiver, utils.XDRObject);
1426
+ };
1427
+
1428
+ XdrStreamingTransport.prototype = new AjaxBasedTransport();
1429
+
1430
+ XdrStreamingTransport.enabled = function() {
1431
+ return !!_window.XDomainRequest;
1432
+ };
1433
+ XdrStreamingTransport.roundTrips = 2; // preflight, ajax
1434
+
1435
+
1436
+
1437
+ // xhr-polling
1438
+ var XhrPollingTransport = SockJS['xhr-polling'] = function(ri, trans_url) {
1439
+ this.run(ri, trans_url, '/xhr', XhrReceiver, utils.XHRObject);
1440
+ };
1441
+
1442
+ XhrPollingTransport.prototype = new AjaxBasedTransport();
1443
+
1444
+ XhrPollingTransport.enabled = XhrStreamingTransport.enabled;
1445
+ XhrPollingTransport.roundTrips = 2; // preflight, ajax
1446
+
1447
+
1448
+ // xdr-polling
1449
+ var XdrPollingTransport = SockJS['xdr-polling'] = function(ri, trans_url) {
1450
+ this.run(ri, trans_url, '/xhr', XhrReceiver, utils.XDRObject);
1451
+ };
1452
+
1453
+ XdrPollingTransport.prototype = new AjaxBasedTransport();
1454
+
1455
+ XdrPollingTransport.enabled = XdrStreamingTransport.enabled;
1456
+ XdrPollingTransport.roundTrips = 2; // preflight, ajax
1457
+ // [*] End of lib/trans-xhr.js
1458
+
1459
+
1460
+ // [*] Including lib/trans-iframe.js
1461
+ // Few cool transports do work only for same-origin. In order to make
1462
+ // them working cross-domain we shall use iframe, served form the
1463
+ // remote domain. New browsers, have capabilities to communicate with
1464
+ // cross domain iframe, using postMessage(). In IE it was implemented
1465
+ // from IE 8+, but of course, IE got some details wrong:
1466
+ // http://msdn.microsoft.com/en-us/library/cc197015(v=VS.85).aspx
1467
+ // http://stevesouders.com/misc/test-postmessage.php
1468
+
1469
+ var IframeTransport = function() {};
1470
+
1471
+ IframeTransport.prototype.i_constructor = function(ri, trans_url, base_url) {
1472
+ var that = this;
1473
+ that.ri = ri;
1474
+ that.origin = utils.getOrigin(base_url);
1475
+ that.base_url = base_url;
1476
+ that.trans_url = trans_url;
1477
+
1478
+ var iframe_url = base_url + '/iframe.html';
1479
+ if (that.ri._options.devel) {
1480
+ iframe_url += '?t=' + (+new Date);
1481
+ }
1482
+ that.window_id = utils.random_string(8);
1483
+ iframe_url += '#' + that.window_id;
1484
+
1485
+ that.iframeObj = utils.createIframe(iframe_url, function(r) {
1486
+ that.ri._didClose(1006, "Unable to load an iframe (" + r + ")");
1487
+ });
1488
+
1489
+ that.onmessage_cb = utils.bind(that.onmessage, that);
1490
+ utils.attachMessage(that.onmessage_cb);
1491
+ };
1492
+
1493
+ IframeTransport.prototype.doCleanup = function() {
1494
+ var that = this;
1495
+ if (that.iframeObj) {
1496
+ utils.detachMessage(that.onmessage_cb);
1497
+ try {
1498
+ // When the iframe is not loaded, IE raises an exception
1499
+ // on 'contentWindow'.
1500
+ if (that.iframeObj.iframe.contentWindow) {
1501
+ that.postMessage('c');
1502
+ }
1503
+ } catch (x) {}
1504
+ that.iframeObj.cleanup();
1505
+ that.iframeObj = null;
1506
+ that.onmessage_cb = that.iframeObj = null;
1507
+ }
1508
+ };
1509
+
1510
+ IframeTransport.prototype.onmessage = function(e) {
1511
+ var that = this;
1512
+ if (e.origin !== that.origin) return;
1513
+ var window_id = e.data.slice(0, 8);
1514
+ var type = e.data.slice(8, 9);
1515
+ var data = e.data.slice(9);
1516
+
1517
+ if (window_id !== that.window_id) return;
1518
+
1519
+ switch(type) {
1520
+ case 's':
1521
+ that.iframeObj.loaded();
1522
+ that.postMessage('s', JSON.stringify([SockJS.version, that.protocol, that.trans_url, that.base_url]));
1523
+ break;
1524
+ case 't':
1525
+ that.ri._didMessage(data);
1526
+ break;
1527
+ }
1528
+ };
1529
+
1530
+ IframeTransport.prototype.postMessage = function(type, data) {
1531
+ var that = this;
1532
+ that.iframeObj.iframe.contentWindow.postMessage(that.window_id + type + (data || ''), that.origin);
1533
+ };
1534
+
1535
+ IframeTransport.prototype.doSend = function (message) {
1536
+ this.postMessage('m', message);
1537
+ };
1538
+
1539
+ IframeTransport.enabled = function() {
1540
+ // postMessage misbehaves in konqueror 4.6.5 - the messages are delivered with
1541
+ // huge delay, or not at all.
1542
+ var konqueror = navigator && navigator.userAgent && navigator.userAgent.indexOf('Konqueror') !== -1;
1543
+ return ((typeof _window.postMessage === 'function' ||
1544
+ typeof _window.postMessage === 'object') && (!konqueror));
1545
+ };
1546
+ // [*] End of lib/trans-iframe.js
1547
+
1548
+
1549
+ // [*] Including lib/trans-iframe-within.js
1550
+ var curr_window_id;
1551
+
1552
+ var postMessage = function (type, data) {
1553
+ if(parent !== _window) {
1554
+ parent.postMessage(curr_window_id + type + (data || ''), '*');
1555
+ } else {
1556
+ utils.log("Can't postMessage, no parent window.", type, data);
1557
+ }
1558
+ };
1559
+
1560
+ var FacadeJS = function() {};
1561
+ FacadeJS.prototype._didClose = function (code, reason) {
1562
+ postMessage('t', utils.closeFrame(code, reason));
1563
+ };
1564
+ FacadeJS.prototype._didMessage = function (frame) {
1565
+ postMessage('t', frame);
1566
+ };
1567
+ FacadeJS.prototype._doSend = function (data) {
1568
+ this._transport.doSend(data);
1569
+ };
1570
+ FacadeJS.prototype._doCleanup = function () {
1571
+ this._transport.doCleanup();
1572
+ };
1573
+
1574
+ SockJS.bootstrap_iframe = function() {
1575
+ var facade;
1576
+ curr_window_id = _document.location.hash.slice(1);
1577
+ var onMessage = function(e) {
1578
+ if(e.source !== parent) return;
1579
+ var window_id = e.data.slice(0, 8);
1580
+ var type = e.data.slice(8, 9);
1581
+ var data = e.data.slice(9);
1582
+ if (window_id !== curr_window_id) return;
1583
+ switch(type) {
1584
+ case 's':
1585
+ var p = JSON.parse(data);
1586
+ var version = p[0];
1587
+ var protocol = p[1];
1588
+ var trans_url = p[2];
1589
+ var base_url = p[3];
1590
+ if (version !== SockJS.version) {
1591
+ utils.log("Incompatibile SockJS! Main site uses:" +
1592
+ " \"" + version + "\", the iframe:" +
1593
+ " \"" + SockJS.version + "\".");
1594
+ }
1595
+ if (!utils.isSameOriginUrl(trans_url) ||
1596
+ !utils.isSameOriginUrl(base_url)) {
1597
+ utils.log("Can't connect to different domain from within an " +
1598
+ "iframe. (" + JSON.stringify([_window.location.href, trans_url, base_url]) +
1599
+ ")");
1600
+ return;
1601
+ }
1602
+ facade = new FacadeJS();
1603
+ facade._transport = new FacadeJS[protocol](facade, trans_url, base_url);
1604
+ break;
1605
+ case 'm':
1606
+ facade._doSend(data);
1607
+ break;
1608
+ case 'c':
1609
+ if (facade)
1610
+ facade._doCleanup();
1611
+ facade = null;
1612
+ break;
1613
+ }
1614
+ };
1615
+
1616
+ // alert('test ticker');
1617
+ // facade = new FacadeJS();
1618
+ // facade._transport = new FacadeJS['w-iframe-xhr-polling'](facade, 'http://host.com:9999/ticker/12/basd');
1619
+
1620
+ utils.attachMessage(onMessage);
1621
+
1622
+ // Start
1623
+ postMessage('s');
1624
+ };
1625
+ // [*] End of lib/trans-iframe-within.js
1626
+
1627
+
1628
+ // [*] Including lib/info.js
1629
+ var InfoReceiver = function(base_url, AjaxObject) {
1630
+ var that = this;
1631
+ utils.delay(function(){that.doXhr(base_url, AjaxObject);});
1632
+ };
1633
+
1634
+ InfoReceiver.prototype = new EventEmitter(['finish']);
1635
+
1636
+ InfoReceiver.prototype.doXhr = function(base_url, AjaxObject) {
1637
+ var that = this;
1638
+ var t0 = (new Date()).getTime();
1639
+ var xo = new AjaxObject('GET', base_url + '/info' , null);
1640
+
1641
+ var tref = utils.delay(8000,
1642
+ function(){xo.ontimeout();});
1643
+
1644
+ xo.onfinish = function(status, text) {
1645
+ clearTimeout(tref);
1646
+ tref = null;
1647
+ if (status === 200) {
1648
+ var rtt = (new Date()).getTime() - t0;
1649
+ var info = JSON.parse(text);
1650
+ if (typeof info !== 'object') info = {};
1651
+ that.emit('finish', info, rtt);
1652
+ } else {
1653
+ that.emit('finish');
1654
+ }
1655
+ };
1656
+ xo.ontimeout = function() {
1657
+ xo.close();
1658
+ that.emit('finish');
1659
+ };
1660
+ };
1661
+
1662
+ var InfoReceiverIframe = function(base_url) {
1663
+ var that = this;
1664
+ var go = function() {
1665
+ var ifr = new IframeTransport();
1666
+ ifr.protocol = 'w-iframe-info-receiver';
1667
+ var fun = function(r) {
1668
+ if (typeof r === 'string' && r.substr(0,1) === 'm') {
1669
+ var d = JSON.parse(r.substr(1));
1670
+ var info = d[0], rtt = d[1];
1671
+ that.emit('finish', info, rtt);
1672
+ } else {
1673
+ that.emit('finish');
1674
+ }
1675
+ ifr.doCleanup();
1676
+ ifr = null;
1677
+ };
1678
+ var mock_ri = {
1679
+ _options: {},
1680
+ _didClose: fun,
1681
+ _didMessage: fun
1682
+ };
1683
+ ifr.i_constructor(mock_ri, base_url, base_url);
1684
+ }
1685
+ if(!_document.body) {
1686
+ utils.attachEvent('load', go);
1687
+ } else {
1688
+ go();
1689
+ }
1690
+ };
1691
+ InfoReceiverIframe.prototype = new EventEmitter(['finish']);
1692
+
1693
+
1694
+ var InfoReceiverFake = function() {
1695
+ // It may not be possible to do cross domain AJAX to get the info
1696
+ // data, for example for IE7. But we want to run JSONP, so let's
1697
+ // fake the response, with rtt=2s (rto=6s).
1698
+ var that = this;
1699
+ utils.delay(function() {
1700
+ that.emit('finish', {}, 2000);
1701
+ });
1702
+ };
1703
+ InfoReceiverFake.prototype = new EventEmitter(['finish']);
1704
+
1705
+ var createInfoReceiver = function(base_url) {
1706
+ if (utils.isSameOriginUrl(base_url)) {
1707
+ // If, for some reason, we have SockJS locally - there's no
1708
+ // need to start up the complex machinery. Just use ajax.
1709
+ return new InfoReceiver(base_url, utils.XHRObject);
1710
+ }
1711
+ switch (utils.isXHRCorsCapable()) {
1712
+ case 1:
1713
+ return new InfoReceiver(base_url, utils.XHRObject);
1714
+ case 2:
1715
+ return new InfoReceiver(base_url, utils.XDRObject);
1716
+ case 3:
1717
+ // Opera
1718
+ return new InfoReceiverIframe(base_url);
1719
+ default:
1720
+ // IE 7
1721
+ return new InfoReceiverFake();
1722
+ };
1723
+ };
1724
+
1725
+
1726
+ var WInfoReceiverIframe = FacadeJS['w-iframe-info-receiver'] = function(ri, _trans_url, base_url) {
1727
+ var ir = new InfoReceiver(base_url, utils.XHRObject);
1728
+ ir.onfinish = function(info, rtt) {
1729
+ ri._didMessage('m'+JSON.stringify([info, rtt]));
1730
+ ri._didClose();
1731
+ }
1732
+ };
1733
+ WInfoReceiverIframe.prototype.doCleanup = function() {};
1734
+ // [*] End of lib/info.js
1735
+
1736
+
1737
+ // [*] Including lib/trans-iframe-eventsource.js
1738
+ var EventSourceIframeTransport = SockJS['iframe-eventsource'] = function () {
1739
+ var that = this;
1740
+ that.protocol = 'w-iframe-eventsource';
1741
+ that.i_constructor.apply(that, arguments);
1742
+ };
1743
+
1744
+ EventSourceIframeTransport.prototype = new IframeTransport();
1745
+
1746
+ EventSourceIframeTransport.enabled = function () {
1747
+ return ('EventSource' in _window) && IframeTransport.enabled();
1748
+ };
1749
+
1750
+ EventSourceIframeTransport.need_body = true;
1751
+ EventSourceIframeTransport.roundTrips = 3; // html, javascript, eventsource
1752
+
1753
+
1754
+ // w-iframe-eventsource
1755
+ var EventSourceTransport = FacadeJS['w-iframe-eventsource'] = function(ri, trans_url) {
1756
+ this.run(ri, trans_url, '/eventsource', EventSourceReceiver, utils.XHRObject);
1757
+ }
1758
+ EventSourceTransport.prototype = new AjaxBasedTransport();
1759
+ // [*] End of lib/trans-iframe-eventsource.js
1760
+
1761
+
1762
+ // [*] Including lib/trans-iframe-xhr-polling.js
1763
+ var XhrPollingIframeTransport = SockJS['iframe-xhr-polling'] = function () {
1764
+ var that = this;
1765
+ that.protocol = 'w-iframe-xhr-polling';
1766
+ that.i_constructor.apply(that, arguments);
1767
+ };
1768
+
1769
+ XhrPollingIframeTransport.prototype = new IframeTransport();
1770
+
1771
+ XhrPollingIframeTransport.enabled = function () {
1772
+ return _window.XMLHttpRequest && IframeTransport.enabled();
1773
+ };
1774
+
1775
+ XhrPollingIframeTransport.need_body = true;
1776
+ XhrPollingIframeTransport.roundTrips = 3; // html, javascript, xhr
1777
+
1778
+
1779
+ // w-iframe-xhr-polling
1780
+ var XhrPollingITransport = FacadeJS['w-iframe-xhr-polling'] = function(ri, trans_url) {
1781
+ this.run(ri, trans_url, '/xhr', XhrReceiver, utils.XHRObject);
1782
+ };
1783
+
1784
+ XhrPollingITransport.prototype = new AjaxBasedTransport();
1785
+ // [*] End of lib/trans-iframe-xhr-polling.js
1786
+
1787
+
1788
+ // [*] Including lib/trans-iframe-htmlfile.js
1789
+ // This transport generally works in any browser, but will cause a
1790
+ // spinning cursor to appear in any browser other than IE.
1791
+ // We may test this transport in all browsers - why not, but in
1792
+ // production it should be only run in IE.
1793
+
1794
+ var HtmlFileIframeTransport = SockJS['iframe-htmlfile'] = function () {
1795
+ var that = this;
1796
+ that.protocol = 'w-iframe-htmlfile';
1797
+ that.i_constructor.apply(that, arguments);
1798
+ };
1799
+
1800
+ // Inheritance.
1801
+ HtmlFileIframeTransport.prototype = new IframeTransport();
1802
+
1803
+ HtmlFileIframeTransport.enabled = function() {
1804
+ return IframeTransport.enabled();
1805
+ };
1806
+
1807
+ HtmlFileIframeTransport.need_body = true;
1808
+ HtmlFileIframeTransport.roundTrips = 3; // html, javascript, htmlfile
1809
+
1810
+
1811
+ // w-iframe-htmlfile
1812
+ var HtmlFileTransport = FacadeJS['w-iframe-htmlfile'] = function(ri, trans_url) {
1813
+ this.run(ri, trans_url, '/htmlfile', HtmlfileReceiver, utils.XHRObject);
1814
+ };
1815
+ HtmlFileTransport.prototype = new AjaxBasedTransport();
1816
+ // [*] End of lib/trans-iframe-htmlfile.js
1817
+
1818
+
1819
+ // [*] Including lib/trans-polling.js
1820
+
1821
+ var Polling = function(ri, Receiver, recv_url, AjaxObject) {
1822
+ var that = this;
1823
+ that.ri = ri;
1824
+ that.Receiver = Receiver;
1825
+ that.recv_url = recv_url;
1826
+ that.AjaxObject = AjaxObject;
1827
+ that._scheduleRecv();
1828
+ };
1829
+
1830
+ Polling.prototype._scheduleRecv = function() {
1831
+ var that = this;
1832
+ var poll = that.poll = new that.Receiver(that.recv_url, that.AjaxObject);
1833
+ var msg_counter = 0;
1834
+ poll.onmessage = function(e) {
1835
+ msg_counter += 1;
1836
+ that.ri._didMessage(e.data);
1837
+ };
1838
+ poll.onclose = function(e) {
1839
+ that.poll = poll = poll.onmessage = poll.onclose = null;
1840
+ if (!that.poll_is_closing) {
1841
+ if (e.reason === 'permanent') {
1842
+ that.ri._didClose(1006, 'Polling error (' + e.reason + ')');
1843
+ } else {
1844
+ that._scheduleRecv();
1845
+ }
1846
+ }
1847
+ };
1848
+ };
1849
+
1850
+ Polling.prototype.abort = function() {
1851
+ var that = this;
1852
+ that.poll_is_closing = true;
1853
+ if (that.poll) {
1854
+ that.poll.abort();
1855
+ }
1856
+ };
1857
+ // [*] End of lib/trans-polling.js
1858
+
1859
+
1860
+ // [*] Including lib/trans-receiver-eventsource.js
1861
+
1862
+ var EventSourceReceiver = function(url) {
1863
+ var that = this;
1864
+ var es = new EventSource(url);
1865
+ es.onmessage = function(e) {
1866
+ that.dispatchEvent(new SimpleEvent('message',
1867
+ {'data': unescape(e.data)}));
1868
+ };
1869
+ that.es_close = es.onerror = function(e, abort_reason) {
1870
+ // ES on reconnection has readyState = 0 or 1.
1871
+ // on network error it's CLOSED = 2
1872
+ var reason = abort_reason ? 'user' :
1873
+ (es.readyState !== 2 ? 'network' : 'permanent');
1874
+ that.es_close = es.onmessage = es.onerror = null;
1875
+ // EventSource reconnects automatically.
1876
+ es.close();
1877
+ es = null;
1878
+ // Safari and chrome < 15 crash if we close window before
1879
+ // waiting for ES cleanup. See:
1880
+ // https://code.google.com/p/chromium/issues/detail?id=89155
1881
+ utils.delay(200, function() {
1882
+ that.dispatchEvent(new SimpleEvent('close', {reason: reason}));
1883
+ });
1884
+ };
1885
+ };
1886
+
1887
+ EventSourceReceiver.prototype = new REventTarget();
1888
+
1889
+ EventSourceReceiver.prototype.abort = function() {
1890
+ var that = this;
1891
+ if (that.es_close) {
1892
+ that.es_close({}, true);
1893
+ }
1894
+ };
1895
+ // [*] End of lib/trans-receiver-eventsource.js
1896
+
1897
+
1898
+ // [*] Including lib/trans-receiver-htmlfile.js
1899
+ var _is_ie_htmlfile_capable;
1900
+ var isIeHtmlfileCapable = function() {
1901
+ if (_is_ie_htmlfile_capable === undefined) {
1902
+ if ('ActiveXObject' in _window) {
1903
+ try {
1904
+ _is_ie_htmlfile_capable = !!new ActiveXObject('htmlfile');
1905
+ } catch (x) {}
1906
+ } else {
1907
+ _is_ie_htmlfile_capable = false;
1908
+ }
1909
+ }
1910
+ return _is_ie_htmlfile_capable;
1911
+ };
1912
+
1913
+
1914
+ var HtmlfileReceiver = function(url) {
1915
+ var that = this;
1916
+ utils.polluteGlobalNamespace();
1917
+
1918
+ that.id = 'a' + utils.random_string(6, 26);
1919
+ url += ((url.indexOf('?') === -1) ? '?' : '&') +
1920
+ 'c=' + escape(WPrefix + '.' + that.id);
1921
+
1922
+ var constructor = isIeHtmlfileCapable() ?
1923
+ utils.createHtmlfile : utils.createIframe;
1924
+
1925
+ var iframeObj;
1926
+ _window[WPrefix][that.id] = {
1927
+ start: function () {
1928
+ iframeObj.loaded();
1929
+ },
1930
+ message: function (data) {
1931
+ that.dispatchEvent(new SimpleEvent('message', {'data': data}));
1932
+ },
1933
+ stop: function () {
1934
+ that.iframe_close({}, 'network');
1935
+ }
1936
+ };
1937
+ that.iframe_close = function(e, abort_reason) {
1938
+ iframeObj.cleanup();
1939
+ that.iframe_close = iframeObj = null;
1940
+ delete _window[WPrefix][that.id];
1941
+ that.dispatchEvent(new SimpleEvent('close', {reason: abort_reason}));
1942
+ };
1943
+ iframeObj = constructor(url, function(e) {
1944
+ that.iframe_close({}, 'permanent');
1945
+ });
1946
+ };
1947
+
1948
+ HtmlfileReceiver.prototype = new REventTarget();
1949
+
1950
+ HtmlfileReceiver.prototype.abort = function() {
1951
+ var that = this;
1952
+ if (that.iframe_close) {
1953
+ that.iframe_close({}, 'user');
1954
+ }
1955
+ };
1956
+ // [*] End of lib/trans-receiver-htmlfile.js
1957
+
1958
+
1959
+ // [*] Including lib/trans-receiver-xhr.js
1960
+
1961
+ var XhrReceiver = function(url, AjaxObject) {
1962
+ var that = this;
1963
+ var buf_pos = 0;
1964
+
1965
+ that.xo = new AjaxObject('POST', url, null);
1966
+ that.xo.onchunk = function(status, text) {
1967
+ if (status !== 200) return;
1968
+ while (1) {
1969
+ var buf = text.slice(buf_pos);
1970
+ var p = buf.indexOf('\n');
1971
+ if (p === -1) break;
1972
+ buf_pos += p+1;
1973
+ var msg = buf.slice(0, p);
1974
+ that.dispatchEvent(new SimpleEvent('message', {data: msg}));
1975
+ }
1976
+ };
1977
+ that.xo.onfinish = function(status, text) {
1978
+ that.xo.onchunk(status, text);
1979
+ that.xo = null;
1980
+ var reason = status === 200 ? 'network' : 'permanent';
1981
+ that.dispatchEvent(new SimpleEvent('close', {reason: reason}));
1982
+ }
1983
+ };
1984
+
1985
+ XhrReceiver.prototype = new REventTarget();
1986
+
1987
+ XhrReceiver.prototype.abort = function() {
1988
+ var that = this;
1989
+ if (that.xo) {
1990
+ that.xo.close();
1991
+ that.dispatchEvent(new SimpleEvent('close', {reason: 'user'}));
1992
+ that.xo = null;
1993
+ }
1994
+ };
1995
+ // [*] End of lib/trans-receiver-xhr.js
1996
+
1997
+
1998
+ // [*] Including lib/test-hooks.js
1999
+ // For testing
2000
+ SockJS.getUtils = function(){
2001
+ return utils;
2002
+ };
2003
+
2004
+ SockJS.getIframeTransport = function(){
2005
+ return IframeTransport;
2006
+ };
2007
+ // [*] End of lib/test-hooks.js
2008
+
2009
+ return SockJS;
2010
+ })();
2011
+ if ('_sockjs_onload' in window) setTimeout(_sockjs_onload, 1);
2012
+ // [*] End of lib/index.js
2013
+
2014
+ // [*] End of lib/all.js