heartbit graphical feedback added
[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><body><iframe id='iframe'></iframe></body></html>");
142     this.transfdoc.close();
143
144     this.ifra = this.transfdoc.getElementById("iframe");
145     this.ifra.contentWindow.location.href = page;
146     this.stopped = false;
147 }
148
149 transport_htmlfile.prototype = {
150     doc: null,
151     xynt_streaming: null,
152     stopped: true,
153     ifra: null,
154     tradoc: null,
155
156     destroy: function () { /* public */
157         if (this.ifra != null) {
158         //     this.doc.body.removeChild(this.ifra);
159         //     delete this.ifra;
160              this.ifra = null;
161         }
162
163         if (this.transfdoc) {
164             delete this.transfdoc;
165             this.transfdoc = null;
166         }
167     },
168
169     xstr_is_init: function () { /* public */
170         return (typeof(this.ifra.contentWindow.xynt_streaming) != 'undefined');
171     },
172
173     /* only after a successfull is_initialized call */
174     xstr_is_ready: function () { /* public */
175         return (this.ifra.contentWindow.xynt_streaming == "ready");
176     },
177
178     /* only after a successfull is_initialized call */
179     xstr_set: function () { /* public */
180         if (this.ifra.contentWindow.xynt_streaming == "ready") {
181             this.ifra.contentWindow.xynt_streaming = this.xynt_streaming;
182             return (true);
183         }
184         else if (this.ifra.contentWindow.xynt_streaming == this.xynt_streaming) {
185             return (true);
186         }
187         else {
188             return (false);
189         }
190     },
191
192     ctx_new_is_set: function () { /* public */
193         return (typeof(this.ifra.contentWindow.ctx_new) != 'undefined');
194     },
195
196     ctx_new_curlen_get: function () { /* public */
197         return (this.ifra.contentWindow.ctx_new.length);
198     },
199
200     ctx_new_getchar: function(idx) { /* public */
201         return (this.ifra.contentWindow.ctx_new.charAt(idx));
202     },
203
204     ctx_old_len_is_set: function () { /* public */
205         return (typeof(this.ifra.contentWindow.ctx_old_len) != 'undefined');
206     },
207
208     ctx_old_len_get: function () { /* public */
209         return (this.ifra.contentWindow.ctx_old_len);
210     },
211
212     ctx_old_len_set: function (len) { /* public */
213         this.ifra.contentWindow.ctx_old_len = len;
214     },
215
216     ctx_old_len_add: function (len) { /* public */
217         this.ifra.contentWindow.ctx_old_len += len;
218     },
219
220     new_part: function () { /* public */
221         return (this.ifra.contentWindow.ctx_new.substr(this.ifra.contentWindow.ctx_old_len));
222     },
223
224     scrcls_set: function (step) { /* public */
225         this.ifra.contentWindow.script_clean = step;
226     },
227
228     postproc: function () { /* public */
229         if (this.stopped && !this.xstr_is_ready()) {
230             this.xynt_streaming.reload();
231         }
232     }
233 }
234
235
236
237 //
238 // CLASS transport_iframe
239 //
240 function transport_iframe(doc, xynt_streaming, page)
241 {
242     this.doc = doc;
243     this.xynt_streaming = xynt_streaming;
244     this.ifra = doc.createElement("iframe");
245     this.ifra.style.visibility = "hidden";
246     doc.body.appendChild(this.ifra);
247     this.ifra.contentWindow.location.href = page;
248     this.stopped = false;
249 }
250
251 transport_iframe.prototype = {
252     doc: null,
253     xynt_streaming: null,
254     stopped: true,
255     ifra: null,
256
257     destroy: function () { /* public */
258         try {
259             if (this.ifra != null) {
260                 // NOTE:  on Opera this remove child crash js if called from
261                 //        inside of the iframe, on IE on Windows without
262                 //        it stream abort fails.
263                 //        the problem is fixed setting into the iframe's onload
264                 //        function the stopped attribute to true and delegate
265                 //        postproc() fired by xynt_streaming watchdog()
266                 this.doc.body.removeChild(this.ifra);
267                 delete this.ifra;
268                 this.ifra = null;
269             }
270         } catch (b) {
271             alert("destroy exception catched");
272         }
273     },
274
275     xstr_is_init: function () { /* public */
276         return (typeof(this.ifra.contentWindow.xynt_streaming) != 'undefined');
277     },
278
279     /* only after a successfull is_initialized call */
280     xstr_is_ready: function () { /* public */
281         return (this.ifra.contentWindow.xynt_streaming == "ready");
282     },
283
284     /* only after a successfull is_initialized call */
285     xstr_set: function () { /* public */
286         if (this.ifra.contentWindow.xynt_streaming == "ready") {
287             this.ifra.contentWindow.xynt_streaming = this.xynt_streaming;
288             return (true);
289         }
290         else if (this.ifra.contentWindow.xynt_streaming == this.xynt_streaming) {
291             return (true);
292         }
293         else {
294             return (false);
295         }
296     },
297
298
299     /* only after a successfull is_ready call to be sure the accessibility of the var */
300     xstr_set_old: function (xynt_streaming) { /* public */
301         this.ifra.contentWindow.xynt_streaming = xynt_streaming;
302     },
303
304     ctx_new_is_set: function () { /* public */
305         return (typeof(this.ifra.contentWindow.ctx_new) != 'undefined');
306     },
307
308     ctx_new_curlen_get: function () { /* public */
309         return (this.ifra.contentWindow.ctx_new.length);
310     },
311
312     ctx_new_getchar: function(idx) { /* public */
313         return (this.ifra.contentWindow.ctx_new.charAt(idx));
314     },
315
316     ctx_old_len_is_set: function () { /* public */
317         return (typeof(this.ifra.contentWindow.ctx_old_len) != 'undefined');
318     },
319
320     ctx_old_len_get: function () { /* public */
321         return (this.ifra.contentWindow.ctx_old_len);
322     },
323
324     ctx_old_len_set: function (len) { /* public */
325         this.ifra.contentWindow.ctx_old_len = len;
326     },
327
328     ctx_old_len_add: function (len) { /* public */
329         this.ifra.contentWindow.ctx_old_len += len;
330     },
331
332     new_part: function () { /* public */
333         return (this.ifra.contentWindow.ctx_new.substr(this.ifra.contentWindow.ctx_old_len));
334     },
335
336     scrcls_set: function (step) { /* public */
337         this.ifra.contentWindow.script_clean = step;
338     },
339
340     postproc: function () { /* public */
341         if (this.stopped && !this.xstr_is_ready()) {
342             this.xynt_streaming.reload();
343         }
344     }
345 }
346
347 function xynt_streaming(win, transp_type, console, gst, from, cookiename, sess, sandbox, page, cmdproc)
348 {
349     this.win = win;
350     this.transp_type = transp_type;
351     this.console = console;
352     this.gst = gst;
353     this.from = from;
354     this.cookiename = cookiename;
355     this.sess = sess;
356     this.sandbox = sandbox;
357     this.page = page;
358     this.cmdproc = cmdproc;
359     // this.cmdproc = function(com){/* console.log("COM: "+com); */ eval(com);}
360     this.doc = win.document;
361     this.keepalive_old = -1;
362     this.keepalive_new = -1;
363
364     this.mon_errtime = this.keepalives_eq_max * this.watchdog_checktm * this.watchdog_timeout;
365     this.mon_wrntime = this.mon_errtime / 2;
366
367     this.mon_update();
368
369 }
370
371 xynt_streaming.prototype = {
372     win:               null,
373     transp_type:       null,
374     transp:            null,
375     console:           null,
376     gst:               null,
377     from:              null,
378     cookiename:        null,
379     sess:              null,
380     sandbox:           null,
381     page:              null,
382     cmdproc:           null,
383
384     start_time:        0,
385     restart_wait:      5000, // wait restart_wait millisec before begin to check if restart is needed
386
387     doc:               null,
388     cookiepath: "/brisk/",
389     watchdog_hdl:      null,
390     hbit:              function () {},
391     keepalive_old:    -1,
392     keepalive_new:    -1,
393     keepalives_equal:  0,
394     /* NOTE: right watch_timeout value to 100, for devel reasons use 1000 or more */
395     /* restart after  4 * 40 * 100 millisec if server ping is missing => 16secs */
396     keepalives_eq_max: 4,
397     watchdog_checktm:  40,
398     watchdog_timeout:  100,
399     watchdog_ct:       0,
400     watchable:         false,
401     restart_n:         0,
402     comm_match:        /_*@BEGIN@(.*?)@END@/g, 
403     comm_clean:        /_*@BEGIN@(.*?)@END@/,
404     stream:            "",
405     the_end:           false,
406
407     mon_time:         -1,
408     mon_wrntime:       0,
409     mon_errtime:       0,
410
411     mon_stat_old:      "",
412
413     mon_update: function()
414     {
415         var date = new Date();
416
417         this.mon_time = date.getTime();
418     },
419
420     /*
421       ping arrives at least every RD_KEEPALIVE_TOUT secs (currently 4 secs)
422
423       return values: 0 unknown
424                      1 ok
425                      2 warning
426                      3 error
427      */
428     mon_status: function()
429     {
430         var delta, date;
431
432         var date = new Date();
433
434         delta = date.getTime() - this.mon_time;
435
436         if (delta < this.mon_wrntime)
437             return 1;
438         else if (delta < this.mon_errtime)
439             return 2;
440         else
441             return 3;
442     },
443
444     start: function() { /* public */
445         if (this.the_end) 
446             return;
447
448         createCookie(this.cookiename, sess, 24*365, this.cookiepath);
449         // alert("start");
450         this.log("xynt_streaming:start restart: "+this.restart_n);
451         this.keepalives_equal = 0;
452
453         // page arrangement
454         this.page = url_complete(this.win.location.href, this.page);
455         // stat, subst, this.gst.st
456
457         this.page = url_append_args(this.page, "sess", this.sess, "stat", stat, "subst", subst, "step", this.gst.st, "from", this.from);
458         this.log(this.page);
459
460         // transport instantiation
461         if (this.transp_type == "xhr") {
462             this.page = url_append_args(this.page, "transp", "xhr");
463             this.transp = new transport_xhr(this.doc, this, this.page);
464         }
465         else if (this.transp_type == "iframe") {
466             this.page = url_append_args(this.page, "transp", "iframe");
467             this.transp = new transport_iframe(this.doc, this, this.page);
468         }
469         else if (this.transp_type == "htmlfile") {
470             this.page = url_append_args(this.page, "transp", "htmlfile");
471             this.transp = new transport_htmlfile(this.doc, this, this.page);
472         }
473         else
474             return;
475
476         // watchdog setting
477         this.watchdog_ct  = 0;
478         if (!this.the_end) {
479             this.watchdog_hdl = setTimeout(function(obj) { obj.log("tout1"); obj.watchdog(); }, this.watchdog_timeout, this);
480         }
481
482         var date = new Date();
483         this.start_time = date.getTime();
484     },
485
486     stop: function() {
487         this.the_end = true;
488         this.abort();
489     },
490
491     hbit_set: function (hbit) {
492         this.hbit = hbit;
493     },
494
495
496     hbit_status: function () {
497         var ret;
498
499         ret = this.mon_status();
500         // console.log("mon_status: "+ret+" 0: "+this.mon_time);
501         switch (ret) {
502         case 0:
503             mon_stat = "b";
504             break;
505         case 1:
506             mon_stat = "g";
507             break;
508         case 2:
509             mon_stat = "y";
510             break;
511         case 3:
512             mon_stat = "r";
513             break;
514         }
515
516         if (this.mon_stat_old != mon_stat) {
517             this.hbit(mon_stat);
518             this.mon_stat_old = mon_stat;
519         }
520     },
521
522
523     watchdog: function () {
524         // alert("watchdog");
525         var i, again;
526         var comm_newpart, comm_len, comm_arr;
527         var ctx_new_len;
528
529         if (this.sandbox != null) {
530             // 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;
531             var zug = "WATCHDOG  sess = ["+this.sess+"]  step = "+this.gst.st+" step_loc = "+this.gst.st_loc+" step_loc_new = "+this.gst.st_loc_new;          
532             if (zug != this.sandbox.innerHTML)
533                 this.sandbox.innerHTML = zug;
534         }
535
536         // WATCHDOGING THE CONNECTION
537         this.log("hs::watchdog: start, cur equal times: "+this.keepalives_equal);
538         if (!this.watchable) {
539             do {
540                 try{
541                     // if (typeof(this.ifra.contentWindow.xynt_streaming) == 'undefined')
542                     if (!this.transp.xstr_is_init()) {
543                         this.log("hs::watchdog: xstr_is_init = false");
544                         break;
545                     }
546                 }
547                 catch(b) {
548                     this.log("hs::watchdog: exception");
549                     break;
550                 }
551
552                 /*
553                   on IE7 the the window frame scope is cleaned after the href is set, so we wait 
554                   for a well know variable value before assign this object value to it (OO is a passion)
555                 */
556                 // if (this.ifra.contentWindow.xynt_streaming == "ready") {
557                 if (this.transp.xstr_set()) {
558                     // this.ifra.contentWindow.xynt_streaming = this;
559                     this.watchable = true;
560                     this.watchdog_ct = 0;
561                     this.log("hs::watchdog: watchable = yes");
562                 }
563             } while (false);
564         }
565         if ( (this.watchdog_ct % this.watchdog_checktm) == 0) {
566             this.log("hs::watchdog: this.keepalive_old: "+this.keepalive_old+" this.keepalive_new: "+this.keepalive_new);
567             if (this.keepalive_old == this.keepalive_new) {
568                 this.keepalives_equal++;
569             }
570             else {
571                 this.keepalive_old = this.keepalive_new;
572                 this.keepalives_equal = 0;
573             }
574             
575             if (this.keepalives_equal >= this.keepalives_eq_max) {
576                 this.log("hs::watchdog: MAX ACHIEVED "+this.keepalives_equal);
577                 this.reload();
578                 // alert("watchdog return reload");
579                 this.hbit_status();
580                 return;
581             }
582         }
583
584         // PICK COMMANDS FROM STREAM
585         do {
586             // alert("do--while begin ["+again+"]");
587             // CHECK: maybe again here isn't needed 
588             again = 0;
589             try {
590                 /* if (typeof(this.ifra.contentWindow.ctx_new)     == 'undefined' ||
591                    typeof(this.ifra.contentWindow.ctx_old_len) == 'undefined') */
592                 if (!this.transp.ctx_new_is_set() || !this.transp.ctx_old_len_is_set())
593                     break;
594             }
595             catch(b) {
596                 break;
597             }
598
599             // ctx_new_len = this.ifra.contentWindow.ctx_new.length;
600             ctx_new_len = this.transp.ctx_new_curlen_get();
601             // if (ctx_new_len <= this.ifra.contentWindow.ctx_old_len) {
602             if (ctx_new_len <= this.transp.ctx_old_len_get()) {
603                 break;
604             }
605             this.log("new: "+ ctx_new_len + "  old: "+this.transp.ctx_old_len_get());
606             this.keepalive_new++;
607             // alert("pre-loop 1");
608             for (i = this.transp.ctx_old_len_get() ; i < ctx_new_len ; i++) {
609                 // if (this.ifra.contentWindow.ctx_new.charAt(i) != '_') {
610                 if (this.transp.ctx_new_getchar(i) != '_') {
611                     // this.log("ctx_new.char(i) != '_' ["+this.ifra.contentWindow.ctx_new.charAt(i)+"]");
612                     break;
613                 }
614                 this.mon_update();
615                 this.hbit_status();
616
617                 // else {
618                 //     this.log("ctx_new.charAt(i) == '_'");
619                 // }
620             }
621             // this.ifra.contentWindow.ctx_old_len = i;
622             this.transp.ctx_old_len_set(i);
623             if (i == ctx_new_len) {
624                 this.log("old_len == i");
625                 break;
626             }
627             else {
628                 this.log("old_len != i: "+i);
629             }
630             // alert("do--while middle ["+this.ifra.contentWindow.ctx_old_len+"]");
631
632             comm_newpart = this.transp.new_part();
633             this.log("COM_NEWPART: ["+comm_newpart+"]");
634             comm_len = 0;
635             comm_arr = comm_newpart.match(this.comm_match);
636
637             // alert("do--while middle2 ["+again+"]");
638             if (comm_arr) {
639                 var comm_arr_len = comm_arr.length;
640                 for (i = 0 ; i < comm_arr_len ; i++) {
641                     var temp = comm_arr[i].replace(this.comm_clean,"$1").split("|");
642                     this.gst.comms = this.gst.comms.concat(temp);
643                     comm_len += comm_arr[i].length;
644                 }
645                 again = 1;
646                 this.mon_update();
647                 this.hbit_status();
648             }
649             // this.ifra.contentWindow.ctx_old_len += comm_len;
650             this.transp.ctx_old_len_add(comm_len);
651             // this.ifra.contentWindow.script_clean = this.gst.st;
652             this.transp.scrcls_set(this.gst.st);
653             // alert("do--while end ["+again+"]");
654         } while (again);
655
656         // alert("post while");
657         // EXECUTION OF STREAM COMMANDS
658         do {
659             again = 0;
660             //MOP ?? xhrrestart = 0;
661             if (this.gst.st_loc < this.gst.st_loc_new) {
662                 // there is some slow actions running
663                 break;
664             }
665             else if (this.gst.comms.length > 0) {
666                 var singlecomm;
667                 
668                 singlecomm = this.gst.comms.shift();
669                 // alert("EXE"+gugu);
670                 // $("xhrdeltalog").innerHTML = "EVALL: "+singlecomm.replace("<", "&lt;", "g"); +"<br>";
671                 //xx this.hbit("+");
672
673                 // alert("SINGLE: ["+singlecomm+"]");
674                 this.cmdproc(singlecomm);
675                 again = 1;
676             }
677         } while (again);
678         this.watchdog_ct++;
679         if (!this.the_end) {
680             var date = new Date();
681             if (date.getTime() > (this.start_time + this.restart_wait)) {
682                 this.transp.postproc();
683             }
684             this.watchdog_hdl = setTimeout(function(obj) { /* obj.log("tout2"); */ obj.watchdog(); }, this.watchdog_timeout, this);
685             this.hbit_status();
686         }
687         // alert("watchdog return normal");
688
689         return;
690     },
691
692     //
693     // moved to xynt-streaming-ifra as push()
694     //
695     // keepalive: function (s) {
696     //     this.log("hs::keepalive");
697     //     if (s != null) {
698     //         this.log(s);
699     //         this.ifra.contentWindow.ctx_new += "@BEGIN@"+s+"@END@";
700     //     }
701     //     else {
702     //         this.ifra.contentWindow.ctx_new += "_";
703     //     }
704     //     // this.keepalive_new++;
705     // },
706
707     abort: function () { /* public */
708         // this.log("PATH: "+this.ifra.contentWindow.location.protocol + "://" + this.ifra.contentWindow.location.host + "/" + this.ifra.contentWindow.location.pathname);
709
710         this.gst.abort();
711         if (this.watchdog_hdl != null) {
712             clearTimeout(this.watchdog_hdl);
713             this.watchdog_hdl = null;
714         }
715
716         this.restart_n++;
717         this.log("hs::reload");
718         this.watchable = false;
719         if (this.transp != null) {
720             this.transp.destroy();
721             delete this.transp;
722             this.transp = null;
723         }
724     },
725
726     reload: function () {
727         this.abort();
728         this.start(null);
729     },
730
731     log: function (s) {
732         if (this.console != null) {
733             return (this.console.log(s));
734         }
735     }
736 }