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