1 /*---------------------------------------------------------------------
  2  *  This is part of the Cloud Computing interface of the Karoo project.
  3  *
  4  *  (C) 2009 Brian Modra <brian@zwartberg.com>
  5  *
  6  *  This library is free software; you can redistribute them and/or modify
  7  *  it under the terms of the GNU Lesser General Public License as published by
  8  *  the Free Software Foundation; either version 2.1 of the License,
  9  *  or (at your option) any later version.
 10  *
 11  *  This library is distributed in the hope that it will be useful,
 12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 14  *  See the GNU Lesser General Public License for more details.
 15  *
 16  *  You should have received a copy of the GNU Lesser General Public License
 17  *  along with these libraries; if not, write to the Free Software Foundation,
 18  *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 19  *----------------------------------------------------------------------*/
 20 
 21 /** @fileOverview This file contains Javascript functions for the client-side interface of the surf gateway
 22  * 
 23  * The Karoo Project System uses UDP for messaging. A "Cloud Computing" interface must use HTTP,
 24  * so there is a gateway. The gateway is the surf rock. It receives "messages" from the web client,
 25  * which are actually pre-packaged UDP messages ready to be sent on their way.
 26  * Hence the binary format has found its way, at least part way, into the javascript interface.
 27  * An integer in Javascript is a maximum of 48 bits, and a float is 64 bits. A compromise was met:
 28  * A 128 bit float will be truncated to 64 bits, and a 64 bit int will be
 29  * truncated to 48 bits. In the other direction, they are expanded.
 30  *
 31  * Please see also
 32  * <a href="../index.html">The Karoo Project doxygen documentation</a>
 33  */
 34 
 35 if (typeof XMLHttpRequest  === "undefined") {
 36     XMLHttpRequest = function() {
 37 	try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
 38 	catch(e) {}
 39 	try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
 40 	catch(e) {}
 41 	try { return new ActiveXObject("Msxml2.XMLHTTP"); }
 42 	catch(e) {}
 43 	try { return new ActiveXObject("Microsoft.XMLHTTP"); }
 44 	catch(e) {}
 45 	alert("This application will not work on this browser. Please install and use Firefox.");
 46     };
 47 }
 48 
 49 /** 
 50  * This is a handy function to use while debugging. If the console object is not available,
 51  * I.e. if Firebug is not installed, then it will pop up an alert instead.
 52  *
 53  * @param {string} str the message to be displayed
 54  */
 55 function debug(str)
 56 {
 57     try {
 58 	console.debug(str);
 59     }
 60     catch (e) {
 61 	alert(str);
 62     }
 63 }
 64 
 65 /** 
 66  * Gets the type of an Object.
 67  * @param val the object to be queried
 68  * @return "number", "string", or "array".
 69  * @type string
 70  */
 71 function getType(val)
 72 {
 73     var str = val.constructor.toString();
 74     if (str.match(/number/i)) {
 75 	return "number";
 76     }
 77     else if (str.match(/string/i)) {
 78 	return "string";
 79     }
 80     else if (str.match(/array/i)) {
 81 	return "array";
 82     }
 83     return typeof(val);
 84 }
 85 
 86 /** 
 87  * This will extract the numeric part of a string or object.
 88  *
 89  * @param n the Object that represents a number 
 90  * @return the Object after parsing to a number
 91  * @type number
 92  */
 93 function convertToNumber(n)
 94 {
 95     var type = getType(n);
 96     if (type == "string") {
 97 	var re = /[a-z,A-Z ]+/g;
 98 	var s = n.replace(re, "");
 99 	return parseFloat(s);
100     }
101     else if (type == "array") {
102 	var a = new Array();
103 	for (var i = 0; i < n.length; i++) {
104 	    a[i] = convertToNumber(n[i]);
105 	}
106         return a;
107     }
108     else if (type == "number") {
109 	return n;
110     }
111     else {
112 	return convertToNumber(""+n);
113     }
114 }
115 
116 /** 
117  * Writes a floating point number into IEEE binary format.
118  *
119  * <p>Oops there should be a problem: this should only work for up to 32 bit floats. Above that,
120  * there will be a rounding off of precision
121  * because the Javascript number is actually a 64-bit float, with only 48 bits for pure integer values.
122  * This must be fixed, probably best to convert it so it uses a string, and hex encoded values.
123  * Suprisingly, it works!</p>
124  *
125  * @param {number} val the floating point number
126  * @param {number} mantissa_width e.g. for a 32 bit float, this is 23
127  * @param {number} mantissa_mask e.g. for a 32 bit float, this is 0x007FFFFF
128  * @param {number} mancap this is the mantissa cap e.g. for a 32 bit float, it is 0x00800000
129  * @param {number} exponent_mask e.g. for a for a 32 bit float, it is 0x7F800000
130  * @param {number} exponent_bias e.g. for a for a 32 bit float, it is 127
131  * @param {number} negative_bit e.g. for a for a 32 bit float, it is 0x80000000
132  * @return the binary format
133  */
134 function toIEEE(val, mantissa_width, mantissa_mask, mancap, exponent_mask, exponent_bias, negative_bit)
135 {
136     val = convertToNumber(val);
137 
138     if (val == 0.0) {
139 	return 0;
140     }
141     else {
142 	var neg;
143 	var frac;
144 	if (val < 0.0) {
145 	    neg = true;
146 	    val = -val;
147 	}
148 	else {
149 	    neg = false;
150 	}
151 	var exp = parseInt(Math.log(val)/Math.log(2));
152 	if (exp <= 0) {
153 	    if (Math.pow(2, exp) > val)
154 		exp--;
155 	}
156 	if (exp > exponent_bias) {
157 	    exp = exponent_bias*2+1;
158 	    frac = 0;
159 	}
160 	else if (exp == (-exponent_bias)) {
161 	    exp = 0;
162 	    frac = Math.round((val / Math.pow(2, -(exponent_bias-1))) * mancap);
163 	}
164 	else if (exp < (-exponent_bias)) {
165 	    frac = 0;
166 	    exp = 0;
167 	}
168 	else {
169 	    frac = Math.round(((val / Math.pow(2, exp)) - 1.0) * mancap);
170 	    exp += exponent_bias;
171 	}
172 	var num = neg? negative_bit : 0;
173 	num |= exp << mantissa_width;
174 	num |= frac & mantissa_mask;
175 	return num;
176     }
177 }
178 
179 /** 
180  * 
181  *
182  * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p>
183  *
184  * @param {number} num the binary format 
185  * @param {number} mantissa_width e.g. for a 32 bit float, this is 23
186  * @param {number} mantissa_mask e.g. for a 32 bit float, this is 0x007FFFFF
187  * @param {number} mancap this is the mantissa cap e.g. for a 32 bit float, it is 0x00800000
188  * @param {number} exponent_mask e.g. for a for a 32 bit float, it is 0x7F800000
189  * @param {number} exponent_bias e.g. for a for a 32 bit float, it is 127
190  * @param {number} negative_bit e.g. for a for a 32 bit float, it is 0x80000000
191  * @return the number once converted
192  * @type number 
193  */
194 function fromIEEE(num, mantissa_width, mantissa_mask, mancap, exponent_mask, exponent_bias, negative_bit)
195 {
196     var neg = (num & negative_bit) != 0;
197     var exp = (num & exponent_mask) >> mantissa_width;
198     if ((num & exponent_mask) == exponent_mask) {
199 	if ((num & mantissa_mask) != 0) {
200 	    return "NaN";
201 	}
202 	else if (neg) {
203 	    return "-INFINITY";
204 	}
205 	else {
206 	    return "INFINITY";
207 	}
208     }
209     else if (exp == 0) {
210 	if ((num & mantissa_mask) != 0) {
211 	    var data = Math.pow(2, -(exponent_bias-1)) * (num & mantissa_mask) / mancap;
212 	    if (neg)
213 		data = -data;
214 	    return data;
215 	}
216 	else if (neg) {
217 	    return -0.0;
218 	}
219 	else {
220 	    return 0.0;
221 	}
222     }
223     else {
224 	var data = Math.pow(2.0, exp - exponent_bias) * (1.0 + (num & mantissa_mask) / mancap);
225 	if (neg)
226 	    data = -data;
227 	return data;
228     }
229 }
230 
231 
232 function toIEEE32(val)
233 {
234     return toIEEE(val, 23, 0x007FFFFF, 0x00800000, 0x7F800000, 127, 0x80000000);
235 }
236 
237 function toIEEE64(val)
238 {
239     return toIEEE(val, 52, 0x000FFFFFFFFFFFFF, 0x0010000000000000, 0x7FF0000000000000, 1023, 0x8000000000000000);
240 }
241 
242 function fromIEEE32(val)
243 {
244     return fromIEEE(val, 23, 0x007FFFFF, 0x00800000, 0x7F800000, 127, 0x80000000);
245 }
246 
247 function fromIEEE64(val)
248 {
249     return fromIEEE(val, 52, 0x000FFFFFFFFFFFFF, 0x0010000000000000, 0x7FF0000000000000, 1023, 0x8000000000000000);
250 }
251 
252 /** @private */
253 var requester_arr = new Array();
254 /** @private */
255 var requester_id = 0;
256 
257 /** @private */
258 function findRequester(id)
259 {
260     for (var i = 0; i < requester_arr.length; i++) {
261 	var r = requester_arr[i];
262 	if (r.id == id) {
263 	    return r;
264 	}
265     }
266     return null;
267 }
268 
269 /** @private */
270 function removeRequester(id)
271 {
272     for (var i = 0; i < requester_arr.length; i++) {
273 	var r = requester_arr[i];
274 	if (r.id == id) {
275 	    requester_arr.splice(i, 1);
276 	    return;
277 	}
278     }
279 }
280 
281 /** @private */
282 function pollerTimeout(id)
283 {
284     for (var i = 0; i < server_poller_arr.length; i++) {
285 	var poller = server_poller_arr[i];
286 	if (poller.id == id) {
287 	    var millis = (new Date()).getTime();
288 	    var ok = millis > (poller.fireAt - poller.poll_delay / 20);
289 	    if (poller.expecting || ok || poller.queued.length) {
290 		if (ok)
291 		    poller.expecting = false;
292 		poller.timer_id = null;
293 		poller.poll();
294 		return;
295 	    }
296 	    else {
297 		poller.timer_id = setTimeout("pollerTimeout("+id+");", poller.poll_delay / 5);
298 	    }
299 	}
300     }
301 }
302 
303 /**
304  * Get a random alpha-numeric character.
305  * @return a randomly selected alpha-numeric character
306  * @type string
307  */
308 function getRandomChar()
309 {
310     var i = Math.floor(Math.random()*3);
311     switch (i) {
312     case 0:
313 	return String.fromCharCode('0'.charCodeAt()+Math.floor(Math.random()*10));
314     case 1:
315 	return String.fromCharCode('a'.charCodeAt()+Math.floor(Math.random()*26));
316     default:
317 	return String.fromCharCode('A'.charCodeAt()+Math.floor(Math.random()*26));
318     }
319 }
320 
321 /** 
322  * This class is the basic datagram message object used in communication to/from the serf gateway.
323  *
324  * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p>
325  *
326  * @constructor
327  * @param {string} recipient_name the name of the rock which will receive the message
328  * @param {string} recipient_type the type ofthe rock. this is only needed if the recipient_name is null. Then
329  * the message will be sent to the least loaded rock of the specified type.
330  * @param {number} message_type the message type, a rock-specific number for the message type.
331  * @param {string} hex the body of the message encoded as hex in a text string
332  * @type rock_datagram_message
333  */
334 function rock_datagram_message(recipient_name, recipient_type, message_type, hex)
335 {
336     this.recipient_name = recipient_name;
337     this.recipient_type = recipient_type;
338     this.message_type = message_type;
339     this.hex = hex? hex: "";
340     this.pos = 0;
341 
342     /**
343      * set the type of the rock that will receive this message
344      * @param {string} type the textual type of the recipient rock
345      */
346     this.setRecipientType = function(type)
347     {
348 	this.recipient_type = type;
349     }
350 
351     /**
352      * Set the name of the rock that will receive this message
353      * @param {string} name the recipient rock's name
354      */
355     this.setRecipientName = function(name)
356     {
357 	this.recipient_name = name;
358     }
359 
360     /**
361      * Set the message type
362      * @param {number} type the message type (as opposed to recipient rock type) 
363      */
364     this.setType = function(type)
365     {
366 	this.message_type = type;
367     }
368 
369     /**
370      * Write a number to the end of the hex encoded message body
371      * @param {number} val the number to write
372      * @param {number} size the number of bits to write
373      */
374     this.writeNumber = function(val, size)
375     {
376 	size /= 8;
377 	for (var i = 0; i < size; i++) {
378 	    var nibble = new Number(val & 0xf);
379 	    var c2 = nibble.toString(16);
380 	    val = val >> 4;
381 	    nibble = new Number(val & 0xf);
382 	    this.hex += nibble.toString(16);
383 	    this.hex += c2;
384 	    val = val >> 4;
385 	}
386     }
387 
388     /**
389      * Read a number from the message, starting from where it last finished reading from.
390      * @param {number} size the number of bits to read.
391      * @return the number
392      * @type number
393      */
394     this.readNumber = function(size)
395     {
396 	val = 0;
397 	size /= 8;
398 	for (var i = 0; i < size; i++) {
399 	    var c1 = hex.charCodeAt(this.pos++);
400 	    if (c1 >= 97 && c1 <= 102) {
401 		c1 = 10 + c1 - 97;
402 	    }
403 	    else if (c1 >= 65 && c1 <= 70) {
404 		c1 = 10 + c1 - 65;
405 	    }
406 	    else if (c1 >= 48 && c1 <= 57) {
407 		c1 -= 48;
408 	    }
409 	    var c2 = hex.charCodeAt(this.pos++);
410 	    if (c2 >= 97 && c2 <= 102) {
411 		c2 = 10 + c2 - 97;
412 	    }
413 	    else if (c2 >= 65 && c2 <= 70) {
414 		c2 = 10 + c2 - 65;
415 	    }
416 	    else if (c2 >= 48 && c2 <= 57) {
417 		c2 -= 48;
418 	    }
419 	    val |= ((c1 << 4) + c2) << (8 * i);
420 	}
421 	return val;
422     }
423 
424     /**
425      * Write an unsigned 8-bit number to the end of the hex encoded message body
426      * @param {number} val the number to write
427      */
428     this.writeU8 = function(val)
429     {
430 	this.writeNumber(val, 8);
431     }
432 
433     /**
434      * Read an unsigned 8-bit number from the message, starting from where it last finished reading from.
435      * @return the number
436      * @type number
437      */
438     this.readU8 = function()
439     {
440 	return this.readNumber(8);
441     }
442 
443     /**
444      * Write an unsigned 16-bit number to the end of the hex encoded message body
445      * @param {number} val the number to write
446      */
447     this.writeU16 = function(val)
448     {
449 	this.writeNumber(val, 16);
450     }
451 
452     /**
453      * Read an unsigned 16-bit number from the message, starting from where it last finished reading from.
454      * @return the number
455      * @type number
456      */
457     this.readU16 = function()
458     {
459 	return this.readNumber(16);
460     }
461 
462     /**
463      * Write an unsigned 32-bit number to the end of the hex encoded message body
464      * @param {number} val the number to write
465      */
466     this.writeU32 = function(val)
467     {
468 	this.writeNumber(val, 32);
469     }
470 
471     /**
472      * Read an unsigned 32-bit number from the message, starting from where it last finished reading from.
473      * @return the number
474      * @type number
475      */
476     this.readU32 = function()
477     {
478 	return this.readNumber(32);
479     }
480 
481     /**
482      * Write an unsigned 64-bit number to the end of the hex encoded message body
483      * @param {number} val the number to write
484      */
485     this.writeU64 = function(val)
486     {
487 	this.writeNumber(val, 64);
488     }
489 
490     /**
491      * Read an unsigned 64-bit number from the message, starting from where it last finished reading from.
492      * @return the number
493      * @type number
494      */
495     this.readU64 = function()
496     {
497 	return this.readNumber(64);
498     }
499 
500     /**
501      * Write an integer (signed) 8-bit number to the end of the hex encoded message body
502      * @param {number} val the number to write
503      */
504     this.writeI8 = function(val)
505     {
506 	this.writeNumber(val, 8);
507     }
508 
509     /**
510      * Read an integer (signed) 8-bit number from the message, starting from where it last finished reading from.
511      * @return the number
512      * @type number
513      */
514     this.readI8 = function()
515     {
516 	var val = this.readNumber(8);
517 	if ((val & 0x80) != 0) {
518 	    val &= 0x7f;
519 	    val = - val;
520 	}
521 	return val;
522     }
523 
524     /**
525      * Write an integer (signed) 16-bit number to the end of the hex encoded message body
526      * @param {number} val the number to write
527      */
528     this.writeI16 = function(val)
529     {
530 	this.writeNumber(val, 16);
531     }
532 
533     /**
534      * Read an integer (signed) 16-bit number from the message, starting from where it last finished reading from.
535      * @return the number
536      * @type number
537      */
538     this.readI16 = function()
539     {
540 	var val = this.readNumber(16);
541 	if ((val & 0x8000) != 0) {
542 	    val &= 0x7fff;
543 	    val = - val;
544 	}
545 	return val;
546     }
547 
548     /**
549      * Write an integer (signed) 32-bit number to the end of the hex encoded message body
550      * @param {number} val the number to write
551      */
552     this.writeI32 = function(val)
553     {
554 	this.writeNumber(val, 32);
555     }
556 
557     /**
558      * Read an integer (signed) 32-bit number from the message, starting from where it last finished reading from.
559      * @return the number
560      * @type number
561      */
562     this.readI32 = function()
563     {
564 	var val = this.readNumber(32);
565 	if ((val & 0x80000000) != 0) {
566 	    val &= 0x7fffffff;
567 	    val = - val;
568 	}
569 	return val;
570     }
571 
572     /**
573      * Write a float 32-bit number to the end of the hex encoded message body
574      * @param {number} val the number to write
575      */
576     this.writeF = function(val)
577     {
578 	this.writeNumber(toIEEE32(val), 32);
579     }
580 
581     /**
582      * Read a 32-bit float number from the message, starting from where it last finished reading from.
583      * @return the number
584      * @type number
585      */
586     this.readF = function()
587     {
588 	var val = this.readNumber(32);
589 	return fromIEEE32(val);
590     }
591 
592     /**
593      * Write a text string to the end of the hex encoded message body
594      * @param {string} val the text to write
595      */
596     this.writeS = function(val)
597     {
598 	this.writeNumber(val.length, 32);
599 	for (var i = 0; i < val.length; i++) {
600 	    this.writeNumber(val.charCodeAt(i), 8);
601 	}
602     }
603 
604     /**
605      * Read a text string from the message, starting from where it last finished reading from.
606      * @return the text
607      * @type string
608      */
609     this.readS = function()
610     {
611 	var len = this.readNumber(32);
612 	var val = "";
613 	for (var i = 0; i < len; i++) {
614 	    var c = this.readNumber(8)
615 	    val += String.fromCharCode(c);
616 	}
617 	return val;
618     }
619 
620     /** @private */
621     this.processReqChange = function(req, poller, obj)
622     {
623 	if (req.readyState == 4) {
624 	    if (req.status == 200) {
625 		if (req.responseXML != null) {
626 		    var nodes = req.responseXML.getElementsByTagName("queued-message");
627 		    
628 		    for (var i = 0; i < nodes.length; i++) {
629 			var n = nodes.item(i);
630 			var qd = new rock_datagram_message(n.getAttribute("rock-name"), null,
631 			    parseInt(n.getAttribute("type")), n.getAttribute("hex"));
632 			poller.parse(qd);
633 		    }
634 		}
635 		else {
636 		    //alert("no data");
637 		}
638 		if (poller.timer_id == null) {
639 		    poller.fireAt = (new Date()).getTime() + poller.poll_delay;
640 		    poller.timer_id = setTimeout("pollerTimeout("+poller.id+");", poller.poll_delay / 5);
641 		    return;
642 		}
643 	    }
644 	    else if (req.status != 200) {
645 		//alert("fetched the wrong page or network error...");
646 	    }
647 	    removeRequester(obj.id);
648 	}
649     }
650 
651     /** 
652      * Send this message
653      *
654      * @param {server_poller} poller the poller to send the message via, which will also handle the reply 
655      */
656     this.send = function(poller)
657     {
658 	if (!(this.recipient_name || this.recipient_type)) {
659 	    alert("no type or name in rock_datagram_message object when send() function called");
660 	    return;
661 	}
662 
663 	var obj = new Object();
664 	obj.id = requester_id++;
665 	obj.poller = poller;
666 	obj.msg = this;
667 	requester_arr.push(obj);
668 	eval("var func = function() { var obj = findRequester("+obj.id+"); if (obj) { obj.msg.processReqChange(this, obj.poller, obj); } }");
669 
670 	var req = new XMLHttpRequest();
671 	req.onreadystatechange = func;
672 	req.open("POST", poller.url, true);
673 	//req.setRequestHeader("Username", poller.username);
674 	//req.setRequestHeader("Password", poller.password);
675 
676 	if (false) {
677 	    req.setRequestHeader("Content-Type", "multipart/form-data");
678 	    var boundary = "";
679 	    for (var i = 0; i < 20; i++) {
680 		boundary += getRandomChar();
681 	    }
682 	    var data = "";
683 	    if (this.recipient_name) {
684 	    }
685 	}
686 	else {
687 	    req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
688 	    var data = "";
689 
690 	    if (this.recipient_name) {
691 		data = "rock-name=" + encodeURIComponent(this.recipient_name);
692 	    }
693 	    if (this.recipient_type) {
694 		var str = "rock-type=" + encodeURIComponent(this.recipient_type);
695 		if (data.length) {
696 		    data += "&" + str;
697 		}
698 		else {
699 		    data = str;
700 		}
701 	    }
702 	    data += "&type=" + encodeURIComponent(this.message_type);
703 	    data += "&hex=" + encodeURIComponent(this.hex);
704 
705 	    req.send(data);
706 	}
707 
708 	if (this.message_type != 10 || this.recipient_type != "surf" || poller.stopped) {
709 	    poller.setExpecting();
710 	}
711     }
712 }
713 
714 /** @private@ */
715 var server_poller_arr = new Array();
716 /** @private@ */
717 var server_poller_id = 0;
718 
719 /** 
720  * This object handles the polling and sending to/from the gateway.
721  *
722  * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p>
723  *
724  * @constructor
725  * @param {number} poll_delay the number of milliseconds between normal polls
726  * @param {string} url the URL of the server
727  * @param {function} handler the function to call to handle incoming messages
728  */
729 function server_poller(poll_delay, url, /*username, password,*/ handler)
730 {
731     this.url = url;
732     //this.username = username;
733     //this.password = password;
734     this.handler = handler;
735     this.poll_delay = poll_delay;
736     this.id = server_poller_id++;
737     this.timer_id = null;
738     this.fireAt = null;
739     this.expecting = false;
740     this.queued = new Array();
741     this.stopped = true;
742 
743     /**
744      * This function executes the poll.
745      */
746     this.poll = function()
747     {
748 	var found = false;
749 	for (var i = 0; i < server_poller_arr.length; i++) {
750 	    var poller = server_poller_arr[i];
751 	    if (poller.id == this.id) {
752 		found = true;
753 		break;
754 	    }
755 	}
756 	if (!found) {
757 	    server_poller_arr.push(this);
758 	}
759 	this.stoppped = false;
760 	if (this.queued.length) {
761 	    var message = this.queued[0];
762 	    this.queued.splice(0, 1);
763 	    message.send(this);
764 	}
765 	else {
766 	    var message = new rock_datagram_message();
767 	    message.setRecipientType("surf");
768 	    message.setType(10);
769 	    message.send(this);
770 	}
771     }
772 
773     /**
774      * Send a message
775      * @param {rock_datagram_message} message the message to send
776      */
777     this.send = function(message)
778     {
779 	var found = false;
780 	for (var i = 0; i < server_poller_arr.length; i++) {
781 	    var poller = server_poller_arr[i];
782 	    if (poller.id == this.id) {
783 		found = true;
784 		break;
785 	    }
786 	}
787 	if (!found) {
788 	    server_poller_arr.push(this);
789 	}
790 	this.queued.push(message);
791 	this.setExpecting();
792     }
793 
794     /** @private */
795     this.parse = function(qd)
796     {
797 	this.expecting = false;
798 	this.handler(this, qd);
799     }
800 
801     /**
802      * Stop the poller
803      */
804     this.stop = function()
805     {
806 	this.stopped = true;
807 	for (var i = 0; i < server_poller_arr.length; i++) {
808 	    var poller = server_poller_arr[i];
809 	    if (poller.id == this.id) {
810 		server_poller_arr.splice(i, 1);
811 		clearTimeout(this.timer_id);
812 		this.timer_id = null;
813 		server_poller_arr.splice(i, 1);
814 		break;
815 	    }
816 	}
817     }
818 
819     /**
820      * Tell the poller that it should expect a message. this speeds up the polling rate by a factor of 10.
821      * You should not need to call this, because the poller does it automatically when you send a message,
822      * until the reply is received.
823      * The "expecting" status only holds until it receives a message.
824      */
825     this.setExpecting = function()
826     {
827 	this.stopped = false;
828 	this.expecting = true;
829 	if (this.timer_id)
830 	    clearTimeout(this.timer_id);
831 	this.fireAt = (new Date()).getTime();
832 	this.timer_id = setTimeout("pollerTimeout("+this.id+");", 1);
833     }
834 
835     //server_poller_arr.push(this);
836 
837     //this.poll(); ... only start it when requested by the caller
838 }
839