some fixes for xhr and iframe transport came to light after hunged stream test was...
[brisk.git] / web / xynt-streaming.js
1 // old targetpage == page and moved into start method
2
3 //
4 // CLASS transport_xhr
5 //
6 function transport_xhr(doc, xynt_streaming, page)
7 {
8     this.doc = doc;
9     this.xynt_streaming = xynt_streaming;
10     this.xhr = createXMLHttpRequest();
11     this.xhr.open('GET', page);
12
13     var self = this;
14     this.xhr.onreadystatechange = function () { self.xhr_cb(); };
15     this.xhr.send(null);
16
17     this.stopped = false;
18 }
19
20 transport_xhr.prototype = {
21     doc: null,
22     xynt_streaming: "ready",
23     xhr: null,
24     stopped: true,
25
26     ctx_old: "",
27     ctx_old_len: 0,
28     ctx_new: null,
29
30     // script_clean: 0,
31
32     destroy: function () { /* public */
33         if (this.xhr != null) {
34             this.xhr_abort();
35         }
36         delete this.xhr;
37     },
38
39     xhr_cb: function () {
40         var ret;
41
42         if (this.xhr.readyState == 4) {
43             // console.log("SS: "+safestatus(xhr));
44
45             // NOTE: delay management later
46             // try {
47             //     if ((ret = safestatus(this.xhr)) == 200) {
48             //         this.delay = 0;
49             //         // console.log("del a null "+this.delayed);
50             //     } else if (ret != -1) {
51             //         this.delay = 5000;
52             //         this.hbit('X');
53             //         // alert('There was a problem with the request.' + ret);
54             //     }
55             // } catch(b) {};
56
57             // this.delayed = null;
58             this.stopped = true;
59         }
60     },
61
62     xhr_abort: function() {
63         if (this.xhr != null) {
64             this.xhr.abort();
65         }
66     },
67
68     xstr_is_init: function () { /* public */
69         try {
70             if (this.xhr.responseText != null) {
71                 this.ctx_new = this.xhr.responseText;
72             }
73         }
74         catch (e) {
75         }
76
77         return (this.ctx_new != null);
78     },
79
80     /* only after a successfull is_initialized call */
81     xstr_is_ready: function () { /* public */
82         return (this.xynt_streaming == "ready");
83     },
84
85     xstr_set: function () { /* public */
86         // already set
87     },
88
89     ctx_new_is_set: function () { /* public */
90         return (this.ctx_new != null);
91     },
92
93     ctx_new_curlen_get: function () { /* public */
94         return (this.ctx_new.length);
95     },
96
97     ctx_new_getchar: function(idx) { /* public */
98         return (this.ctx_new[idx]);
99     },
100
101     ctx_old_len_is_set: function () { /* public */
102         return (true);
103     },
104
105     ctx_old_len_get: function () { /* public */
106         return (this.ctx_old_len);
107     },
108
109     ctx_old_len_set: function (len) { /* public */
110         this.ctx_old_len = len;
111     },
112
113     ctx_old_len_add: function (len) { /* public */
114         this.ctx_old_len += len;
115     },
116
117     new_part: function () { /* public */
118         return (this.ctx_new.substr(this.ctx_old_len));
119     },
120
121     scrcls_set: function (step) { /* public */
122         // this.script_clean = step;
123     },
124
125     postproc: function () {
126         if (this.stopped && !this.xstr_is_ready()) {
127             this.xynt_streaming.reload();
128         }
129     }
130 }
131
132 //
133 // CLASS transport_htmlfile
134 //
135 function transport_htmlfile(doc, xynt_streaming, page)
136 {
137     this.doc = doc;
138     this.xynt_streaming = xynt_streaming;
139     this.transfdoc = new ActiveXObject("htmlfile");
140     this.transfdoc.open();
141     /*this.transfdoc.write("<html><head><script>");
142     this.transfdoc.write("document.domain=\""+(doc.domain)+"\";");
143     this.transfdoc.write("</"+"script></"+"head>"); */
144     this.transfdoc.write("<html><body><iframe id='iframe'></iframe></body></html>");
145     this.transfdoc.close();
146
147     this.ifra = this.transfdoc.getElementById("iframe");
148     this.ifra.contentWindow.location.href = page;
149 }
150
151 transport_htmlfile.prototype = {
152     doc: null,
153     xynt_streaming: null,
154     ifra: null,
155     tradoc: null,
156
157     destroy: function () { /* public */
158         if (this.ifra != null) {
159         //     this.doc.body.removeChild(this.ifra);
160         //     delete this.ifra;
161              this.ifra = null;
162         }
163
164         if (this.transfdoc) {
165             delete this.transfdoc;
166             this.transfdoc = null;
167         }
168     },
169
170     xstr_is_init: function () { /* public */
171         return (typeof(this.ifra.contentWindow.xynt_streaming) != 'undefined');
172     },
173
174     /* only after a successfull is_initialized call */
175     xstr_set: function () { /* public */
176         if (this.ifra.contentWindow.xynt_streaming == "ready") {
177             this.ifra.contentWindow.xynt_streaming = this.xynt_streaming;
178             return (true);
179         }
180         else if (this.ifra.contentWindow.xynt_streaming == this.xynt_streaming) {
181             return (true);
182         }
183         else {
184             return (false);
185         }
186     },
187
188     ctx_new_is_set: function () { /* public */
189         return (typeof(this.ifra.contentWindow.ctx_new) != 'undefined');
190     },
191
192     ctx_new_curlen_get: function () { /* public */
193         return (this.ifra.contentWindow.ctx_new.length);
194     },
195
196     ctx_new_getchar: function(idx) { /* public */
197     },
198
199     ctx_old_len_is_set: function () { /* public */
200         return (typeof(this.ifra.contentWindow.ctx_old_len) != 'undefined');
201     },
202
203     ctx_old_len_get: function () { /* public */
204         return (this.ifra.contentWindow.ctx_old_len);
205     },
206
207     ctx_old_len_set: function (len) { /* public */
208         this.ifra.contentWindow.ctx_old_len = len;
209     },
210
211     ctx_old_len_add: function (len) { /* public */
212         this.ifra.contentWindow.ctx_old_len += len;
213     },
214
215     new_part: function () { /* public */
216         return (this.ifra.contentWindow.ctx_new.substr(this.ifra.contentWindow.ctx_old_len));
217     },
218
219     scrcls_set: function (step) { /* public */
220         this.ifra.contentWindow.script_clean = step;
221     },
222
223     postproc: function () {
224     }
225 }
226
227
228
229 //
230 // CLASS transport_iframe
231 //
232 function transport_iframe(doc, xynt_streaming, page)
233 {
234     this.doc = doc;
235     this.xynt_streaming = xynt_streaming;
236     this.ifra = doc.createElement("iframe");
237     this.ifra.style.visibility = "hidden";
238     doc.body.appendChild(this.ifra);
239     this.ifra.contentWindow.location.href = page;
240 }
241
242 transport_iframe.prototype = {
243     doc: null,
244     xynt_streaming: null,
245     ifra: null,
246
247     destroy: function () { /* public */
248         try {
249             if (this.ifra != null) {
250                 // FIXME: with opera on win this remove child crash js so is
251                 //        commented AND NOWWW ????
252                 this.doc.body.removeChild(this.ifra);
253                 delete this.ifra;
254                 this.ifra = null;
255             }
256         } catch (b) {
257             alert("destroy exception catched");
258         }
259     },
260
261     xstr_is_init: function () { /* public */
262         return (typeof(this.ifra.contentWindow.xynt_streaming) != 'undefined');
263     },
264
265     /* only after a successfull is_initialized call */
266     xstr_is_ready: function () { /* public */
267         return (this.ifra.contentWindow.xynt_streaming == "ready");
268     },
269
270     /* only after a successfull is_initialized call */
271     xstr_set: function () { /* public */
272         if (this.ifra.contentWindow.xynt_streaming == "ready") {
273             this.ifra.contentWindow.xynt_streaming = this.xynt_streaming;
274             return (true);
275         }
276         else if (this.ifra.contentWindow.xynt_streaming == this.xynt_streaming) {
277             return (true);
278         }
279         else {
280             return (false);
281         }
282     },
283
284
285     /* only after a successfull is_ready call to be sure the accessibility of the var */
286     xstr_set_old: function (xynt_streaming) { /* public */
287         this.ifra.contentWindow.xynt_streaming = xynt_streaming;
288     },
289
290     ctx_new_is_set: function () { /* public */
291         return (typeof(this.ifra.contentWindow.ctx_new) != 'undefined');
292     },
293
294     ctx_new_curlen_get: function () { /* public */
295         return (this.ifra.contentWindow.ctx_new.length);
296     },
297
298     ctx_new_getchar: function(idx) { /* public */
299     },
300
301     ctx_old_len_is_set: function () { /* public */
302         return (typeof(this.ifra.contentWindow.ctx_old_len) != 'undefined');
303     },
304
305     ctx_old_len_get: function () { /* public */
306         return (this.ifra.contentWindow.ctx_old_len);
307     },
308
309     ctx_old_len_set: function (len) { /* public */
310         this.ifra.contentWindow.ctx_old_len = len;
311     },
312
313     ctx_old_len_add: function (len) { /* public */
314         this.ifra.contentWindow.ctx_old_len += len;
315     },
316
317     new_part: function () { /* public */
318         return (this.ifra.contentWindow.ctx_new.substr(this.ifra.contentWindow.ctx_old_len));
319     },
320
321     scrcls_set: function (step) { /* public */
322         this.ifra.contentWindow.script_clean = step;
323     },
324
325     postproc: function () {
326     }
327 }
328
329 function xynt_streaming(win, transp_type, console, gst, from, cookiename, sess, sandbox, page, cmdproc)
330 {
331     this.win = win;
332     this.transp_type = transp_type;
333     this.console = console;
334     this.gst = gst;
335     this.from = from;
336     this.cookiename = cookiename;
337     this.sess = sess;
338     this.sandbox = sandbox;
339     this.page = page;
340     this.cmdproc = cmdproc;
341     // this.cmdproc = function(com){/* console.log("COM: "+com); */ eval(com);}
342
343     this.doc = win.document;
344     this.keepalive_old = -1;
345     this.keepalive_new = -1;
346 }
347
348 xynt_streaming.prototype = {
349     win:               null,
350     transp_type:       null,
351     transp:            null,
352     console:           null,
353     gst:               null,
354     from:              null,
355     cookiename:        null,
356     sess:              null,
357     sandbox:           null,
358     page:              null,
359     cmdproc:           null,
360
361     doc:               null,
362     cookiepath: "/brisk/",
363     watchdog_hdl:      null,
364     hbit:              null,
365     keepalive_old:    -1,
366     keepalive_new:    -1,
367     keepalives_equal:  0,
368     /* NOTE: right watch_timeout value to 100, for devel reasons use 1000 or more */
369     /* restart after  4 * 40 * 100 millisec if server ping is missing => 16secs */
370     keepalives_eq_max: 4,
371     watchdog_checktm:  40,
372     // FIXME watchdog_timeout:  100,
373     watchdog_timeout:  100,
374     watchdog_ct:       0,
375     watchable:         false,
376     restart_n:         0,
377     comm_match:        /_*@BEGIN@(.*?)@END@/g, 
378     comm_clean:        /_*@BEGIN@(.*?)@END@/,
379     stream:            "",
380     the_end:           false,
381
382     start: function() { /* public */
383         if (this.the_end) 
384             return;
385
386         createCookie(this.cookiename, sess, 24*365, this.cookiepath);
387         // alert("start");
388         this.log("xynt_streaming:start restart: "+this.restart_n);
389         this.keepalives_equal = 0;
390
391         // page arrangement
392         this.page = url_complete(this.win.location.href, this.page);
393         // stat, subst, this.gst.st
394
395         this.page = url_append_args(this.page, "sess", this.sess, "stat", stat, "subst", subst, "step", this.gst.st, "from", this.from);
396         this.log(this.page);
397
398         // transport instantiation
399         if (this.transp_type == "xhr") {
400             this.page = url_append_args(this.page, "transp", "xhr");
401             this.transp = new transport_xhr(this.doc, this, this.page);
402         }
403         else if (this.transp_type == "iframe") {
404             this.page = url_append_args(this.page, "transp", "iframe");
405             this.transp = new transport_iframe(this.doc, this, this.page);
406         }
407         else if (this.transp_type == "htmlfile") {
408             this.page = url_append_args(this.page, "transp", "htmlfile");
409             this.transp = new transport_htmlfile(this.doc, this, this.page);
410         }
411         else
412             return;
413
414         // watchdog setting
415         this.watchdog_ct  = 0;
416         if (!this.the_end) {
417             this.watchdog_hdl = setTimeout(function(obj) { obj.log("tout1"); obj.watchdog(); }, this.watchdog_timeout, this);
418         }
419     },
420
421     stop: function() {
422         this.the_end = true;
423         this.abort();
424     },
425
426     hbit_set: function (hbit) {
427         this.hbit = hbit;
428     },
429
430     watchdog: function () {
431         // alert("watchdog");
432         var i, again;
433         var comm_newpart, comm_len, comm_arr;
434         var ctx_new_len;
435
436         if (this.sandbox != null) {
437             // from old: var zug = "POLL sess = "+sess+" stat = "+stat+" subst = "+subst+" step = "+this.gst.st+" step_loc = "+this.gst.st_loc+" step_loc_new = "+this.gst.st_loc_new+" STOP: "+this.stopped;
438             var zug = "WATCHDOG  sess = ["+this.sess+"]  step = "+this.gst.st+" step_loc = "+this.gst.st_loc+" step_loc_new = "+this.gst.st_loc_new;          
439             if (zug != this.sandbox.innerHTML)
440                 this.sandbox.innerHTML = zug;
441         }
442
443         // WATCHDOGING THE CONNECTION
444         this.log("hs::watchdog: start, cur equal times: "+this.keepalives_equal);
445         if (!this.watchable) {
446             do {
447                 try{
448                     // if (typeof(this.ifra.contentWindow.xynt_streaming) == 'undefined')
449                     if (!this.transp.xstr_is_init()) {
450                         this.log("hs::watchdog: xstr_is_init = false");
451                         break;
452                     }
453                 }
454                 catch(b) {
455                     this.log("hs::watchdog: exception");
456                     break;
457                 }
458
459                 /*
460                   on IE7 the the window frame scope is cleaned after the href is set, so we wait 
461                   for a well know variable value before assign this object value to it (OO is a passion)
462                 */
463                 // if (this.ifra.contentWindow.xynt_streaming == "ready") {
464                 if (this.transp.xstr_set()) {
465                     // this.ifra.contentWindow.xynt_streaming = this;
466                     this.watchable = true;
467                     this.watchdog_ct = 0;
468                     this.log("hs::watchdog: watchable = yes");
469                 }
470             } while (false);
471         }
472         if ( (this.watchdog_ct % this.watchdog_checktm) == 0) {
473             this.log("hs::watchdog: this.keepalive_old: "+this.keepalive_old+" this.keepalive_new: "+this.keepalive_new);
474             if (this.keepalive_old == this.keepalive_new) {
475                 this.keepalives_equal++;
476             }
477             else {
478                 this.keepalive_old = this.keepalive_new;
479                 this.keepalives_equal = 0;
480             }
481             
482             if (this.keepalives_equal >= this.keepalives_eq_max) {
483                 this.log("hs::watchdog: MAX ACHIEVED "+this.keepalives_equal);
484                 this.reload();
485                 // alert("watchdog return reload");
486                 return;
487             }
488         }
489
490         // PICK COMMANDS FROM STREAM
491         do {
492             // alert("do--while begin ["+again+"]");
493             // CHECK: maybe again here isn't needed 
494             again = 0;
495             try {
496                 /* if (typeof(this.ifra.contentWindow.ctx_new)     == 'undefined' ||
497                    typeof(this.ifra.contentWindow.ctx_old_len) == 'undefined') */
498                 if (!this.transp.ctx_new_is_set() || !this.transp.ctx_old_len_is_set())
499                     break;
500             }
501             catch(b) {
502                 break;
503             }
504
505             // ctx_new_len = this.ifra.contentWindow.ctx_new.length;
506             ctx_new_len = this.transp.ctx_new_curlen_get();
507             // if (ctx_new_len <= this.ifra.contentWindow.ctx_old_len) {
508             if (ctx_new_len <= this.transp.ctx_old_len_get()) {
509                 break;
510             }
511             this.log("new: "+ ctx_new_len + "  old: "+this.transp.ctx_old_len_get());
512             this.keepalive_new++;
513             // alert("pre-loop 1");
514             for (i = this.transp.ctx_old_len_get() ; i < ctx_new_len ; i++) {
515                 // if (this.ifra.contentWindow.ctx_new.charAt(i) != '_') {
516                 if (this.transp.ctx_new_getchar(i) != '_') {
517                     // this.log("ctx_new.char(i) != '_' ["+this.ifra.contentWindow.ctx_new.charAt(i)+"]");
518                     break;
519                 }
520                 // else {
521                 //     this.log("ctx_new.charAt(i) == '_'");
522                 // }
523             }
524             // this.ifra.contentWindow.ctx_old_len = i;
525             this.transp.ctx_old_len_set(i);
526             if (i == ctx_new_len) {
527                 this.log("old_len == i");
528                 break;
529             }
530             else {
531                 this.log("old_len != i: "+i);
532             }
533             // alert("do--while middle ["+this.ifra.contentWindow.ctx_old_len+"]");
534
535             comm_newpart = this.transp.new_part();
536             this.log("COM_NEWPART: ["+comm_newpart+"]");
537             comm_len = 0;
538             comm_arr = comm_newpart.match(this.comm_match);
539
540             // alert("do--while middle2 ["+again+"]");
541             if (comm_arr) {
542                 var comm_arr_len = comm_arr.length;
543                 for (i = 0 ; i < comm_arr_len ; i++) {
544                     var temp = comm_arr[i].replace(this.comm_clean,"$1").split("|");
545                     this.gst.comms = this.gst.comms.concat(temp);
546                     comm_len += comm_arr[i].length;
547                 }
548                 again = 1;
549             }
550             // this.ifra.contentWindow.ctx_old_len += comm_len;
551             this.transp.ctx_old_len_add(comm_len);
552             // this.ifra.contentWindow.script_clean = this.gst.st;
553             this.transp.scrcls_set(this.gst.st);
554             // alert("do--while end ["+again+"]");
555         } while (again);
556
557         // alert("post while");
558         // EXECUTION OF STREAM COMMANDS
559         do {
560             again = 0;
561             //MOP ?? xhrrestart = 0;
562             if (this.gst.st_loc < this.gst.st_loc_new) {
563                 // there is some slow actions running
564                 break;
565             }
566             else if (this.gst.comms.length > 0) {
567                 var singlecomm;
568                 
569                 singlecomm = this.gst.comms.shift();
570                 // alert("EXE"+gugu);
571                 // $("xhrdeltalog").innerHTML = "EVALL: "+singlecomm.replace("<", "&lt;", "g"); +"<br>";
572                 //xx this.hbit("+");
573
574                 // alert("SINGLE: ["+singlecomm+"]");
575                 this.cmdproc(singlecomm);
576                 again = 1;
577             }
578         } while (again);
579         this.watchdog_ct++;
580         if (!this.the_end) {
581             this.transp.postproc();
582             this.watchdog_hdl = setTimeout(function(obj) { /* obj.log("tout2"); */ obj.watchdog(); }, this.watchdog_timeout, this);
583         }
584         // alert("watchdog return normal");
585
586         return;
587     },
588
589     //
590     // moved to xynt-streaming-ifra as push()
591     //
592     // keepalive: function (s) {
593     //     this.log("hs::keepalive");
594     //     if (s != null) {
595     //         this.log(s);
596     //         this.ifra.contentWindow.ctx_new += "@BEGIN@"+s+"@END@";
597     //     }
598     //     else {
599     //         this.ifra.contentWindow.ctx_new += "_";
600     //     }
601     //     // this.keepalive_new++;
602     // },
603
604     abort: function () { /* public */
605         // this.log("PATH: "+this.ifra.contentWindow.location.protocol + "://" + this.ifra.contentWindow.location.host + "/" + this.ifra.contentWindow.location.pathname);
606
607         this.gst.abort();
608         if (this.watchdog_hdl != null) {
609             clearTimeout(this.watchdog_hdl);
610             this.watchdog_hdl = null;
611         }
612
613         this.restart_n++;
614         this.log("hs::reload");
615         this.watchable = false;
616         if (this.transp != null) {
617             this.transp.destroy();
618             delete this.transp;
619             this.transp = null;
620         }
621     },
622
623     reload: function () {
624         this.abort();
625         this.start(null);
626     },
627
628     log: function (s) {
629         if (this.console != null) {
630             return (this.console.log(s));
631         }
632     }
633 }