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 cave rock 22 * 23 * <p>Although this file is for the cave rock, communication to the cave actually occurs via the surf 24 * rock.</p> 25 * 26 * <p>The low level interface is implemented in client.js</p> 27 * 28 * <p>The Karoo Project System uses UDP for messaging. A "Cloud Computing" interface must use HTTP, 29 * so there is a gateway. The gateway is the surf rock. It receives "messages" from the web client, 30 * which are actually pre-packaged UDP messages ready to be sent on their way. 31 * Hence the binary format has found its way, at least part way, into the javascript interface. 32 * An integer in Javascript is a maximum of 48 bits, and a float is 64 bits. A compromise was met: 33 * A 128 bit float will be truncated to 64 bits, and a 64 bit int will be 34 * truncated to 48 bits. In the other direction, they are expanded.</p> 35 * 36 * <p>Please see also <a href="../index.html">The Karoo Project API documentation</a>, and especially, the 37 * <a href="../web_client_interface_page.html">Karoo project web client interface</a>.</p> 38 */ 39 40 /** 41 * this is the type of a column when its NULL 42 * @see caveParam 43 */ 44 var DB_DATA_TYPE_NULL = 0; 45 46 /** 47 * this is the type of a database column when it is an 8 bit unsigned integer 48 * @see caveParam 49 */ 50 var DB_DATA_TYPE_UNSIGNED_8 = 1; // 51 52 /** 53 * this is the type of a database column when it is a boolean value 54 * @see caveParam 55 */ 56 var DB_DATA_TYPE_BOOL = 2; 57 58 /** 59 * this is the type of a database column when it is an 8 bit character 60 * @see caveParam 61 */ 62 var DB_DATA_TYPE_CHAR = 3; 63 64 /** 65 * this is the type of a database column when it is a 16 bit unsigned integer 66 * @see caveParam 67 */ 68 var DB_DATA_TYPE_UNSIGNED_16 = 4; 69 70 /** 71 * this is the type of a database column when it is a 32 bit unsigned integer 72 * @see caveParam 73 */ 74 var DB_DATA_TYPE_UNSIGNED_32 = 5; 75 76 /** 77 * this is the type of a database column when it is a 64 bit unsigned integer 78 * @see caveParam 79 */ 80 var DB_DATA_TYPE_UNSIGNED_64 = 6; 81 82 /** 83 * this is the type of a database column when it is an 8 bit integer 84 * @see caveParam 85 */ 86 var DB_DATA_TYPE_INT_8 = 7; 87 88 /** 89 * this is the type of a database column when it is a 16 bit short integer 90 * @see caveParam 91 */ 92 var DB_DATA_TYPE_INT_16 = 8; 93 94 /** 95 * this is the type of a database column when it is a 32 bit integer 96 * @see caveParam 97 */ 98 var DB_DATA_TYPE_INT_32 = 9; 99 100 /** 101 * this is the type of a database column when it is a 64 bit integer 102 * @see caveParam 103 */ 104 var DB_DATA_TYPE_INT_64 = 10; 105 106 /** 107 * this is the type of a database column when it is a 32 bit float 108 * @see caveParam 109 */ 110 var DB_DATA_TYPE_FLOAT = 11; 111 112 /** 113 * this is the type of a database column when it is a 64 bit float 114 * @see caveParam 115 */ 116 var DB_DATA_TYPE_DOUBLE = 12; 117 118 /** 119 * this is the type of a database column when it is a 128 bit float 120 * @see caveParam 121 */ 122 var DB_DATA_TYPE_LONG_DOUBLE = 13; 123 124 /** 125 * this is the type of a database column when it contains a string (text, char, varchar etc) 126 * @see caveParam 127 */ 128 var DB_DATA_TYPE_STRING = 14; 129 130 /** 131 * The cave_poller is the object that maintains contact with the gateway and sends/gets the messages 132 * @type server_poller 133 */ 134 var cave_poller = null; 135 136 /** 137 * This function initiates the cave interface. 138 * 139 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 140 * 141 * @param {number} poll_delay the number of milliseconds between polls, 5000 is recommended (5 seconds). 142 * @param {string} url the URL of the service. 143 * @return there is no return value. 144 */ 145 function initCave(poll_delay, url /*, username, password*/) 146 { 147 if (cave_poller == null) { 148 cave_poller = new server_poller(poll_delay, url, /*username, password,*/ caveHandler); 149 var img = document.getElementById("busy-anim-img"); 150 if (img) { 151 var src = img.getAttribute("src"); 152 var ind = src.lastIndexOf('_'); 153 if (ind != -1) { 154 src = src.substring(0, ind+1); 155 for (i = 0; i < 36; i++) { 156 pic= new Image(100,100); 157 pic.src = src + i + ".png"; 158 } 159 } 160 } 161 } 162 } 163 164 /** 165 * Stop the poller. If your application knows that there are no messages that will be sent to your 166 * application that is running in the web browser, then there is no need to keep polling (for 167 * something that will not happen.) Once the poller has been stopped, it will not start again 168 * until you send another message from the web application. Then it will resume polling as it was 169 * previously configured. 170 * 171 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 172 */ 173 function caveStop() 174 { 175 if (cave_poller) { 176 cave_poller.stop(); 177 } 178 } 179 180 /** @private */ 181 var busyAnimId = null; 182 183 /** @private */ 184 function busyAnimate(ind, busy) 185 { 186 if (busyAnimId == null) 187 return; 188 var img = document.getElementById("busy-anim-img"); 189 if (!img) { 190 busyAnimId = null; 191 return; 192 } 193 194 if (img.complete) { 195 var src = img.getAttribute("src"); 196 var i = src.lastIndexOf('_'); 197 if (i == -1) { 198 busyAnimId = null; 199 return; 200 } 201 src = src.substring(0, i+1); 202 img.src = src + ind + ".png"; 203 ind++; 204 if (ind > 35 || !busy) 205 ind = 0; 206 } 207 if (busy) { 208 busyAnimId = setTimeout("busyAnimate("+ind+",true);", 100); 209 } 210 else { 211 busyAnimId = null; 212 } 213 } 214 215 /** 216 * Call this function to cause the busy icon to spin. 217 * 218 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 219 * 220 * @param {boolean} busy true if it is busy 221 */ 222 function setBusy(busy) 223 { 224 var img = document.getElementById("busy-anim-img"); 225 if (!img) { 226 return; 227 } 228 var src = img.getAttribute("src"); 229 var i = src.lastIndexOf('.'); 230 if (i == -1) { 231 return; 232 } 233 src = src.substr(0, i); 234 if (busy) { 235 img.src = src + ".gif"; 236 } 237 else { 238 img.src = src + ".png"; 239 } 240 /* 241 if (busy) { 242 if (!busyAnimId) { 243 busyAnimId = setTimeout("busyAnimate(0,true);", 1); 244 } 245 } 246 else { 247 if (busyAnimId) { 248 clearTimeout(busyAnimId); 249 } 250 busyAnimId = setTimeout("busyAnimate(0,false);", 1); 251 } 252 */ 253 } 254 255 /** @private */ 256 var CAVE_MESSAGE_SQL_SUCCESS = 0x80000001; 257 /** @private */ 258 var CAVE_MESSAGE_SQL_FAILURE = 0x80000002; 259 /** @private */ 260 var CAVE_MESSAGE_SQL_ROW = 0x80000003; 261 /** @private */ 262 var CAVE_MESSAGE_SQL_DONE = 0x80000004; 263 264 /** @private */ 265 function updateCaveTrans(msg, name, seq, sql, row, params) 266 { 267 var trans = null; 268 for (var i = 0; i < caveTransactions.length; i++) { 269 var c = caveTransactions[i]; 270 if (c.name == name) { 271 if (c.arr) { 272 for (var j = 0; j < c.arr.length; j++) { 273 var obj = c.arr[j]; 274 if (obj.seq == seq) { 275 trans = obj; 276 if (msg.message_type == CAVE_MESSAGE_SQL_FAILURE) { 277 c.arr.splice(j, 1); 278 if (c.arr.length == 0) { 279 caveTransactions.splice(i, 1); 280 } 281 } 282 break; 283 } 284 } 285 } 286 else if (c.failed) { 287 caveTransactions.splice(i, 1); 288 } 289 break; 290 } 291 } 292 293 if (caveTransactions.length) { 294 setBusy(true); 295 } 296 else { 297 setBusy(false); 298 } 299 300 switch (msg.message_type) { 301 case CAVE_MESSAGE_SQL_SUCCESS: 302 { 303 if (trans) { 304 trans.rows = row; 305 } 306 } 307 break; 308 case CAVE_MESSAGE_SQL_FAILURE: 309 { 310 var detail = "sql #"+str+", row#"+row+", message was from "+msg.recipient_name+"("+msg.message_type+")"; 311 alert("failure: "+params+"\n"+detail); 312 if (trans) { 313 trans.failed = true; 314 } 315 } 316 break; 317 case CAVE_MESSAGE_SQL_ROW: 318 { 319 if (trans) { 320 var arr; 321 if (sql < trans.sqls.length) { 322 arr = trans.sqls[sql]; 323 } 324 else { 325 arr = trans.sqls[sql] = new Array(); 326 } 327 arr[row] = params; 328 trans.checkDone(); 329 } 330 } 331 break; 332 case CAVE_MESSAGE_SQL_DONE: 333 { 334 if (trans) { 335 trans.rows = row; 336 trans.done = true; 337 trans.checkDone(sql); 338 } 339 } 340 break; 341 } 342 } 343 344 /** @private */ 345 function caveHandler(poller, msg) 346 { 347 switch (msg.message_type) { 348 case CAVE_MESSAGE_SQL_SUCCESS: 349 { 350 var reply_id_s = msg.readS(); 351 var reply_id_i = msg.readU64(); 352 var sql = msg.readU8(); 353 var row = msg.readU32(); 354 updateCaveTrans(msg, reply_id_s, reply_id_i, sql, row); 355 } 356 break; 357 case CAVE_MESSAGE_SQL_FAILURE: 358 { 359 var reply_id_s = msg.readS(); 360 var reply_id_i = msg.readU64(); 361 var sql = msg.readU8(); 362 var row = msg.readU32(); 363 var err = msg.readS(); 364 updateCaveTrans(msg, reply_id_s, reply_id_i, sql, row, err); 365 } 366 break; 367 case CAVE_MESSAGE_SQL_ROW: 368 { 369 var reply_id_s = msg.readS(); 370 var reply_id_i = msg.readU64(); 371 var sql = msg.readU8(); 372 var row = msg.readU32(); 373 var cols = msg.readU8(); 374 var params = new Array(); 375 for (var i = 0; i < cols; i++) { 376 var column = msg.readS(); 377 var type = msg.readU8(); 378 var val = null; 379 switch (type) { 380 case DB_DATA_TYPE_NULL: 381 break; 382 case DB_DATA_TYPE_UNSIGNED_8: 383 val = msg.readU8(); 384 break; 385 case DB_DATA_TYPE_BOOL: 386 val = msg.readU8(); 387 if (val == 0) { 388 val = false; 389 } 390 else { 391 val = true; 392 } 393 break; 394 case DB_DATA_TYPE_CHAR: 395 val = msg.readU8(); 396 val = String.fromCharCode(val); 397 break; 398 case DB_DATA_TYPE_UNSIGNED_16: 399 val = msg.readU16(); 400 break; 401 case DB_DATA_TYPE_UNSIGNED_32: 402 val = msg.readU32(); 403 break; 404 case DB_DATA_TYPE_UNSIGNED_64: 405 val = msg.readU64(); 406 break; 407 case DB_DATA_TYPE_INT_8: 408 val = msg.readI8(); 409 break; 410 case DB_DATA_TYPE_INT_16: 411 val = msg.readI16(); 412 break; 413 case DB_DATA_TYPE_INT_32: 414 val = msg.readI32(); 415 break; 416 case DB_DATA_TYPE_INT_64: 417 val = msg.readI64(); 418 break; 419 case DB_DATA_TYPE_FLOAT: 420 val = msg.readF(); 421 break; 422 case DB_DATA_TYPE_DOUBLE: 423 val = msg.readD(); 424 break; 425 case DB_DATA_TYPE_LONG_DOUBLE: 426 val = msg.readLD(); 427 break; 428 case DB_DATA_TYPE_STRING: 429 val = msg.readS(); 430 break; 431 } 432 var param = new caveParam(column, type, val) 433 params.push(param); 434 } 435 updateCaveTrans(msg, reply_id_s, reply_id_i, sql, row, params); 436 } 437 break; 438 case CAVE_MESSAGE_SQL_DONE: 439 { 440 var reply_id_s = msg.readS(); 441 var reply_id_i = msg.readU64(); 442 var sql = msg.readU8(); 443 var row = msg.readU32(); 444 updateCaveTrans(msg, reply_id_s, reply_id_i, sql, row); 445 } 446 break; 447 } 448 } 449 450 /** @private */ 451 var caveTransactionSeq = 0; 452 453 /** 454 * remove all queued transactions (so they don't get sent, or if they already have been sent, so their 455 * replies are ignored. 456 * 457 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 458 */ 459 function zapTransactions() 460 { 461 caveTransactions = new Array(); 462 setBusy(false); 463 } 464 465 466 /** 467 * Create a new caveTransaction object. 468 * You should not call this directly, but rather use the beginTransaction function, 469 * which also inserts the caveTransacrtion object into a list of current transactions. 470 * 471 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 472 * 473 * @constructor 474 * @param {string} name unique transaction name from the client-side. This is used 475 * as a key into the list of waiting transactions, for associating requests with replies. 476 * @param {function} doneCallback the function to call when a reply comes in for this 477 * transaction. 478 */ 479 function caveTransaction(name, doneCallback) 480 { 481 this.name = name; 482 this.seq = ++caveTransactionSeq; 483 this.doneCallback = doneCallback; 484 this.sqls = new Array(); 485 this.rows = 0; 486 this.done = false; 487 this.failed = false; 488 489 /** @private */ 490 this.checkDone = function(sql) 491 { 492 var arr = null; 493 494 if (!sql) { 495 sql = this.sqls.length; 496 } 497 498 var row_count = 0; 499 for (var i = 0; i < sql; i++) { 500 arr = this.sqls[i]; 501 if (arr) { 502 for (var j = 0; j < arr.length; j++) { 503 if (arr[j]) { 504 row_count++; 505 } 506 } 507 } 508 } 509 var processed = this.done && (row_count == this.rows); 510 if (this.failed) { 511 processed = true; 512 } 513 514 if (processed) { 515 for (var i = 0; i < caveTransactions.length; i++) { 516 var c = caveTransactions[i]; 517 if (c.name == this.name) { 518 if (c.arr) { 519 for (var j = 0; j < c.arr.length; j++) { 520 var obj = c.arr[j]; 521 if (obj.seq == this.seq) { 522 c.arr.splice(j, 1); 523 if (c.arr.length == 0) { 524 caveTransactions.splice(i, 1); 525 } 526 break; 527 } 528 } 529 } 530 else if (c.failed) { 531 caveTransactions.splice(i, 1); 532 } 533 break; 534 } 535 } 536 537 if (this.doneCallback) { 538 this.doneCallback(this); 539 } 540 541 if (caveTransactions.length) { 542 setBusy(true); 543 } 544 else { 545 setBusy(false); 546 } 547 } 548 } 549 } 550 551 /** 552 * This object is used in many functions for passing parameters to/from the services. 553 * 554 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 555 * 556 * @constructor 557 * @param {string} name the name of the parameter 558 * @param {number} type tye type of the parameter (DB_DATA_TYPE_...) 559 * @param value The value (class is determined by the type value) 560 */ 561 function caveParam(name, type, value) 562 { 563 this.name = name; 564 this.type = type; 565 this.value = value; 566 } 567 568 /** @private */ 569 var caveTransactions = new Array(); 570 571 /** 572 * Check if there is a transaction already in progress with the same name. 573 * This is useful for stopping overload and confusion if a user keeps clicking a 574 * send button for example. 575 * 576 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 577 * 578 * @param {string} name the client-side unique name of the transaction that was used when beginTransaction was called. 579 * @return true if there is a transaction in progress now. 580 */ 581 function caveQueryInProgress(name) 582 { 583 for (var i = 0; i < caveTransactions.length; i++) { 584 var c = caveTransactions[i]; 585 if (c.name == name) { 586 return true; 587 } 588 } 589 return false; 590 } 591 592 /** 593 * Create a new caveTransaction object and insert it into the list of waiting transactions. 594 * 595 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 596 * 597 * @param {string} name unique transaction name from the client-side. This is used 598 * as a key into the list of waiting transactions, for associating requests with replies. 599 * @param {function} doneCallback the function to call when a reply comes in for this 600 * transaction. 601 * @return the transaction object 602 * @type caveTransaction 603 * @see sendCaveQuery 604 */ 605 function beginTransaction(name, doneCallback) 606 { 607 var ct = null; 608 for (var i = 0; i < caveTransactions.length; i++) { 609 var c = caveTransactions[i]; 610 if (c.name == name) { 611 ct = c; 612 break; 613 } 614 } 615 var trans = new caveTransaction(name, doneCallback); 616 if (ct) { 617 ct.arr.push(trans); 618 } 619 else { 620 ct = new Object(); 621 ct.arr = new Array(); 622 ct.name = name; 623 ct.arr.push(trans); 624 caveTransactions.push(ct); 625 } 626 return trans; 627 } 628 629 /** 630 * This is a convenience function for sending a message to the cave rock. 631 * 632 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 633 * 634 * Assuming you have a service defined in your cave rock's configuration file something like this: 635 * 636 * @param {string} name the client-side unique name of the transaction 637 * @param {string} id the ID of the cave service that you are calling 638 * @param {Array} params an array of caveParam objects that are being used to send parameters to the service 639 * @param {function} doneCallback the function to call when the reply has completed 640 * @param {string} rock_name the name of the cave rock. 641 * 642 * @example 643 * <service name="user" id="login"> 644 * <sql transaction="commit"> 645 * select permission,username,sessionid from login( 646 * <parameter name="username"/>, 647 * <parameter name="password"/>); 648 * </sql> 649 * </service> 650 * ... then you could have something like the following as the javascript 651 * that is called when the "login" button is clicked: 652 * @example 653 * function doLogin() 654 * { 655 * if (!caveQueryInProgress("login")) { 656 * var params = new Array(); 657 * params[0] = new caveParam("username", DB_DATA_TYPE_STRING, username); 658 * params[1] = new caveParam("password", DB_DATA_TYPE_STRING, password); 659 * sendCaveQuery("login", "login", params, doneLoginCallback, "ke-cave"); 660 * } 661 * } 662 * 663 * The callback function will eventually be called and passed one parameter: 664 * the caveTransaction object. 665 * @see findParametersInTransaction 666 * 667 * @example 668 * function doneLoginCallback(trans) 669 * { 670 * var params = new Array(); 671 * params[0] = new caveParam("permission"); 672 * params[1] = new caveParam("sessionid"); 673 * findParametersInTransaction(trans, params); 674 * permission = 0; 675 * sessionid = null; 676 * if (params[0].value) { 677 * permission = params[0].value; 678 * } 679 * if (params[1].value) { 680 * sessionid = params[1].value; 681 * } 682 * if (permission == 0) { 683 * alert("invalid username or password"); 684 * } 685 * else if ((permission & 3) != 3) { 686 * alert("no permission to make changes"); 687 * } 688 * if ((permission & 3) != 3) { 689 * logout(); 690 * } 691 * else { 692 * login(); 693 * } 694 * caveStop(); // stop the polling now, no need to, we have the reply 695 * } 696 */ 697 function sendCaveQuery(name, id, params, doneCallback, rock_name) 698 { 699 var trans = beginTransaction(name, doneCallback); 700 var message = new rock_datagram_message(); 701 if (!rock_name) { 702 rock_name = "cave"; 703 } 704 message.setRecipientName(rock_name); 705 message.setRecipientType("cave"); 706 message.setType(10); 707 message.writeS(id); 708 message.writeS(trans.name); 709 message.writeU64(trans.seq); 710 message.writeU8(params.length); 711 for (var i = 0; i < params.length; i++) { 712 var p = params[i]; 713 message.writeS(p.name); 714 message.writeU8(p.type); 715 switch (p.type) { 716 case DB_DATA_TYPE_NULL: 717 break; 718 case DB_DATA_TYPE_UNSIGNED_8: 719 message.writeU8(p.value); 720 break; 721 case DB_DATA_TYPE_BOOL: 722 message.writeU8(p.value? 1:0); 723 break; 724 case DB_DATA_TYPE_CHAR: 725 message.writeU8(p.value.charCodeAt()); 726 break; 727 case DB_DATA_TYPE_UNSIGNED_16: 728 message.writeU16(p.value); 729 break; 730 case DB_DATA_TYPE_UNSIGNED_32: 731 message.writeU32(p.value); 732 break; 733 case DB_DATA_TYPE_UNSIGNED_64: 734 message.writeU64(p.value); 735 break; 736 case DB_DATA_TYPE_INT_8: 737 message.writeI8(p.value); 738 break; 739 case DB_DATA_TYPE_INT_16: 740 message.writeI16(p.value); 741 break; 742 case DB_DATA_TYPE_INT_32: 743 message.writeI32(p.value); 744 break; 745 case DB_DATA_TYPE_INT_64: 746 message.writeI64(p.value); 747 break; 748 case DB_DATA_TYPE_FLOAT: 749 message.writeF(p.value); 750 break; 751 case DB_DATA_TYPE_DOUBLE: 752 message.writeD(p.value); 753 break; 754 case DB_DATA_TYPE_LONG_DOUBLE: 755 message.writeLD(p.value); 756 break; 757 case DB_DATA_TYPE_STRING: 758 message.writeS(p.value); 759 break; 760 case DB_DATA_TYPE_BYTEA: 761 message.writeS(p.value); 762 break; 763 } 764 } 765 setBusy(true); 766 cave_poller.send(message); 767 } 768 769 /** 770 * This function will be called by a FORM with via the "onsubmit" attribute. This function 771 * creates a hex encoded message body which will be sent as part of a file submit via a "multipart/form-data" 772 * FORM post. 773 * 774 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 775 * 776 * @param {string} name the client-side unique name of the transaction 777 * @param {string} id the cave session ID to be called. 778 * @param {Array} params an array of caveParam objects containing the parameters to be sent to the service 779 * @param {number} seq a unique sequence number 780 * @return a HEX encoded message body reaty for sending to a service 781 * @type string 782 */ 783 function getHexOfPreparedCaveQuery(name, id, params, seq) 784 { 785 var message = new rock_datagram_message(); 786 message.writeS(id); 787 message.writeS(name); 788 message.writeU64(seq); 789 message.writeU8(params.length); 790 for (var i = 0; i < params.length; i++) { 791 var p = params[i]; 792 message.writeS(p.name); 793 message.writeU8(p.type); 794 switch (p.type) { 795 case DB_DATA_TYPE_NULL: 796 break; 797 case DB_DATA_TYPE_UNSIGNED_8: 798 message.writeU8(p.value); 799 break; 800 case DB_DATA_TYPE_BOOL: 801 message.writeU8(p.value? 1:0); 802 break; 803 case DB_DATA_TYPE_CHAR: 804 message.writeU8(p.value.charCodeAt()); 805 break; 806 case DB_DATA_TYPE_UNSIGNED_16: 807 message.writeU16(p.value); 808 break; 809 case DB_DATA_TYPE_UNSIGNED_32: 810 message.writeU32(p.value); 811 break; 812 case DB_DATA_TYPE_UNSIGNED_64: 813 message.writeU64(p.value); 814 break; 815 case DB_DATA_TYPE_INT_8: 816 message.writeI8(p.value); 817 break; 818 case DB_DATA_TYPE_INT_16: 819 message.writeI16(p.value); 820 break; 821 case DB_DATA_TYPE_INT_32: 822 message.writeI32(p.value); 823 break; 824 case DB_DATA_TYPE_INT_64: 825 message.writeI64(p.value); 826 break; 827 case DB_DATA_TYPE_FLOAT: 828 message.writeF(p.value); 829 break; 830 case DB_DATA_TYPE_DOUBLE: 831 message.writeD(p.value); 832 break; 833 case DB_DATA_TYPE_LONG_DOUBLE: 834 message.writeLD(p.value); 835 break; 836 case DB_DATA_TYPE_STRING: 837 message.writeS(p.value); 838 break; 839 case DB_DATA_TYPE_BYTEA: 840 message.writeS(p.value); 841 break; 842 } 843 } 844 return message.hex; 845 } 846 847 /** @private */ 848 function caveListRowMouseOver(td, id) 849 { 850 var cave_list = findCaveList(id); 851 if (cave_list) { 852 cave_list.rowChangeClass(td, false); 853 } 854 } 855 856 /** @private */ 857 function caveListRowMouseDown(td, id) 858 { 859 } 860 861 /** @private */ 862 function caveListRowMouseUp(td, id) 863 { 864 var cave_list = findCaveList(id); 865 if (cave_list) { 866 cave_list.rowChangeClass(td, true); 867 } 868 } 869 870 /** @private */ 871 var htmlns="http://www.w3.org/1999/xhtml"; 872 873 /** 874 * This is the support class for a list widget 875 * 876 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 877 * 878 * @constructor 879 * @param {string} id the unique ID of this list widget 880 * @param {document} doc the document (of the DOM) 881 * @param {Array} columns an array of caveColumn objects 882 * @see createCaveList 883 */ 884 function caveList(id, doc, columns) 885 { 886 this.id = id; 887 this.doc = doc; 888 this.columns = columns; 889 this.row_data = new Array(); 890 891 /** @private */ 892 this.createRow = function(header) 893 { 894 var tbody = this.doc.getElementById("cave-list-body"); 895 var cols = new Array(); 896 var tr = this.doc.createElementNS(htmlns, "tr"); 897 tr.setAttribute("class", "cave-list"); 898 tbody.appendChild(tr); 899 for (var i = 0; i < this.columns.length; i++) { 900 var td = this.doc.createElementNS(htmlns, header? "th": "td"); 901 td.setAttribute("class", "cave-list"); 902 if (!header) { 903 td.setAttribute("onmouseover", "caveListRowMouseOver(this,'"+this.id+"');"); 904 td.setAttribute("onmousedown", "caveListRowMouseDown(this,'"+this.id+"');"); 905 td.setAttribute("onmouseup", "caveListRowMouseUp(this,'"+this.id+"');"); 906 } 907 tr.appendChild(td); 908 cols[i] = td; 909 } 910 return cols; 911 } 912 913 /** @private */ 914 this.rowChangeClass = function(td, select) 915 { 916 var tr = td.parentNode; 917 var clazz = tr.className; 918 if (select) { 919 if (clazz == "cave-list-select") 920 return; 921 } 922 else { 923 if (clazz == "cave-list-highlight") 924 return; 925 } 926 var tbody = tr.parentNode; 927 for (var r = tbody.firstChild; r; r = r.nextSibling) { 928 if (r.nodeType == 1 && r.nodeName == "tr") { 929 var skip; 930 clazz = r.className; 931 var new_clazz = clazz == "cave-list-select-highlight"? "cave-list-select": "cave-list"; 932 if (select) { 933 skip = false; 934 } 935 else { 936 skip = clazz == "cave-list-select"; 937 } 938 if (!skip) { 939 if (r != tr && clazz != new_clazz) { 940 r.className = new_clazz; 941 for (var n = r.firstChild; n; n = n.nextSibling) { 942 if (n.nodeType == 1 && n.nodeName == "td") { 943 n.className = new_clazz; 944 } 945 } 946 } 947 } 948 } 949 } 950 951 clazz = tr.className; 952 if (clazz == "cave-list-select") { 953 clazz = select? "cave-list-select" : "cave-list-select-highlight"; 954 } 955 else { 956 clazz = select? "cave-list-select" : "cave-list-highlight"; 957 } 958 tr.className = clazz; 959 for (var n = tr.firstChild; n; n = n.nextSibling) { 960 if (n.nodeType == 1 && n.nodeName == "td") { 961 n.className = clazz; 962 } 963 } 964 } 965 966 /** 967 * Get the selected row 968 * @return an array of caveParam objects, representing the selected row. 969 */ 970 this.getSelected = function() 971 { 972 var row_ind = 0; 973 var tbody = this.doc.getElementById("cave-list-body"); 974 if (tbody) { 975 for (var tr = tbody.firstChild; tr; tr = tr.nextSibling) { 976 if (tr.className == "cave-list-select" || tr.className == "cave-list-select-highlight") { 977 return this.row_data[row_ind]; 978 } 979 row_ind++; 980 } 981 } 982 return null; 983 } 984 985 /** 986 * Clear the list of all rows. 987 */ 988 this.clear = function() 989 { 990 var tbody = this.doc.getElementById("cave-list-body"); 991 if (tbody) { 992 while (tbody.firstChild) { 993 tbody.removeChild(tbody.firstChild); 994 } 995 } 996 this.row_data = new Array(); 997 } 998 999 /** 1000 * Fill the list with rows from the caveTransaction object. 1001 * @param {caveTransaction} trans the transaction to get the row data from 1002 */ 1003 this.fill = function(trans) 1004 { 1005 var hdr = this.createRow(true); 1006 for (var i = 0; i < this.columns.length; i++) { 1007 hdr[i].appendChild(this.doc.createTextNode(this.columns[i].title)); 1008 } 1009 for (var i = 0; i < trans.sqls.length; i++) { 1010 var arr = trans.sqls[i]; 1011 for (var j = 0; j < arr.length; j++) { 1012 this.row_data.push(arr); 1013 var row = this.createRow(false); 1014 var a = arr[j]; 1015 for (var k = 0; k < a.length; k++) { 1016 var p = a[k]; 1017 for (var m = 0; m < this.columns.length; m++) { 1018 var c = this.columns[m]; 1019 if (p.name == c.name) { 1020 row[m].appendChild(this.doc.createTextNode(p.value)); 1021 } 1022 } 1023 } 1024 } 1025 } 1026 } 1027 } 1028 1029 /** @private */ 1030 var caveListMap = new Array(); 1031 1032 /** 1033 * This caveColumn object is used in the caveList object to describe the columns in the table. 1034 * 1035 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 1036 * 1037 * @constructor 1038 * @param {string} name the name of the column 1039 * @param {string} title the title of the column 1040 */ 1041 function caveColumn(name, title) 1042 { 1043 this.name = name; 1044 this.title = title; 1045 } 1046 1047 /** 1048 * This is a convenience function for creating a caveList object. 1049 * It also adds the caveList object to an array of lists so that when events happen, the system can 1050 * apply them to the appropriate caveList object. 1051 * 1052 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 1053 * 1054 * @param {string} id the unique ID of this list widget 1055 * @param {Array} columns an array of caveColumn objects 1056 */ 1057 function createCaveList(id, columns) 1058 { 1059 var frame = document.getElementById(id); 1060 if (!frame) 1061 return; 1062 var doc = frame.contentWindow.document; 1063 var cave_list = new caveList(id, doc, columns); 1064 caveListMap.push(cave_list); 1065 } 1066 1067 /** 1068 * This function deletes the specified (by ID) caveList object. 1069 * 1070 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 1071 * 1072 * @param {string} id the unique ID of this list widget 1073 */ 1074 function deleteCaveList(id) 1075 { 1076 for (var i = 0; i < caveListMap.length; i++) { 1077 var c = caveListMap[i]; 1078 if (c.id == id) { 1079 caveListMap.splice(i, 1); 1080 return; 1081 } 1082 } 1083 } 1084 1085 /** 1086 * This function finds the specified caveList object in the list. 1087 * 1088 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 1089 * 1090 * @param {string} id the unique ID of this list widget 1091 * @return the caveList object or null if not found 1092 * @type caveList 1093 */ 1094 function findCaveList(id) 1095 { 1096 for (var i = 0; i < caveListMap.length; i++) { 1097 var c = caveListMap[i]; 1098 if (c.id == id) { 1099 return c; 1100 } 1101 } 1102 return null; 1103 } 1104 1105 /** 1106 * This is a generally useful function for parsing a boolean value from an Object. 1107 * 1108 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 1109 * 1110 * @param val the object to be parsed 1111 * @return true if the val was "true" 1112 */ 1113 function parseBoolean(val) 1114 { 1115 return val == "true" || val == "TRUE" || val == "t" || val == "T" || val == true; 1116 } 1117 1118 /** 1119 * Pass this function the transaction object and an array of caveParam objects which you are interested it, 1120 * and it will search through the transaction to find the parameters you are intersted in, set their type and 1121 * value. On return from this function you can check teh array of parameters (that you passed to it), 1122 * and extract the values that it found. Any it could not find will have a null value. 1123 * 1124 * <p>See also the <a href="../../web_client_interface_page.html">overview and examples</a>.</p> 1125 * 1126 * @param {caveTransaction} trans the transaction object from the remote cave object 1127 * @param {Array} params an array of CaveParam objects 1128 */ 1129 function findParametersInTransaction(trans, params) 1130 { 1131 for (var m = 0; m < params.length; m++) { 1132 var p = params[m]; 1133 p.value = null; 1134 } 1135 var found = false; 1136 for (var i = 0; i < trans.sqls.length; i++) { 1137 var sql = trans.sqls[i]; 1138 for (var j = 0; j < sql.length; j++) { 1139 var row = sql[j]; 1140 for (var k = 0; k < row.length; k++) { 1141 var col = row[k]; 1142 var done = true; 1143 for (var m = 0; m < params.length; m++) { 1144 var p = params[m]; 1145 if (col.name == p.name) { 1146 p.value = col.value; 1147 p.type = col.type; 1148 } 1149 if (p.value == null) { 1150 done = false; 1151 } 1152 } 1153 if (done) { 1154 found = true; 1155 break; 1156 } 1157 } 1158 if (found) { 1159 break; 1160 } 1161 } 1162 if (found) { 1163 break; 1164 } 1165 } 1166 } 1167