1package Jemplate::Runtime; 2use strict; 3use warnings; 4 5sub main { return &kernel } 6sub kernel { 7 <<'...'; 8/*------------------------------------------------------------------------------ 9Jemplate - Template Toolkit for JavaScript 10 11DESCRIPTION - This module provides the runtime JavaScript support for 12compiled Jemplate templates. 13 14AUTHOR - Ingy döt Net <ingy@cpan.org> 15 16Copyright 2006-2014 Ingy döt Net. 17 18This module is free software; you can redistribute it and/or 19modify it under the same terms as Perl itself. 20------------------------------------------------------------------------------*/ 21 22//------------------------------------------------------------------------------ 23// Main Jemplate class 24//------------------------------------------------------------------------------ 25 26if (typeof Jemplate == 'undefined') { 27 var Jemplate = function() { 28 this.init.apply(this, arguments); 29 }; 30} 31 32Jemplate.VERSION = '0.22'; 33 34Jemplate.process = function() { 35 var jemplate = new Jemplate(Jemplate.prototype.config); 36 return jemplate.process.apply(jemplate, arguments); 37} 38 39;(function(){ 40 41if (! Jemplate.templateMap) 42 Jemplate.templateMap = {}; 43 44var proto = Jemplate.prototype = {}; 45 46proto.config = { 47 AUTO_RESET: true, 48 BLOCKS: {}, 49 CONTEXT: null, 50 DEBUG_UNDEF: false, 51 DEFAULT: null, 52 ERROR: null, 53 EVAL_JAVASCRIPT: false, 54 GLOBAL : true, 55 SCOPE : this, 56 FILTERS: {}, 57 INCLUDE_PATH: [''], 58 INTERPOLATE: false, 59 OUTPUT: null, 60 PLUGINS: {}, 61 POST_PROCESS: [], 62 PRE_PROCESS: [], 63 PROCESS: null, 64 RECURSION: false, 65 STASH: null, 66 TOLERANT: null, 67 VARIABLES: {}, 68 WRAPPER: [] 69}; 70 71proto.defaults = { 72 AUTO_RESET: true, 73 BLOCKS: {}, 74 CONTEXT: null, 75 DEBUG_UNDEF: false, 76 DEFAULT: null, 77 ERROR: null, 78 EVAL_JAVASCRIPT: false, 79 GLOBAL : true, 80 SCOPE : this, 81 INCLUDE_PATH: [''], 82 INTERPOLATE: false, 83 OUTPUT: null, 84 PLUGINS: {}, 85 POST_PROCESS: [], 86 PRE_PROCESS: [], 87 PROCESS: null, 88 RECURSION: false, 89 STASH: null, 90 TOLERANT: null, 91 VARIABLES: {}, 92 WRAPPER: [] 93}; 94 95 96Jemplate.init = function(config) { 97 98 Jemplate.prototype.config = config || {}; 99 100 for (var i in Jemplate.prototype.defaults) { 101 if(typeof Jemplate.prototype.config[i] == "undefined") { 102 Jemplate.prototype.config[i] = Jemplate.prototype.defaults[i]; 103 } 104 } 105} 106 107proto.init = function(config) { 108 109 this.config = config || {}; 110 111 for (var i in Jemplate.prototype.defaults) { 112 if(typeof this.config[i] == "undefined") { 113 this.config[i] = Jemplate.prototype.defaults[i]; 114 } 115 } 116} 117 118proto.process = function(template, data, output) { 119 var context = this.config.CONTEXT || new Jemplate.Context(); 120 context.config = this.config; 121 122 context.stash = new Jemplate.Stash(this.config.STASH, this.config); 123 124 context.__filter__ = new Jemplate.Filter(); 125 context.__filter__.config = this.config; 126 127 context.__plugin__ = new Jemplate.Plugin(); 128 context.__plugin__.config = this.config; 129 130 var result; 131 132 var proc = function(input) { 133 try { 134 if (typeof context.config.PRE_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.PRE_PROCESS]; 135 for (var i = 0; i < context.config.PRE_PROCESS.length; i++) { 136 context.process(context.config.PRE_PROCESS[i]); 137 } 138 139 result = context.process(template, input); 140 141 if (typeof context.config.POST_PROCESS == 'string') context.config.PRE_PROCESS = [context.config.POST_PROCESS]; 142 for (i = 0; i < context.config.POST_PROCESS.length; i++) { 143 context.process(context.config.POST_PROCESS[i]); 144 } 145 } 146 catch(e) { 147 if (! String(e).match(/Jemplate\.STOP\n/)) 148 throw(e); 149 result = e.toString().replace(/Jemplate\.STOP\n/, ''); 150 } 151 152 if (typeof output == 'undefined') 153 return result; 154 if (typeof output == 'function') { 155 output(result); 156 return null; 157 } 158 if (typeof(output) == 'string' || output instanceof String) { 159 if (output.match(/^#[\w\-]+$/)) { 160 var id = output.replace(/^#/, ''); 161 var element = document.getElementById(id); 162 if (typeof element == 'undefined') 163 throw('No element found with id="' + id + '"'); 164 element.innerHTML = result; 165 return null; 166 } 167 } 168 else { 169 output.innerHTML = result; 170 return null; 171 } 172 173 throw("Invalid arguments in call to Jemplate.process"); 174 175 return 1; 176 } 177 178 if (typeof data == 'function') 179 data = data(); 180 else if (typeof data == 'string') { 181// Jemplate.Ajax.get(data, function(r) { proc(Jemplate.JSON.parse(r)) }); 182 var url = data; 183 Jemplate.Ajax.processGet(url, function(data) { proc(data) }); 184 return null; 185 } 186 187 return proc(data); 188} 189 190//------------------------------------------------------------------------------ 191// Jemplate.Context class 192//------------------------------------------------------------------------------ 193if (typeof Jemplate.Context == 'undefined') 194 Jemplate.Context = function() {}; 195 196proto = Jemplate.Context.prototype; 197 198proto.include = function(template, args) { 199 return this.process(template, args, true); 200} 201 202proto.process = function(template, args, localise) { 203 if (localise) 204 this.stash.clone(args); 205 else 206 this.stash.update(args); 207 var func = Jemplate.templateMap[template]; 208 if (typeof func == 'undefined') 209 throw('No Jemplate template named "' + template + '" available'); 210 var output = func(this); 211 if (localise) 212 this.stash.declone(); 213 return output; 214} 215 216proto.set_error = function(error, output) { 217 this._error = [error, output]; 218 return error; 219} 220 221proto.plugin = function(name, args) { 222 if (typeof name == 'undefined') 223 throw "Unknown plugin name ':" + name + "'"; 224 225 // The Context object (this) is passed as the first argument to the plugin. 226 var func = eval(name); 227 return new func(this, args); 228} 229 230proto.filter = function(text, name, args) { 231 if (name == 'null') 232 name = "null_filter"; 233 if (typeof this.__filter__.filters[name] == "function") 234 return this.__filter__.filters[name](text, args, this); 235 else 236 throw "Unknown filter name ':" + name + "'"; 237} 238 239//------------------------------------------------------------------------------ 240// Jemplate.Plugin class 241//------------------------------------------------------------------------------ 242if (typeof Jemplate.Plugin == 'undefined') { 243 Jemplate.Plugin = function() { }; 244} 245 246proto = Jemplate.Plugin.prototype; 247 248proto.plugins = {}; 249 250//------------------------------------------------------------------------------ 251// Jemplate.Filter class 252//------------------------------------------------------------------------------ 253if (typeof Jemplate.Filter == 'undefined') { 254 Jemplate.Filter = function() { }; 255} 256 257proto = Jemplate.Filter.prototype; 258 259proto.filters = {}; 260 261proto.filters.null_filter = function(text) { 262 return ''; 263} 264 265proto.filters.upper = function(text) { 266 return text.toUpperCase(); 267} 268 269proto.filters.lower = function(text) { 270 return text.toLowerCase(); 271} 272 273proto.filters.ucfirst = function(text) { 274 var first = text.charAt(0); 275 var rest = text.substr(1); 276 return first.toUpperCase() + rest; 277} 278 279proto.filters.lcfirst = function(text) { 280 var first = text.charAt(0); 281 var rest = text.substr(1); 282 return first.toLowerCase() + rest; 283} 284 285proto.filters.trim = function(text) { 286 return text.replace( /^\s+/g, "" ).replace( /\s+$/g, "" ); 287} 288 289proto.filters.collapse = function(text) { 290 return text.replace( /^\s+/g, "" ).replace( /\s+$/g, "" ).replace(/\s+/, " "); 291} 292 293proto.filters.html = function(text) { 294 text = text.replace(/&/g, '&'); 295 text = text.replace(/</g, '<'); 296 text = text.replace(/>/g, '>'); 297 text = text.replace(/"/g, '"'); // " end quote for emacs 298 return text; 299} 300 301proto.filters.html_para = function(text) { 302 var lines = text.split(/(?:\r?\n){2,}/); 303 return "<p>\n" + lines.join("\n</p>\n\n<p>\n") + "</p>\n"; 304} 305 306proto.filters.html_break = function(text) { 307 return text.replace(/(\r?\n){2,}/g, "$1<br />$1<br />$1"); 308} 309 310proto.filters.html_line_break = function(text) { 311 return text.replace(/(\r?\n)/g, "$1<br />$1"); 312} 313 314proto.filters.uri = function(text) { 315 return encodeURIComponent(text); 316} 317 318proto.filters.url = function(text) { 319 return encodeURI(text); 320} 321 322proto.filters.indent = function(text, args) { 323 var pad = args[0]; 324 if (! text) return null; 325 if (typeof pad == 'undefined') 326 pad = 4; 327 328 var finalpad = ''; 329 if (typeof pad == 'number' || String(pad).match(/^\d$/)) { 330 for (var i = 0; i < pad; i++) { 331 finalpad += ' '; 332 } 333 } else { 334 finalpad = pad; 335 } 336 var output = text.replace(/^/gm, finalpad); 337 return output; 338} 339 340proto.filters.truncate = function(text, args) { 341 var len = args[0]; 342 if (! text) return null; 343 if (! len) 344 len = 32; 345 // This should probably be <=, but TT just uses < 346 if (text.length < len) 347 return text; 348 var newlen = len - 3; 349 return text.substr(0,newlen) + '...'; 350} 351 352proto.filters.repeat = function(text, iter) { 353 if (! text) return null; 354 if (! iter || iter == 0) 355 iter = 1; 356 if (iter == 1) return text 357 358 var output = text; 359 for (var i = 1; i < iter; i++) { 360 output += text; 361 } 362 return output; 363} 364 365proto.filters.replace = function(text, args) { 366 if (! text) return null; 367 var re_search = args[0]; 368 var text_replace = args[1]; 369 if (! re_search) 370 re_search = ''; 371 if (! text_replace) 372 text_replace = ''; 373 var re = new RegExp(re_search, 'g'); 374 return text.replace(re, text_replace); 375} 376 377//------------------------------------------------------------------------------ 378// Jemplate.Stash class 379//------------------------------------------------------------------------------ 380if (typeof Jemplate.Stash == 'undefined') { 381 Jemplate.Stash = function(stash, config) { 382 this.__config__ = config; 383 384 this.data = { 385 GLOBAL : this.__config__.SCOPE 386 }; 387 this.LOCAL_ANCHOR = {}; 388 this.data.LOCAL = this.LOCAL_ANCHOR; 389 390 this.update(stash); 391 }; 392} 393 394proto = Jemplate.Stash.prototype; 395 396proto.clone = function(args) { 397 var data = this.data; 398 this.data = { 399 GLOBAL : this.__config__.SCOPE 400 }; 401 this.data.LOCAL = this.LOCAL_ANCHOR; 402 this.update(data); 403 this.update(args); 404 this.data._PARENT = data; 405} 406 407proto.declone = function(args) { 408 this.data = this.data._PARENT || this.data; 409} 410 411proto.update = function(args) { 412 if (typeof args == 'undefined') return; 413 for (var key in args) { 414 if (key != 'GLOBAL' && key != 'LOCAL') { 415 this.set(key, args[key]); 416 } 417 } 418} 419 420proto.get = function(ident, args) { 421 var root = this.data; 422 423 var value; 424 425 if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) { 426 427 if (typeof ident == 'string') { 428 ident = ident.split('.'); 429 var newIdent = []; 430 for (var i = 0; i < ident.length; i++) { 431 newIdent.push(ident.replace(/\(.*$/,'')); 432 newIdent.push(0); 433 } 434 ident = newIdent; 435 } 436 437 for (var i = 0; i < ident.length; i += 2) { 438 var dotopArgs = ident.slice(i, i+2); 439 dotopArgs.unshift(root); 440 value = this._dotop.apply(this, dotopArgs); 441 if (typeof value == 'undefined') 442 break; 443 root = value; 444 } 445 } 446 else { 447 value = this._dotop(root, ident, args); 448 } 449 450 if (typeof value == 'undefined' || value == null) { 451 if (this.__config__.DEBUG_UNDEF) 452 throw("undefined value found while using DEBUG_UNDEF"); 453 value = ''; 454 } 455 456 return value; 457} 458 459 460 461proto.set = function(ident, value, set_default) { 462 463 var root, result, error; 464 465 root = this.data; 466 467 while (true) { 468 if ( (ident instanceof Array) || (typeof ident == 'string' && /\./.test(ident) ) ) { 469 470 if (typeof ident == 'string') { 471 ident = ident.split('.'); 472 var newIdent = []; 473 for (var i = 0; i < ident.length; i++) { 474 newIdent.push(ident.replace(/\(.*$/,'')); 475 newIdent.push(0); 476 } 477 ident = newIdent; 478 } 479 480 for (var i = 0; i < ident.length - 2; i += 2) { 481 var dotopArgs = ident.slice(i, i+2); 482 dotopArgs.unshift(root); 483 dotopArgs.push(1); 484 result = this._dotop.apply(this, dotopArgs); 485 if (typeof value == 'undefined') 486 break; 487 root = result; 488 } 489 490 var assignArgs = ident.slice(ident.length-2, ident.length); 491 assignArgs.unshift(root); 492 assignArgs.push(value); 493 assignArgs.push(set_default); 494 495 496 result = this._assign.apply(this, assignArgs); 497 } else { 498 result = this._assign(root, ident, 0, value, set_default); 499 } 500 break; 501 } 502 503 return (typeof result != 'undefined') ? result : ''; 504} 505 506 507 508proto._dotop = function(root, item, args, lvalue) { 509 if (root == this.LOCAL_ANCHOR) root = this.data; 510 var atroot = root == this.data; 511 512 var value,result = undefined; 513 514 var is_function_call = args instanceof Array; 515 516 args = args || []; 517 518 if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) { 519 return undefined; 520 } 521 522 523 //root is complex object, not scalar 524 if (atroot || (root instanceof Object && !(root instanceof Array)) || root == this.data.GLOBAL) { 525 526 if (typeof root[item] != 'undefined' && root[item] != null && (!is_function_call || !this.hash_functions[item])) { //consider undefined == null 527 if (typeof root[item] == 'function') { 528 result = root[item].apply(root,args); 529 } else { 530 return root[item]; 531 } 532 } else if (lvalue) { 533 return root[item] = {}; 534 } else if (this.hash_functions[item] && !atroot || item == 'import') { 535 args.unshift(root); 536 result = this.hash_functions[item].apply(this,args); 537 } else if (item instanceof Array) { 538 result = {}; 539 540 for (var i = 0; i < item.length; i++) result[item[i]] = root[item[i]]; 541 return result; 542 } 543 } else if (root instanceof Array) { 544 if (this.list_functions[item]) { 545 args.unshift(root); 546 result = this.list_functions[item].apply(this,args); 547 } else if (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' ) { 548 if (typeof root[item] != 'function') return root[item]; 549 result = root[item].apply(this, args); 550 } else if (item instanceof Array) { 551 for (var i = 0; i < item.length; i++) result.push(root[item[i]]); 552 return result; 553 } 554 } else if (this.string_functions[item] && !lvalue) { 555 args.unshift(root); 556 result = this.string_functions[item].apply(this, args); 557 } else if (this.list_functions[item] && !lvalue) { 558 args.unshift([root]); 559 result = this.list_functions[item].apply(this,args); 560 } else { 561 result = undefined; 562 } 563 564 565 if (result instanceof Array) { 566 if (typeof result[0] == 'undefined' && typeof result[1] != 'undefined') { 567 throw result[1]; 568 } 569 } 570 571 return result; 572 573} 574 575 576proto._assign = function(root, item, args, value, set_default) { 577 var atroot = root == this.data; 578 var result; 579 580 args = args || []; 581 582 if (typeof root == 'undefined' || typeof item == 'undefined' || typeof item == 'string' && item.match(/^[\._]/)) { 583 return undefined; 584 } 585 586 if (atroot || root.constructor == Object || root == this.data.GLOBAL) { 587 588 if (root == this.LOCAL_ANCHOR) root = this.data; 589 590 if (!(set_default && typeof root[item] != 'undefined')) { 591 if (atroot && item == 'GLOBAL') throw "Attempt to modify GLOBAL access modifier" 592 if (atroot && item == 'LOCAL') throw "Attempt to modify LOCAL access modifier" 593 594 return root[item] = value; 595 } 596 } else if ((root instanceof Array) && (typeof item == 'string' && /^-?\d+$/.test(item) || typeof item == 'number' )) { 597 if (!(set_default && typeof root[item] != 'undefined')) { 598 return root[item] = value; 599 } 600 } else if ( (root.constructor != Object) && (root instanceof Object) ) { 601 try { 602 result = root[item].apply(root,args); 603 } catch (e) { 604 } 605 } else { 606 throw 'dont know how to assign to [' + root + '.' + item +']'; 607 } 608 609 return undefined; 610} 611 612 613proto.string_functions = {}; 614 615// typeof 616proto.string_functions['typeof'] = function(value) { 617 return typeof value; 618} 619 620// chunk(size) negative size chunks from end 621proto.string_functions.chunk = function(string, size) { 622 //var size = args; 623 var list = new Array(); 624 if (! size) 625 size = 1; 626 if (size < 0) { 627 size = 0 - size; 628 for (var i = string.length - size; i >= 0; i = i - size) 629 list.unshift(string.substr(i, size)); 630 if (string.length % size) 631 list.unshift(string.substr(0, string.length % size)); 632 } 633 else 634 for (i = 0; i < string.length; i = i + size) 635 list.push(string.substr(i, size)); 636 return list; 637} 638 639// defined is value defined? 640proto.string_functions.defined = function(string) { 641 return 1; 642} 643 644// hash treat as single-element hash with key value 645proto.string_functions.hash = function(string) { 646 return { 'value': string }; 647} 648 649// length length of string representation 650proto.string_functions.length = function(string) { 651 return string.length; 652} 653 654// list treat as single-item list 655proto.string_functions.list = function(string) { 656 return [ string ]; 657} 658 659// match(re) get list of matches 660proto.string_functions.match = function(string, re, modifiers) { 661 var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers); 662 var list = string.match(regexp); 663 return list; 664} 665 666// repeat(n) repeated n times 667proto.string_functions.repeat = function(string, args) { 668 var n = args || 1; 669 var output = ''; 670 for (var i = 0; i < n; i++) { 671 output += string; 672 } 673 return output; 674} 675 676// replace(re, sub, global) replace instances of re with sub 677proto.string_functions.replace = function(string, re, sub, modifiers) { 678 var regexp = new RegExp(re, modifiers == undefined ? 'g' : modifiers); 679 if (! sub) sub = ''; 680 681 return string.replace(regexp, sub); 682} 683 684// search(re) true if value matches re 685proto.string_functions.search = function(string, re) { 686 var regexp = new RegExp(re); 687 return (string.search(regexp) >= 0) ? 1 : 0; 688} 689 690// size returns 1, as if a single-item list 691proto.string_functions.size = function(string) { 692 return 1; 693} 694 695// split(re) split string on re 696proto.string_functions.split = function(string, re) { 697 var regexp = new RegExp(re); 698 var list = string.split(regexp); 699 return list; 700} 701 702 703 704proto.list_functions = {}; 705 706// typeof 707proto.list_functions['typeof'] = function(list) { 708 return 'array'; 709}; 710 711 712proto.list_functions.list = function(list) { 713 return list; 714}; 715 716proto.list_functions.join = function(list, str) { 717 return list.join(str); 718}; 719 720proto.list_functions.sort = function(list,key) { 721 if( typeof(key) != 'undefined' && key != "" ) { 722 // we probably have a list of hashes 723 // and need to sort based on hash key 724 return list.sort( 725 function(a,b) { 726 if( a[key] == b[key] ) { 727 return 0; 728 } 729 else if( a[key] > b[key] ) { 730 return 1; 731 } 732 else { 733 return -1; 734 } 735 } 736 ); 737 } 738 return list.sort(); 739} 740 741proto.list_functions.nsort = function(list) { 742 return list.sort(function(a, b) { return (a-b) }); 743} 744 745proto.list_functions.grep = function(list, re) { 746 var regexp = new RegExp(re); 747 var result = []; 748 for (var i = 0; i < list.length; i++) { 749 if (list[i].match(regexp)) 750 result.push(list[i]); 751 } 752 return result; 753} 754 755proto.list_functions.unique = function(list) { 756 var result = []; 757 var seen = {}; 758 for (var i = 0; i < list.length; i++) { 759 var elem = list[i]; 760 if (! seen[elem]) 761 result.push(elem); 762 seen[elem] = true; 763 } 764 return result; 765} 766 767proto.list_functions.reverse = function(list) { 768 var result = []; 769 for (var i = list.length - 1; i >= 0; i--) { 770 result.push(list[i]); 771 } 772 return result; 773} 774 775proto.list_functions.merge = function(list /*, ... args */) { 776 var result = []; 777 var push_all = function(elem) { 778 if (elem instanceof Array) { 779 for (var j = 0; j < elem.length; j++) { 780 result.push(elem[j]); 781 } 782 } 783 else { 784 result.push(elem); 785 } 786 } 787 push_all(list); 788 for (var i = 1; i < arguments.length; i++) { 789 push_all(arguments[i]); 790 } 791 return result; 792} 793 794proto.list_functions.slice = function(list, start, end) { 795 // To make it like slice in TT 796 // See rt53453 797 if ( end == -1 ) { 798 return list.slice( start ); 799 } 800 return list.slice( start, end + 1 ); 801} 802 803proto.list_functions.splice = function(list /*, ... args */ ) { 804 var args = Array.prototype.slice.call(arguments); 805 args.shift(); 806 807 return list.splice.apply(list,args); 808} 809 810proto.list_functions.push = function(list, value) { 811 list.push(value); 812 return list; 813} 814 815proto.list_functions.pop = function(list) { 816 return list.pop(); 817} 818 819proto.list_functions.unshift = function(list, value) { 820 list.unshift(value); 821 return list; 822} 823 824proto.list_functions.shift = function(list) { 825 return list.shift(); 826} 827 828proto.list_functions.first = function(list) { 829 return list[0]; 830} 831 832proto.list_functions.size = function(list) { 833 return list.length; 834} 835 836proto.list_functions.max = function(list) { 837 return list.length - 1; 838} 839 840proto.list_functions.last = function(list) { 841 return list.slice(-1); 842} 843 844proto.hash_functions = {}; 845 846// typeof 847proto.hash_functions['typeof'] = function(hash) { 848 return 'object'; 849}; 850 851 852// each list of alternating keys/values 853proto.hash_functions.each = function(hash) { 854 var list = new Array(); 855 for ( var key in hash ) 856 list.push(key, hash[key]); 857 return list; 858} 859 860// exists(key) does key exist? 861proto.hash_functions.exists = function(hash, key) { 862 return ( typeof( hash[key] ) == "undefined" ) ? 0 : 1; 863} 864 865// import(hash2) import contents of hash2 866// import import into current namespace hash 867proto.hash_functions['import'] = function(hash, hash2) { 868 for ( var key in hash2 ) 869 hash[key] = hash2[key]; 870 return ''; 871} 872 873// keys list of keys 874proto.hash_functions.keys = function(hash) { 875 var list = new Array(); 876 for ( var key in hash ) 877 list.push(key); 878 return list; 879} 880 881// list returns alternating key, value 882proto.hash_functions.list = function(hash, what) { 883 //var what = ''; 884 //if ( args ) 885 //what = args[0]; 886 887 var list = new Array(); 888 var key; 889 if (what == 'keys') 890 for ( key in hash ) 891 list.push(key); 892 else if (what == 'values') 893 for ( key in hash ) 894 list.push(hash[key]); 895 else if (what == 'each') 896 for ( key in hash ) 897 list.push(key, hash[key]); 898 else 899 for ( key in hash ) 900 list.push({ 'key': key, 'value': hash[key] }); 901 902 return list; 903} 904 905// nsort keys sorted numerically 906proto.hash_functions.nsort = function(hash) { 907 var list = new Array(); 908 for (var key in hash) 909 list.push(key); 910 return list.sort(function(a, b) { return (a-b) }); 911} 912 913// item return a value by key 914proto.hash_functions.item = function(hash, key) { 915 return hash[key]; 916} 917 918// size number of pairs 919proto.hash_functions.size = function(hash) { 920 var size = 0; 921 for (var key in hash) 922 size++; 923 return size; 924} 925 926 927// sort keys sorted alphabetically 928proto.hash_functions.sort = function(hash) { 929 var list = new Array(); 930 for (var key in hash) 931 list.push(key); 932 return list.sort(); 933} 934 935// values list of values 936proto.hash_functions.values = function(hash) { 937 var list = new Array(); 938 for ( var key in hash ) 939 list.push(hash[key]); 940 return list; 941} 942 943proto.hash_functions.pairs = function(hash) { 944 var list = new Array(); 945 var keys = new Array(); 946 for ( var key in hash ) { 947 keys.push( key ); 948 } 949 keys.sort(); 950 for ( var key in keys ) { 951 key = keys[key] 952 list.push( { 'key': key, 'value': hash[key] } ); 953 } 954 return list; 955} 956 957// delete 958proto.hash_functions.remove = function(hash, key) { 959 return delete hash[key]; 960} 961proto.hash_functions['delete'] = proto.hash_functions.remove; 962 963//------------------------------------------------------------------------------ 964// Jemplate.Iterator class 965//------------------------------------------------------------------------------ 966if (typeof Jemplate.Iterator == 'undefined') { 967 Jemplate.Iterator = function(object) { 968 if( object instanceof Array ) { 969 this.object = object; 970 this.size = object.length; 971 this.max = this.size -1; 972 } 973 else if ( object instanceof Object ) { 974 this.object = object; 975 var object_keys = new Array; 976 for( var key in object ) { 977 object_keys[object_keys.length] = key; 978 } 979 this.object_keys = object_keys.sort(); 980 this.size = object_keys.length; 981 this.max = this.size -1; 982 } else if (typeof object == 'undefined' || object == null || object == '') { 983 this.object = null; 984 this.max = -1; 985 } 986 } 987} 988 989proto = Jemplate.Iterator.prototype; 990 991proto.get_first = function() { 992 this.index = 0; 993 this.first = 1; 994 this.last = 0; 995 this.count = 1; 996 return this.get_next(1); 997} 998 999proto.get_next = function(should_init) { 1000 var object = this.object; 1001 var index; 1002 if( typeof(should_init) != 'undefined' && should_init ) { 1003 index = this.index; 1004 } else { 1005 index = ++this.index; 1006 this.first = 0; 1007 this.count = this.index + 1; 1008 if( this.index == this.size -1 ) { 1009 this.last = 1; 1010 } 1011 } 1012 if (typeof object == 'undefined') 1013 throw('No object to iterate'); 1014 if( this.object_keys ) { 1015 if (index < this.object_keys.length) { 1016 this.prev = index > 0 ? this.object_keys[index - 1] : ""; 1017 this.next = index < this.max ? this.object_keys[index + 1] : ""; 1018 return [this.object_keys[index], false]; 1019 } 1020 } else { 1021 if (index <= this.max) { 1022 this.prev = index > 0 ? object[index - 1] : ""; 1023 this.next = index < this.max ? object[index +1] : ""; 1024 return [object[index], false]; 1025 } 1026 } 1027 return [null, true]; 1028} 1029 1030var stubExplanation = "stub that doesn't do anything. Try including the jQuery, YUI, or XHR option when building the runtime"; 1031 1032Jemplate.Ajax = { 1033 1034 get: function(url, callback) { 1035 throw("This is a Jemplate.Ajax.get " + stubExplanation); 1036 }, 1037 1038 processGet: function(url, callback) { 1039 throw("This is a Jemplate.Ajax.processGet " + stubExplanation); 1040 }, 1041 1042 post: function(url, callback) { 1043 throw("This is a Jemplate.Ajax.post " + stubExplanation); 1044 } 1045 1046}; 1047 1048Jemplate.JSON = { 1049 1050 parse: function(decodeValue) { 1051 throw("This is a Jemplate.JSON.parse " + stubExplanation); 1052 }, 1053 1054 stringify: function(encodeValue) { 1055 throw("This is a Jemplate.JSON.stringify " + stubExplanation); 1056 } 1057 1058}; 1059 1060}()); 1061 1062... 1063} 1064 1065sub ajax_jquery { 1066 <<'...'; 1067;(function(){ 1068 1069Jemplate.Ajax = { 1070 1071 get: function(url, callback) { 1072 jQuery.get(url, null, callback); 1073 }, 1074 1075 processGet: function(url, processor) { 1076 jQuery.getJSON(url, null, processor); 1077 }, 1078 1079 post: function(url, data, callback) { 1080 jQuery.post(url, data, callback); 1081 } 1082 1083}; 1084 1085}()); 1086 1087 1088... 1089} 1090 1091sub ajax_xhr { 1092 <<'...'; 1093;(function(){ 1094 1095Jemplate.Ajax = { 1096 1097 get: function(url, callback) { 1098 var request = new XMLHttpRequest(); 1099 request.open('GET', url, Boolean(callback)); 1100 request.setRequestHeader('Accept', 'text/json; text/x-json; application/json'); 1101 return this.request(request, null, callback); 1102 }, 1103 1104 processGet: function(url, processor) { 1105 this.get(url, function(responseText){ 1106 processor(Jemplate.JSON.parse(responseText)); 1107 }); 1108 }, 1109 1110 post: function(url, data, callback) { 1111 var request = new XMLHttpRequest(); 1112 request.open('POST', url, Boolean(callback)); 1113 request.setRequestHeader('Accept', 'text/json; text/x-json; application/json'); 1114 request.setRequestHeader( 1115 'Content-Type', 'application/x-www-form-urlencoded' 1116 ); 1117 return this.request(request, data, callback); 1118 }, 1119 1120 request: function(request, data, callback) { 1121 if (callback) { 1122 request.onreadystatechange = function() { 1123 if (request.readyState == 4) { 1124 if(request.status == 200) 1125 callback(request.responseText); 1126 } 1127 }; 1128 } 1129 request.send(data); 1130 if (!callback) { 1131 if (request.status != 200) 1132 throw('Request for "' + url + 1133 '" failed with status: ' + request.status); 1134 return request.responseText; 1135 } 1136 return null; 1137 } 1138}; 1139 1140}()); 1141 1142... 1143} 1144 1145sub ajax_yui { 1146 <<'...'; 1147;(function(){ 1148 1149Jemplate.Ajax = { 1150 1151 get: function(url, callback) { 1152 if (typeof callback == "function") { 1153 callback = { success: callback }; 1154 } 1155 YAHOO.connect.asyncRequest("GET", url, callback); 1156 }, 1157 1158 processGet: function(url, processor) { 1159 this.get(url, function(responseText){ 1160 processor(YAHOO.lang.JSON.parse(responseText)); 1161 }); 1162 }, 1163 1164 post: function(url, data, callback) { 1165 if (typeof callback == "function") { 1166 callback = { success: callback }; 1167 } 1168 YAHOO.connect.asyncRequest("POST", url, callback, data); 1169 } 1170 1171}; 1172 1173}()); 1174 1175... 1176} 1177 1178sub json_json2 { 1179 <<'...'; 1180;(function(){ 1181 1182Jemplate.JSON = { 1183 1184 parse: function(encoded) { 1185 return JSON.parse(encoded); 1186 }, 1187 1188 stringify: function(decoded) { 1189 return JSON.stringify(decoded); 1190 } 1191 1192}; 1193 1194}()); 1195 1196... 1197} 1198 1199sub json_json2_internal { 1200 <<'...'; 1201;(function(){ 1202 1203var JSON; 1204 1205/* 1206 http://www.JSON.org/json2.js 1207 2009-04-16 1208 1209 Public Domain. 1210 1211 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 1212 1213 See http://www.JSON.org/js.html 1214 1215 This file creates a global JSON object containing two methods: stringify 1216 and parse. 1217 1218 JSON.stringify(value, replacer, space) 1219 value any JavaScript value, usually an object or array. 1220 1221 replacer an optional parameter that determines how object 1222 values are stringified for objects. It can be a 1223 function or an array of strings. 1224 1225 space an optional parameter that specifies the indentation 1226 of nested structures. If it is omitted, the text will 1227 be packed without extra whitespace. If it is a number, 1228 it will specify the number of spaces to indent at each 1229 level. If it is a string (such as '\t' or ' '), 1230 it contains the characters used to indent at each level. 1231 1232 This method produces a JSON text from a JavaScript value. 1233 1234 When an object value is found, if the object contains a toJSON 1235 method, its toJSON method will be called and the result will be 1236 stringified. A toJSON method does not serialize: it returns the 1237 value represented by the name/value pair that should be serialized, 1238 or undefined if nothing should be serialized. The toJSON method 1239 will be passed the key associated with the value, and this will be 1240 bound to the object holding the key. 1241 1242 For example, this would serialize Dates as ISO strings. 1243 1244 Date.prototype.toJSON = function (key) { 1245 function f(n) { 1246 // Format integers to have at least two digits. 1247 return n < 10 ? '0' + n : n; 1248 } 1249 1250 return this.getUTCFullYear() + '-' + 1251 f(this.getUTCMonth() + 1) + '-' + 1252 f(this.getUTCDate()) + 'T' + 1253 f(this.getUTCHours()) + ':' + 1254 f(this.getUTCMinutes()) + ':' + 1255 f(this.getUTCSeconds()) + 'Z'; 1256 }; 1257 1258 You can provide an optional replacer method. It will be passed the 1259 key and value of each member, with this bound to the containing 1260 object. The value that is returned from your method will be 1261 serialized. If your method returns undefined, then the member will 1262 be excluded from the serialization. 1263 1264 If the replacer parameter is an array of strings, then it will be 1265 used to select the members to be serialized. It filters the results 1266 such that only members with keys listed in the replacer array are 1267 stringified. 1268 1269 Values that do not have JSON representations, such as undefined or 1270 functions, will not be serialized. Such values in objects will be 1271 dropped; in arrays they will be replaced with null. You can use 1272 a replacer function to replace those with JSON values. 1273 JSON.stringify(undefined) returns undefined. 1274 1275 The optional space parameter produces a stringification of the 1276 value that is filled with line breaks and indentation to make it 1277 easier to read. 1278 1279 If the space parameter is a non-empty string, then that string will 1280 be used for indentation. If the space parameter is a number, then 1281 the indentation will be that many spaces. 1282 1283 Example: 1284 1285 text = JSON.stringify(['e', {pluribus: 'unum'}]); 1286 // text is '["e",{"pluribus":"unum"}]' 1287 1288 1289 text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 1290 // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 1291 1292 text = JSON.stringify([new Date()], function (key, value) { 1293 return this[key] instanceof Date ? 1294 'Date(' + this[key] + ')' : value; 1295 }); 1296 // text is '["Date(---current time---)"]' 1297 1298 1299 JSON.parse(text, reviver) 1300 This method parses a JSON text to produce an object or array. 1301 It can throw a SyntaxError exception. 1302 1303 The optional reviver parameter is a function that can filter and 1304 transform the results. It receives each of the keys and values, 1305 and its return value is used instead of the original value. 1306 If it returns what it received, then the structure is not modified. 1307 If it returns undefined then the member is deleted. 1308 1309 Example: 1310 1311 // Parse the text. Values that look like ISO date strings will 1312 // be converted to Date objects. 1313 1314 myData = JSON.parse(text, function (key, value) { 1315 var a; 1316 if (typeof value === 'string') { 1317 a = 1318/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 1319 if (a) { 1320 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 1321 +a[5], +a[6])); 1322 } 1323 } 1324 return value; 1325 }); 1326 1327 myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 1328 var d; 1329 if (typeof value === 'string' && 1330 value.slice(0, 5) === 'Date(' && 1331 value.slice(-1) === ')') { 1332 d = new Date(value.slice(5, -1)); 1333 if (d) { 1334 return d; 1335 } 1336 } 1337 return value; 1338 }); 1339 1340 1341 This is a reference implementation. You are free to copy, modify, or 1342 redistribute. 1343 1344 This code should be minified before deployment. 1345 See http://javascript.crockford.com/jsmin.html 1346 1347 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 1348 NOT CONTROL. 1349*/ 1350 1351/*jslint evil: true */ 1352 1353/*global JSON */ 1354 1355/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 1356 call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 1357 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 1358 lastIndex, length, parse, prototype, push, replace, slice, stringify, 1359 test, toJSON, toString, valueOf 1360*/ 1361 1362// Create a JSON object only if one does not already exist. We create the 1363// methods in a closure to avoid creating global variables. 1364 1365if (!this.JSON) { 1366 this.JSON = {}; 1367} 1368(function () { 1369 1370 function f(n) { 1371 // Format integers to have at least two digits. 1372 return n < 10 ? '0' + n : n; 1373 } 1374 1375 if (typeof Date.prototype.toJSON !== 'function') { 1376 1377 Date.prototype.toJSON = function (key) { 1378 1379 return this.getUTCFullYear() + '-' + 1380 f(this.getUTCMonth() + 1) + '-' + 1381 f(this.getUTCDate()) + 'T' + 1382 f(this.getUTCHours()) + ':' + 1383 f(this.getUTCMinutes()) + ':' + 1384 f(this.getUTCSeconds()) + 'Z'; 1385 }; 1386 1387 String.prototype.toJSON = 1388 Number.prototype.toJSON = 1389 Boolean.prototype.toJSON = function (key) { 1390 return this.valueOf(); 1391 }; 1392 } 1393 1394 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 1395 escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 1396 gap, 1397 indent, 1398 meta = { // table of character substitutions 1399 '\b': '\\b', 1400 '\t': '\\t', 1401 '\n': '\\n', 1402 '\f': '\\f', 1403 '\r': '\\r', 1404 '"' : '\\"', 1405 '\\': '\\\\' 1406 }, 1407 rep; 1408 1409 1410 function quote(string) { 1411 1412// If the string contains no control characters, no quote characters, and no 1413// backslash characters, then we can safely slap some quotes around it. 1414// Otherwise we must also replace the offending characters with safe escape 1415// sequences. 1416 1417 escapable.lastIndex = 0; 1418 return escapable.test(string) ? 1419 '"' + string.replace(escapable, function (a) { 1420 var c = meta[a]; 1421 return typeof c === 'string' ? c : 1422 '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 1423 }) + '"' : 1424 '"' + string + '"'; 1425 } 1426 1427 1428 function str(key, holder) { 1429 1430// Produce a string from holder[key]. 1431 1432 var i, // The loop counter. 1433 k, // The member key. 1434 v, // The member value. 1435 length, 1436 mind = gap, 1437 partial, 1438 value = holder[key]; 1439 1440// If the value has a toJSON method, call it to obtain a replacement value. 1441 1442 if (value && typeof value === 'object' && 1443 typeof value.toJSON === 'function') { 1444 value = value.toJSON(key); 1445 } 1446 1447// If we were called with a replacer function, then call the replacer to 1448// obtain a replacement value. 1449 1450 if (typeof rep === 'function') { 1451 value = rep.call(holder, key, value); 1452 } 1453 1454// What happens next depends on the value's type. 1455 1456 switch (typeof value) { 1457 case 'string': 1458 return quote(value); 1459 1460 case 'number': 1461 1462// JSON numbers must be finite. Encode non-finite numbers as null. 1463 1464 return isFinite(value) ? String(value) : 'null'; 1465 1466 case 'boolean': 1467 case 'null': 1468 1469// If the value is a boolean or null, convert it to a string. Note: 1470// typeof null does not produce 'null'. The case is included here in 1471// the remote chance that this gets fixed someday. 1472 1473 return String(value); 1474 1475// If the type is 'object', we might be dealing with an object or an array or 1476// null. 1477 1478 case 'object': 1479 1480// Due to a specification blunder in ECMAScript, typeof null is 'object', 1481// so watch out for that case. 1482 1483 if (!value) { 1484 return 'null'; 1485 } 1486 1487// Make an array to hold the partial results of stringifying this object value. 1488 1489 gap += indent; 1490 partial = []; 1491 1492// Is the value an array? 1493 1494 if (Object.prototype.toString.apply(value) === '[object Array]') { 1495 1496// The value is an array. Stringify every element. Use null as a placeholder 1497// for non-JSON values. 1498 1499 length = value.length; 1500 for (i = 0; i < length; i += 1) { 1501 partial[i] = str(i, value) || 'null'; 1502 } 1503 1504// Join all of the elements together, separated with commas, and wrap them in 1505// brackets. 1506 1507 v = partial.length === 0 ? '[]' : 1508 gap ? '[\n' + gap + 1509 partial.join(',\n' + gap) + '\n' + 1510 mind + ']' : 1511 '[' + partial.join(',') + ']'; 1512 gap = mind; 1513 return v; 1514 } 1515 1516// If the replacer is an array, use it to select the members to be stringified. 1517 1518 if (rep && typeof rep === 'object') { 1519 length = rep.length; 1520 for (i = 0; i < length; i += 1) { 1521 k = rep[i]; 1522 if (typeof k === 'string') { 1523 v = str(k, value); 1524 if (v) { 1525 partial.push(quote(k) + (gap ? ': ' : ':') + v); 1526 } 1527 } 1528 } 1529 } else { 1530 1531// Otherwise, iterate through all of the keys in the object. 1532 1533 for (k in value) { 1534 if (Object.hasOwnProperty.call(value, k)) { 1535 v = str(k, value); 1536 if (v) { 1537 partial.push(quote(k) + (gap ? ': ' : ':') + v); 1538 } 1539 } 1540 } 1541 } 1542 1543// Join all of the member texts together, separated with commas, 1544// and wrap them in braces. 1545 1546 v = partial.length === 0 ? '{}' : 1547 gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + 1548 mind + '}' : '{' + partial.join(',') + '}'; 1549 gap = mind; 1550 return v; 1551 } 1552 1553 return ''; 1554 } 1555 1556// If the JSON object does not yet have a stringify method, give it one. 1557 1558 if (typeof JSON.stringify !== 'function') { 1559 JSON.stringify = function (value, replacer, space) { 1560 1561// The stringify method takes a value and an optional replacer, and an optional 1562// space parameter, and returns a JSON text. The replacer can be a function 1563// that can replace values, or an array of strings that will select the keys. 1564// A default replacer method can be provided. Use of the space parameter can 1565// produce text that is more easily readable. 1566 1567 var i; 1568 gap = ''; 1569 indent = ''; 1570 1571// If the space parameter is a number, make an indent string containing that 1572// many spaces. 1573 1574 if (typeof space === 'number') { 1575 for (i = 0; i < space; i += 1) { 1576 indent += ' '; 1577 } 1578 1579// If the space parameter is a string, it will be used as the indent string. 1580 1581 } else if (typeof space === 'string') { 1582 indent = space; 1583 } 1584 1585// If there is a replacer, it must be a function or an array. 1586// Otherwise, throw an error. 1587 1588 rep = replacer; 1589 if (replacer && typeof replacer !== 'function' && 1590 (typeof replacer !== 'object' || 1591 typeof replacer.length !== 'number')) { 1592 throw new Error('JSON.stringify'); 1593 } 1594 1595// Make a fake root object containing our value under the key of ''. 1596// Return the result of stringifying the value. 1597 1598 return str('', {'': value}); 1599 }; 1600 } 1601 1602 1603// If the JSON object does not yet have a parse method, give it one. 1604 1605 if (typeof JSON.parse !== 'function') { 1606 JSON.parse = function (text, reviver) { 1607 1608// The parse method takes a text and an optional reviver function, and returns 1609// a JavaScript value if the text is a valid JSON text. 1610 1611 var j; 1612 1613 function walk(holder, key) { 1614 1615// The walk method is used to recursively walk the resulting structure so 1616// that modifications can be made. 1617 1618 var k, v, value = holder[key]; 1619 if (value && typeof value === 'object') { 1620 for (k in value) { 1621 if (Object.hasOwnProperty.call(value, k)) { 1622 v = walk(value, k); 1623 if (v !== undefined) { 1624 value[k] = v; 1625 } else { 1626 delete value[k]; 1627 } 1628 } 1629 } 1630 } 1631 return reviver.call(holder, key, value); 1632 } 1633 1634 1635// Parsing happens in four stages. In the first stage, we replace certain 1636// Unicode characters with escape sequences. JavaScript handles many characters 1637// incorrectly, either silently deleting them, or treating them as line endings. 1638 1639 cx.lastIndex = 0; 1640 if (cx.test(text)) { 1641 text = text.replace(cx, function (a) { 1642 return '\\u' + 1643 ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 1644 }); 1645 } 1646 1647// In the second stage, we run the text against regular expressions that look 1648// for non-JSON patterns. We are especially concerned with '()' and 'new' 1649// because they can cause invocation, and '=' because it can cause mutation. 1650// But just to be safe, we want to reject all unexpected forms. 1651 1652// We split the second stage into 4 regexp operations in order to work around 1653// crippling inefficiencies in IE's and Safari's regexp engines. First we 1654// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 1655// replace all simple value tokens with ']' characters. Third, we delete all 1656// open brackets that follow a colon or comma or that begin the text. Finally, 1657// we look to see that the remaining characters are only whitespace or ']' or 1658// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 1659 1660 if (/^[\],:{}\s]*$/. 1661test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). 1662replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). 1663replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 1664 1665// In the third stage we use the eval function to compile the text into a 1666// JavaScript structure. The '{' operator is subject to a syntactic ambiguity 1667// in JavaScript: it can begin a block or an object literal. We wrap the text 1668// in parens to eliminate the ambiguity. 1669 1670 j = eval('(' + text + ')'); 1671 1672// In the optional fourth stage, we recursively walk the new structure, passing 1673// each name/value pair to a reviver function for possible transformation. 1674 1675 return typeof reviver === 'function' ? 1676 walk({'': j}, '') : j; 1677 } 1678 1679// If the text is not JSON parseable, then a SyntaxError is thrown. 1680 1681 throw new SyntaxError('JSON.parse'); 1682 }; 1683 } 1684}()); 1685 1686 1687Jemplate.JSON = { 1688 1689 parse: function(encoded) { 1690 return JSON.parse(encoded); 1691 }, 1692 1693 stringify: function(decoded) { 1694 return JSON.stringify(decoded); 1695 } 1696 1697}; 1698 1699 1700}()); 1701... 1702} 1703 1704sub json_yui { 1705 <<'...'; 1706;(function(){ 1707 1708Jemplate.JSON = { 1709 1710 parse: function(encoded) { 1711 return YAHOO.lang.JSON.parse(encoded); 1712 }, 1713 1714 stringify: function(decoded) { 1715 return YAHOO.lang.JSON.stringify(decoded); 1716 } 1717 1718}; 1719 1720}()); 1721 1722... 1723} 1724 1725sub json2 { 1726 <<'...'; 1727/* 1728 http://www.JSON.org/json2.js 1729 2009-04-16 1730 1731 Public Domain. 1732 1733 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 1734 1735 See http://www.JSON.org/js.html 1736 1737 This file creates a global JSON object containing two methods: stringify 1738 and parse. 1739 1740 JSON.stringify(value, replacer, space) 1741 value any JavaScript value, usually an object or array. 1742 1743 replacer an optional parameter that determines how object 1744 values are stringified for objects. It can be a 1745 function or an array of strings. 1746 1747 space an optional parameter that specifies the indentation 1748 of nested structures. If it is omitted, the text will 1749 be packed without extra whitespace. If it is a number, 1750 it will specify the number of spaces to indent at each 1751 level. If it is a string (such as '\t' or ' '), 1752 it contains the characters used to indent at each level. 1753 1754 This method produces a JSON text from a JavaScript value. 1755 1756 When an object value is found, if the object contains a toJSON 1757 method, its toJSON method will be called and the result will be 1758 stringified. A toJSON method does not serialize: it returns the 1759 value represented by the name/value pair that should be serialized, 1760 or undefined if nothing should be serialized. The toJSON method 1761 will be passed the key associated with the value, and this will be 1762 bound to the object holding the key. 1763 1764 For example, this would serialize Dates as ISO strings. 1765 1766 Date.prototype.toJSON = function (key) { 1767 function f(n) { 1768 // Format integers to have at least two digits. 1769 return n < 10 ? '0' + n : n; 1770 } 1771 1772 return this.getUTCFullYear() + '-' + 1773 f(this.getUTCMonth() + 1) + '-' + 1774 f(this.getUTCDate()) + 'T' + 1775 f(this.getUTCHours()) + ':' + 1776 f(this.getUTCMinutes()) + ':' + 1777 f(this.getUTCSeconds()) + 'Z'; 1778 }; 1779 1780 You can provide an optional replacer method. It will be passed the 1781 key and value of each member, with this bound to the containing 1782 object. The value that is returned from your method will be 1783 serialized. If your method returns undefined, then the member will 1784 be excluded from the serialization. 1785 1786 If the replacer parameter is an array of strings, then it will be 1787 used to select the members to be serialized. It filters the results 1788 such that only members with keys listed in the replacer array are 1789 stringified. 1790 1791 Values that do not have JSON representations, such as undefined or 1792 functions, will not be serialized. Such values in objects will be 1793 dropped; in arrays they will be replaced with null. You can use 1794 a replacer function to replace those with JSON values. 1795 JSON.stringify(undefined) returns undefined. 1796 1797 The optional space parameter produces a stringification of the 1798 value that is filled with line breaks and indentation to make it 1799 easier to read. 1800 1801 If the space parameter is a non-empty string, then that string will 1802 be used for indentation. If the space parameter is a number, then 1803 the indentation will be that many spaces. 1804 1805 Example: 1806 1807 text = JSON.stringify(['e', {pluribus: 'unum'}]); 1808 // text is '["e",{"pluribus":"unum"}]' 1809 1810 1811 text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 1812 // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 1813 1814 text = JSON.stringify([new Date()], function (key, value) { 1815 return this[key] instanceof Date ? 1816 'Date(' + this[key] + ')' : value; 1817 }); 1818 // text is '["Date(---current time---)"]' 1819 1820 1821 JSON.parse(text, reviver) 1822 This method parses a JSON text to produce an object or array. 1823 It can throw a SyntaxError exception. 1824 1825 The optional reviver parameter is a function that can filter and 1826 transform the results. It receives each of the keys and values, 1827 and its return value is used instead of the original value. 1828 If it returns what it received, then the structure is not modified. 1829 If it returns undefined then the member is deleted. 1830 1831 Example: 1832 1833 // Parse the text. Values that look like ISO date strings will 1834 // be converted to Date objects. 1835 1836 myData = JSON.parse(text, function (key, value) { 1837 var a; 1838 if (typeof value === 'string') { 1839 a = 1840/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 1841 if (a) { 1842 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 1843 +a[5], +a[6])); 1844 } 1845 } 1846 return value; 1847 }); 1848 1849 myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 1850 var d; 1851 if (typeof value === 'string' && 1852 value.slice(0, 5) === 'Date(' && 1853 value.slice(-1) === ')') { 1854 d = new Date(value.slice(5, -1)); 1855 if (d) { 1856 return d; 1857 } 1858 } 1859 return value; 1860 }); 1861 1862 1863 This is a reference implementation. You are free to copy, modify, or 1864 redistribute. 1865 1866 This code should be minified before deployment. 1867 See http://javascript.crockford.com/jsmin.html 1868 1869 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 1870 NOT CONTROL. 1871*/ 1872 1873/*jslint evil: true */ 1874 1875/*global JSON */ 1876 1877/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 1878 call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 1879 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 1880 lastIndex, length, parse, prototype, push, replace, slice, stringify, 1881 test, toJSON, toString, valueOf 1882*/ 1883 1884// Create a JSON object only if one does not already exist. We create the 1885// methods in a closure to avoid creating global variables. 1886 1887if (!this.JSON) { 1888 this.JSON = {}; 1889} 1890(function () { 1891 1892 function f(n) { 1893 // Format integers to have at least two digits. 1894 return n < 10 ? '0' + n : n; 1895 } 1896 1897 if (typeof Date.prototype.toJSON !== 'function') { 1898 1899 Date.prototype.toJSON = function (key) { 1900 1901 return this.getUTCFullYear() + '-' + 1902 f(this.getUTCMonth() + 1) + '-' + 1903 f(this.getUTCDate()) + 'T' + 1904 f(this.getUTCHours()) + ':' + 1905 f(this.getUTCMinutes()) + ':' + 1906 f(this.getUTCSeconds()) + 'Z'; 1907 }; 1908 1909 String.prototype.toJSON = 1910 Number.prototype.toJSON = 1911 Boolean.prototype.toJSON = function (key) { 1912 return this.valueOf(); 1913 }; 1914 } 1915 1916 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 1917 escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 1918 gap, 1919 indent, 1920 meta = { // table of character substitutions 1921 '\b': '\\b', 1922 '\t': '\\t', 1923 '\n': '\\n', 1924 '\f': '\\f', 1925 '\r': '\\r', 1926 '"' : '\\"', 1927 '\\': '\\\\' 1928 }, 1929 rep; 1930 1931 1932 function quote(string) { 1933 1934// If the string contains no control characters, no quote characters, and no 1935// backslash characters, then we can safely slap some quotes around it. 1936// Otherwise we must also replace the offending characters with safe escape 1937// sequences. 1938 1939 escapable.lastIndex = 0; 1940 return escapable.test(string) ? 1941 '"' + string.replace(escapable, function (a) { 1942 var c = meta[a]; 1943 return typeof c === 'string' ? c : 1944 '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 1945 }) + '"' : 1946 '"' + string + '"'; 1947 } 1948 1949 1950 function str(key, holder) { 1951 1952// Produce a string from holder[key]. 1953 1954 var i, // The loop counter. 1955 k, // The member key. 1956 v, // The member value. 1957 length, 1958 mind = gap, 1959 partial, 1960 value = holder[key]; 1961 1962// If the value has a toJSON method, call it to obtain a replacement value. 1963 1964 if (value && typeof value === 'object' && 1965 typeof value.toJSON === 'function') { 1966 value = value.toJSON(key); 1967 } 1968 1969// If we were called with a replacer function, then call the replacer to 1970// obtain a replacement value. 1971 1972 if (typeof rep === 'function') { 1973 value = rep.call(holder, key, value); 1974 } 1975 1976// What happens next depends on the value's type. 1977 1978 switch (typeof value) { 1979 case 'string': 1980 return quote(value); 1981 1982 case 'number': 1983 1984// JSON numbers must be finite. Encode non-finite numbers as null. 1985 1986 return isFinite(value) ? String(value) : 'null'; 1987 1988 case 'boolean': 1989 case 'null': 1990 1991// If the value is a boolean or null, convert it to a string. Note: 1992// typeof null does not produce 'null'. The case is included here in 1993// the remote chance that this gets fixed someday. 1994 1995 return String(value); 1996 1997// If the type is 'object', we might be dealing with an object or an array or 1998// null. 1999 2000 case 'object': 2001 2002// Due to a specification blunder in ECMAScript, typeof null is 'object', 2003// so watch out for that case. 2004 2005 if (!value) { 2006 return 'null'; 2007 } 2008 2009// Make an array to hold the partial results of stringifying this object value. 2010 2011 gap += indent; 2012 partial = []; 2013 2014// Is the value an array? 2015 2016 if (Object.prototype.toString.apply(value) === '[object Array]') { 2017 2018// The value is an array. Stringify every element. Use null as a placeholder 2019// for non-JSON values. 2020 2021 length = value.length; 2022 for (i = 0; i < length; i += 1) { 2023 partial[i] = str(i, value) || 'null'; 2024 } 2025 2026// Join all of the elements together, separated with commas, and wrap them in 2027// brackets. 2028 2029 v = partial.length === 0 ? '[]' : 2030 gap ? '[\n' + gap + 2031 partial.join(',\n' + gap) + '\n' + 2032 mind + ']' : 2033 '[' + partial.join(',') + ']'; 2034 gap = mind; 2035 return v; 2036 } 2037 2038// If the replacer is an array, use it to select the members to be stringified. 2039 2040 if (rep && typeof rep === 'object') { 2041 length = rep.length; 2042 for (i = 0; i < length; i += 1) { 2043 k = rep[i]; 2044 if (typeof k === 'string') { 2045 v = str(k, value); 2046 if (v) { 2047 partial.push(quote(k) + (gap ? ': ' : ':') + v); 2048 } 2049 } 2050 } 2051 } else { 2052 2053// Otherwise, iterate through all of the keys in the object. 2054 2055 for (k in value) { 2056 if (Object.hasOwnProperty.call(value, k)) { 2057 v = str(k, value); 2058 if (v) { 2059 partial.push(quote(k) + (gap ? ': ' : ':') + v); 2060 } 2061 } 2062 } 2063 } 2064 2065// Join all of the member texts together, separated with commas, 2066// and wrap them in braces. 2067 2068 v = partial.length === 0 ? '{}' : 2069 gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + 2070 mind + '}' : '{' + partial.join(',') + '}'; 2071 gap = mind; 2072 return v; 2073 } 2074 2075 return ''; 2076 } 2077 2078// If the JSON object does not yet have a stringify method, give it one. 2079 2080 if (typeof JSON.stringify !== 'function') { 2081 JSON.stringify = function (value, replacer, space) { 2082 2083// The stringify method takes a value and an optional replacer, and an optional 2084// space parameter, and returns a JSON text. The replacer can be a function 2085// that can replace values, or an array of strings that will select the keys. 2086// A default replacer method can be provided. Use of the space parameter can 2087// produce text that is more easily readable. 2088 2089 var i; 2090 gap = ''; 2091 indent = ''; 2092 2093// If the space parameter is a number, make an indent string containing that 2094// many spaces. 2095 2096 if (typeof space === 'number') { 2097 for (i = 0; i < space; i += 1) { 2098 indent += ' '; 2099 } 2100 2101// If the space parameter is a string, it will be used as the indent string. 2102 2103 } else if (typeof space === 'string') { 2104 indent = space; 2105 } 2106 2107// If there is a replacer, it must be a function or an array. 2108// Otherwise, throw an error. 2109 2110 rep = replacer; 2111 if (replacer && typeof replacer !== 'function' && 2112 (typeof replacer !== 'object' || 2113 typeof replacer.length !== 'number')) { 2114 throw new Error('JSON.stringify'); 2115 } 2116 2117// Make a fake root object containing our value under the key of ''. 2118// Return the result of stringifying the value. 2119 2120 return str('', {'': value}); 2121 }; 2122 } 2123 2124 2125// If the JSON object does not yet have a parse method, give it one. 2126 2127 if (typeof JSON.parse !== 'function') { 2128 JSON.parse = function (text, reviver) { 2129 2130// The parse method takes a text and an optional reviver function, and returns 2131// a JavaScript value if the text is a valid JSON text. 2132 2133 var j; 2134 2135 function walk(holder, key) { 2136 2137// The walk method is used to recursively walk the resulting structure so 2138// that modifications can be made. 2139 2140 var k, v, value = holder[key]; 2141 if (value && typeof value === 'object') { 2142 for (k in value) { 2143 if (Object.hasOwnProperty.call(value, k)) { 2144 v = walk(value, k); 2145 if (v !== undefined) { 2146 value[k] = v; 2147 } else { 2148 delete value[k]; 2149 } 2150 } 2151 } 2152 } 2153 return reviver.call(holder, key, value); 2154 } 2155 2156 2157// Parsing happens in four stages. In the first stage, we replace certain 2158// Unicode characters with escape sequences. JavaScript handles many characters 2159// incorrectly, either silently deleting them, or treating them as line endings. 2160 2161 cx.lastIndex = 0; 2162 if (cx.test(text)) { 2163 text = text.replace(cx, function (a) { 2164 return '\\u' + 2165 ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 2166 }); 2167 } 2168 2169// In the second stage, we run the text against regular expressions that look 2170// for non-JSON patterns. We are especially concerned with '()' and 'new' 2171// because they can cause invocation, and '=' because it can cause mutation. 2172// But just to be safe, we want to reject all unexpected forms. 2173 2174// We split the second stage into 4 regexp operations in order to work around 2175// crippling inefficiencies in IE's and Safari's regexp engines. First we 2176// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 2177// replace all simple value tokens with ']' characters. Third, we delete all 2178// open brackets that follow a colon or comma or that begin the text. Finally, 2179// we look to see that the remaining characters are only whitespace or ']' or 2180// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 2181 2182 if (/^[\],:{}\s]*$/. 2183test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). 2184replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). 2185replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 2186 2187// In the third stage we use the eval function to compile the text into a 2188// JavaScript structure. The '{' operator is subject to a syntactic ambiguity 2189// in JavaScript: it can begin a block or an object literal. We wrap the text 2190// in parens to eliminate the ambiguity. 2191 2192 j = eval('(' + text + ')'); 2193 2194// In the optional fourth stage, we recursively walk the new structure, passing 2195// each name/value pair to a reviver function for possible transformation. 2196 2197 return typeof reviver === 'function' ? 2198 walk({'': j}, '') : j; 2199 } 2200 2201// If the text is not JSON parseable, then a SyntaxError is thrown. 2202 2203 throw new SyntaxError('JSON.parse'); 2204 }; 2205 } 2206}()); 2207 2208... 2209} 2210 2211sub xhr_gregory { 2212 <<'...'; 2213/* 2214 2215Cross-Browser XMLHttpRequest v1.2 2216================================= 2217 2218Emulate Gecko 'XMLHttpRequest()' functionality in IE and Opera. Opera requires 2219the Sun Java Runtime Environment <http://www.java.com/>. 2220 2221by Andrew Gregory 2222http://www.scss.com.au/family/andrew/webdesign/xmlhttprequest/ 2223 2224This work is licensed under the Creative Commons Attribution License. To view a 2225copy of this license, visit http://creativecommons.org/licenses/by-sa/2.5/ or 2226send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 222794305, USA. 2228 2229Attribution: Leave my name and web address in this script intact. 2230 2231Not Supported in Opera 2232---------------------- 2233* user/password authentication 2234* responseXML data member 2235 2236Not Fully Supported in Opera 2237---------------------------- 2238* async requests 2239* abort() 2240* getAllResponseHeaders(), getAllResponseHeader(header) 2241 2242*/ 2243// IE support 2244if (window.ActiveXObject && !window.XMLHttpRequest) { 2245 window.XMLHttpRequest = function() { 2246 var msxmls = new Array( 2247 'Msxml2.XMLHTTP.5.0', 2248 'Msxml2.XMLHTTP.4.0', 2249 'Msxml2.XMLHTTP.3.0', 2250 'Msxml2.XMLHTTP', 2251 'Microsoft.XMLHTTP'); 2252 for (var i = 0; i < msxmls.length; i++) { 2253 try { 2254 return new ActiveXObject(msxmls[i]); 2255 } catch (e) { 2256 } 2257 } 2258 return null; 2259 }; 2260} 2261// Gecko support 2262/* ;-) */ 2263// Opera support 2264if (window.opera && !window.XMLHttpRequest) { 2265 window.XMLHttpRequest = function() { 2266 this.readyState = 0; // 0=uninitialized,1=loading,2=loaded,3=interactive,4=complete 2267 this.status = 0; // HTTP status codes 2268 this.statusText = ''; 2269 this._headers = []; 2270 this._aborted = false; 2271 this._async = true; 2272 this._defaultCharset = 'ISO-8859-1'; 2273 this._getCharset = function() { 2274 var charset = _defaultCharset; 2275 var contentType = this.getResponseHeader('Content-type').toUpperCase(); 2276 val = contentType.indexOf('CHARSET='); 2277 if (val != -1) { 2278 charset = contentType.substring(val); 2279 } 2280 val = charset.indexOf(';'); 2281 if (val != -1) { 2282 charset = charset.substring(0, val); 2283 } 2284 val = charset.indexOf(','); 2285 if (val != -1) { 2286 charset = charset.substring(0, val); 2287 } 2288 return charset; 2289 }; 2290 this.abort = function() { 2291 this._aborted = true; 2292 }; 2293 this.getAllResponseHeaders = function() { 2294 return this.getAllResponseHeader('*'); 2295 }; 2296 this.getAllResponseHeader = function(header) { 2297 var ret = ''; 2298 for (var i = 0; i < this._headers.length; i++) { 2299 if (header == '*' || this._headers[i].h == header) { 2300 ret += this._headers[i].h + ': ' + this._headers[i].v + '\n'; 2301 } 2302 } 2303 return ret; 2304 }; 2305 this.getResponseHeader = function(header) { 2306 var ret = getAllResponseHeader(header); 2307 var i = ret.indexOf('\n'); 2308 if (i != -1) { 2309 ret = ret.substring(0, i); 2310 } 2311 return ret; 2312 }; 2313 this.setRequestHeader = function(header, value) { 2314 this._headers[this._headers.length] = {h:header, v:value}; 2315 }; 2316 this.open = function(method, url, async, user, password) { 2317 this.method = method; 2318 this.url = url; 2319 this._async = true; 2320 this._aborted = false; 2321 this._headers = []; 2322 if (arguments.length >= 3) { 2323 this._async = async; 2324 } 2325 if (arguments.length > 3) { 2326 opera.postError('XMLHttpRequest.open() - user/password not supported'); 2327 } 2328 this.readyState = 1; 2329 if (this.onreadystatechange) { 2330 this.onreadystatechange(); 2331 } 2332 }; 2333 this.send = function(data) { 2334 if (!navigator.javaEnabled()) { 2335 alert("XMLHttpRequest.send() - Java must be installed and enabled."); 2336 return; 2337 } 2338 if (this._async) { 2339 setTimeout(this._sendasync, 0, this, data); 2340 // this is not really asynchronous and won't execute until the current 2341 // execution context ends 2342 } else { 2343 this._sendsync(data); 2344 } 2345 } 2346 this._sendasync = function(req, data) { 2347 if (!req._aborted) { 2348 req._sendsync(data); 2349 } 2350 }; 2351 this._sendsync = function(data) { 2352 this.readyState = 2; 2353 if (this.onreadystatechange) { 2354 this.onreadystatechange(); 2355 } 2356 // open connection 2357 var url = new java.net.URL(new java.net.URL(window.location.href), this.url); 2358 var conn = url.openConnection(); 2359 for (var i = 0; i < this._headers.length; i++) { 2360 conn.setRequestProperty(this._headers[i].h, this._headers[i].v); 2361 } 2362 this._headers = []; 2363 if (this.method == 'POST') { 2364 // POST data 2365 conn.setDoOutput(true); 2366 var wr = new java.io.OutputStreamWriter(conn.getOutputStream(), this._getCharset()); 2367 wr.write(data); 2368 wr.flush(); 2369 wr.close(); 2370 } 2371 // read response headers 2372 // NOTE: the getHeaderField() methods always return nulls for me :( 2373 var gotContentEncoding = false; 2374 var gotContentLength = false; 2375 var gotContentType = false; 2376 var gotDate = false; 2377 var gotExpiration = false; 2378 var gotLastModified = false; 2379 for (var i = 0; ; i++) { 2380 var hdrName = conn.getHeaderFieldKey(i); 2381 var hdrValue = conn.getHeaderField(i); 2382 if (hdrName == null && hdrValue == null) { 2383 break; 2384 } 2385 if (hdrName != null) { 2386 this._headers[this._headers.length] = {h:hdrName, v:hdrValue}; 2387 switch (hdrName.toLowerCase()) { 2388 case 'content-encoding': gotContentEncoding = true; break; 2389 case 'content-length' : gotContentLength = true; break; 2390 case 'content-type' : gotContentType = true; break; 2391 case 'date' : gotDate = true; break; 2392 case 'expires' : gotExpiration = true; break; 2393 case 'last-modified' : gotLastModified = true; break; 2394 } 2395 } 2396 } 2397 // try to fill in any missing header information 2398 var val; 2399 val = conn.getContentEncoding(); 2400 if (val != null && !gotContentEncoding) this._headers[this._headers.length] = {h:'Content-encoding', v:val}; 2401 val = conn.getContentLength(); 2402 if (val != -1 && !gotContentLength) this._headers[this._headers.length] = {h:'Content-length', v:val}; 2403 val = conn.getContentType(); 2404 if (val != null && !gotContentType) this._headers[this._headers.length] = {h:'Content-type', v:val}; 2405 val = conn.getDate(); 2406 if (val != 0 && !gotDate) this._headers[this._headers.length] = {h:'Date', v:(new Date(val)).toUTCString()}; 2407 val = conn.getExpiration(); 2408 if (val != 0 && !gotExpiration) this._headers[this._headers.length] = {h:'Expires', v:(new Date(val)).toUTCString()}; 2409 val = conn.getLastModified(); 2410 if (val != 0 && !gotLastModified) this._headers[this._headers.length] = {h:'Last-modified', v:(new Date(val)).toUTCString()}; 2411 // read response data 2412 var reqdata = ''; 2413 var stream = conn.getInputStream(); 2414 if (stream) { 2415 var reader = new java.io.BufferedReader(new java.io.InputStreamReader(stream, this._getCharset())); 2416 var line; 2417 while ((line = reader.readLine()) != null) { 2418 if (this.readyState == 2) { 2419 this.readyState = 3; 2420 if (this.onreadystatechange) { 2421 this.onreadystatechange(); 2422 } 2423 } 2424 reqdata += line + '\n'; 2425 } 2426 reader.close(); 2427 this.status = 200; 2428 this.statusText = 'OK'; 2429 this.responseText = reqdata; 2430 this.readyState = 4; 2431 if (this.onreadystatechange) { 2432 this.onreadystatechange(); 2433 } 2434 if (this.onload) { 2435 this.onload(); 2436 } 2437 } else { 2438 // error 2439 this.status = 404; 2440 this.statusText = 'Not Found'; 2441 this.responseText = ''; 2442 this.readyState = 4; 2443 if (this.onreadystatechange) { 2444 this.onreadystatechange(); 2445 } 2446 if (this.onerror) { 2447 this.onerror(); 2448 } 2449 } 2450 }; 2451 }; 2452} 2453// ActiveXObject emulation 2454if (!window.ActiveXObject && window.XMLHttpRequest) { 2455 window.ActiveXObject = function(type) { 2456 switch (type.toLowerCase()) { 2457 case 'microsoft.xmlhttp': 2458 case 'msxml2.xmlhttp': 2459 case 'msxml2.xmlhttp.3.0': 2460 case 'msxml2.xmlhttp.4.0': 2461 case 'msxml2.xmlhttp.5.0': 2462 return new XMLHttpRequest(); 2463 } 2464 return null; 2465 }; 2466} 2467... 2468} 2469 2470sub xhr_ilinsky { 2471 <<'...'; 2472// Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com) 2473// 2474// Licensed under the Apache License, Version 2.0 (the "License"); 2475// you may not use this file except in compliance with the License. 2476// You may obtain a copy of the License at 2477// 2478// http://www.apache.org/licenses/LICENSE-2.0 2479// 2480// Unless required by applicable law or agreed to in writing, software 2481// distributed under the License is distributed on an "AS IS" BASIS, 2482// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2483// See the License for the specific language governing permissions and 2484// limitations under the License. 2485 2486(function () { 2487 2488 // Save reference to earlier defined object implementation (if any) 2489 var oXMLHttpRequest = window.XMLHttpRequest; 2490 2491 // Define on browser type 2492 var bGecko = !!window.controllers, 2493 bIE = window.document.all && !window.opera; 2494 2495 // Constructor 2496 function cXMLHttpRequest() { 2497 this._object = oXMLHttpRequest ? new oXMLHttpRequest : new window.ActiveXObject('Microsoft.XMLHTTP'); 2498 }; 2499 2500 // BUGFIX: Firefox with Firebug installed would break pages if not executed 2501 if (bGecko && oXMLHttpRequest.wrapped) 2502 cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped; 2503 2504 // Constants 2505 cXMLHttpRequest.UNSENT = 0; 2506 cXMLHttpRequest.OPENED = 1; 2507 cXMLHttpRequest.HEADERS_RECEIVED = 2; 2508 cXMLHttpRequest.LOADING = 3; 2509 cXMLHttpRequest.DONE = 4; 2510 2511 // Public Properties 2512 cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT; 2513 cXMLHttpRequest.prototype.responseText = ""; 2514 cXMLHttpRequest.prototype.responseXML = null; 2515 cXMLHttpRequest.prototype.status = 0; 2516 cXMLHttpRequest.prototype.statusText = ""; 2517 2518 // Instance-level Events Handlers 2519 cXMLHttpRequest.prototype.onreadystatechange = null; 2520 2521 // Class-level Events Handlers 2522 cXMLHttpRequest.onreadystatechange = null; 2523 cXMLHttpRequest.onopen = null; 2524 cXMLHttpRequest.onsend = null; 2525 cXMLHttpRequest.onabort = null; 2526 2527 // Public Methods 2528 cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) { 2529 2530 // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests 2531 this._async = bAsync; 2532 2533 // Set the onreadystatechange handler 2534 var oRequest = this, 2535 nState = this.readyState; 2536 2537 // BUGFIX: IE - memory leak on page unload (inter-page leak) 2538 if (bIE) { 2539 var fOnUnload = function() { 2540 if (oRequest._object.readyState != cXMLHttpRequest.DONE) 2541 fCleanTransport(oRequest); 2542 }; 2543 if (bAsync) 2544 window.attachEvent("onunload", fOnUnload); 2545 } 2546 2547 this._object.onreadystatechange = function() { 2548 if (bGecko && !bAsync) 2549 return; 2550 2551 // Synchronize state 2552 oRequest.readyState = oRequest._object.readyState; 2553 2554 // 2555 fSynchronizeValues(oRequest); 2556 2557 // BUGFIX: Firefox fires unneccesary DONE when aborting 2558 if (oRequest._aborted) { 2559 // Reset readyState to UNSENT 2560 oRequest.readyState = cXMLHttpRequest.UNSENT; 2561 2562 // Return now 2563 return; 2564 } 2565 2566 if (oRequest.readyState == cXMLHttpRequest.DONE) { 2567 // 2568 fCleanTransport(oRequest); 2569// Uncomment this block if you need a fix for IE cache 2570/* 2571 // BUGFIX: IE - cache issue 2572 if (!oRequest._object.getResponseHeader("Date")) { 2573 // Save object to cache 2574 oRequest._cached = oRequest._object; 2575 2576 // Instantiate a new transport object 2577 cXMLHttpRequest.call(oRequest); 2578 2579 // Re-send request 2580 oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword); 2581 oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0)); 2582 // Copy headers set 2583 if (oRequest._headers) 2584 for (var sHeader in oRequest._headers) 2585 if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions 2586 oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]); 2587 2588 oRequest._object.onreadystatechange = function() { 2589 // Synchronize state 2590 oRequest.readyState = oRequest._object.readyState; 2591 2592 if (oRequest._aborted) { 2593 // 2594 oRequest.readyState = cXMLHttpRequest.UNSENT; 2595 2596 // Return 2597 return; 2598 } 2599 2600 if (oRequest.readyState == cXMLHttpRequest.DONE) { 2601 // Clean Object 2602 fCleanTransport(oRequest); 2603 2604 // get cached request 2605 if (oRequest.status == 304) 2606 oRequest._object = oRequest._cached; 2607 2608 // 2609 delete oRequest._cached; 2610 2611 // 2612 fSynchronizeValues(oRequest); 2613 2614 // 2615 fReadyStateChange(oRequest); 2616 2617 // BUGFIX: IE - memory leak in interrupted 2618 if (bIE && bAsync) 2619 window.detachEvent("onunload", fOnUnload); 2620 } 2621 }; 2622 oRequest._object.send(null); 2623 2624 // Return now - wait untill re-sent request is finished 2625 return; 2626 }; 2627*/ 2628 // BUGFIX: IE - memory leak in interrupted 2629 if (bIE && bAsync) 2630 window.detachEvent("onunload", fOnUnload); 2631 } 2632 2633 // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice 2634 if (nState != oRequest.readyState) 2635 fReadyStateChange(oRequest); 2636 2637 nState = oRequest.readyState; 2638 }; 2639 // Add method sniffer 2640 if (cXMLHttpRequest.onopen) 2641 cXMLHttpRequest.onopen.apply(this, arguments); 2642 2643 this._object.open(sMethod, sUrl, bAsync, sUser, sPassword); 2644 2645 // BUGFIX: Gecko - missing readystatechange calls in synchronous requests 2646 if (!bAsync && bGecko) { 2647 this.readyState = cXMLHttpRequest.OPENED; 2648 2649 fReadyStateChange(this); 2650 } 2651 }; 2652 cXMLHttpRequest.prototype.send = function(vData) { 2653 // Add method sniffer 2654 if (cXMLHttpRequest.onsend) 2655 cXMLHttpRequest.onsend.apply(this, arguments); 2656 2657 // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required 2658 // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent 2659 // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard) 2660 if (vData && vData.nodeType) { 2661 vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml; 2662 if (!this._headers["Content-Type"]) 2663 this._object.setRequestHeader("Content-Type", "application/xml"); 2664 } 2665 2666 this._object.send(vData); 2667 2668 // BUGFIX: Gecko - missing readystatechange calls in synchronous requests 2669 if (bGecko && !this._async) { 2670 this.readyState = cXMLHttpRequest.OPENED; 2671 2672 // Synchronize state 2673 fSynchronizeValues(this); 2674 2675 // Simulate missing states 2676 while (this.readyState < cXMLHttpRequest.DONE) { 2677 this.readyState++; 2678 fReadyStateChange(this); 2679 // Check if we are aborted 2680 if (this._aborted) 2681 return; 2682 } 2683 } 2684 }; 2685 cXMLHttpRequest.prototype.abort = function() { 2686 // Add method sniffer 2687 if (cXMLHttpRequest.onabort) 2688 cXMLHttpRequest.onabort.apply(this, arguments); 2689 2690 // BUGFIX: Gecko - unneccesary DONE when aborting 2691 if (this.readyState > cXMLHttpRequest.UNSENT) 2692 this._aborted = true; 2693 2694 this._object.abort(); 2695 2696 // BUGFIX: IE - memory leak 2697 fCleanTransport(this); 2698 }; 2699 cXMLHttpRequest.prototype.getAllResponseHeaders = function() { 2700 return this._object.getAllResponseHeaders(); 2701 }; 2702 cXMLHttpRequest.prototype.getResponseHeader = function(sName) { 2703 return this._object.getResponseHeader(sName); 2704 }; 2705 cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) { 2706 // BUGFIX: IE - cache issue 2707 if (!this._headers) 2708 this._headers = {}; 2709 this._headers[sName] = sValue; 2710 2711 return this._object.setRequestHeader(sName, sValue); 2712 }; 2713 cXMLHttpRequest.prototype.toString = function() { 2714 return '[' + "object" + ' ' + "XMLHttpRequest" + ']'; 2715 }; 2716 cXMLHttpRequest.toString = function() { 2717 return '[' + "XMLHttpRequest" + ']'; 2718 }; 2719 2720 // Helper function 2721 function fReadyStateChange(oRequest) { 2722 // Execute onreadystatechange 2723 if (oRequest.onreadystatechange) 2724 oRequest.onreadystatechange.apply(oRequest); 2725 2726 // Sniffing code 2727 if (cXMLHttpRequest.onreadystatechange) 2728 cXMLHttpRequest.onreadystatechange.apply(oRequest); 2729 }; 2730 2731 function fGetDocument(oRequest) { 2732 var oDocument = oRequest.responseXML; 2733 // Try parsing responseText 2734 if (bIE && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) { 2735 oDocument = new ActiveXObject('Microsoft.XMLDOM'); 2736 oDocument.loadXML(oRequest.responseText); 2737 } 2738 // Check if there is no error in document 2739 if (oDocument) 2740 if ((bIE && oDocument.parseError != 0) || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror")) 2741 return null; 2742 return oDocument; 2743 }; 2744 2745 function fSynchronizeValues(oRequest) { 2746 try { oRequest.responseText = oRequest._object.responseText; } catch (e) {} 2747 try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {} 2748 try { oRequest.status = oRequest._object.status; } catch (e) {} 2749 try { oRequest.statusText = oRequest._object.statusText; } catch (e) {} 2750 }; 2751 2752 function fCleanTransport(oRequest) { 2753 // BUGFIX: IE - memory leak (on-page leak) 2754 oRequest._object.onreadystatechange = new window.Function; 2755 2756 // Delete private properties 2757 delete oRequest._headers; 2758 }; 2759 2760 // Internet Explorer 5.0 (missing apply) 2761 if (!window.Function.prototype.apply) { 2762 window.Function.prototype.apply = function(oRequest, oArguments) { 2763 if (!oArguments) 2764 oArguments = []; 2765 oRequest.__func = this; 2766 oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]); 2767 delete oRequest.__func; 2768 }; 2769 }; 2770 2771 // Register new object with window 2772 window.XMLHttpRequest = cXMLHttpRequest; 2773})(); 2774 2775 2776... 2777} 2778 2779sub xxx { 2780 <<'...'; 2781//------------------------------------------------------------------------------ 2782// Debugging Support 2783//------------------------------------------------------------------------------ 2784 2785function XXX(msg) { 2786 if (! confirm(msg)) 2787 throw("terminated..."); 2788 return msg; 2789} 2790 2791function JJJ(obj) { 2792 return XXX(JSON.stringify(obj)); 2793} 2794 2795... 2796} 2797 27981; 2799 2800__END__ 2801 2802=encoding UTF-8 2803 2804=head1 NAME 2805 2806Jemplate::Runtime - Perl Module containing the Jemplate JavaScript Runtime 2807 2808=head1 SYNOPSIS 2809 2810 use Jemplate::Runtime; 2811 print Jemplate::Runtime->main; 2812 2813=head1 DESCRIPTION 2814 2815This module is auto-generated and used internally by Jemplate. It 2816contains subroutines that simply return various parts of the Jemplate 2817JavaScript Runtime code. 2818 2819=head1 METHODS 2820 2821head2 kernel 2822 2823head2 ajax_jquery 2824 2825head2 ajax_xhr 2826 2827head2 ajax_yui 2828 2829head2 json_json2 2830 2831head2 json_yui 2832 2833head2 json2 2834 2835head2 xhr_gregory 2836 2837head2 xhr_ilinsky 2838 2839head2 xxx 2840 2841=head1 COPYRIGHT 2842 2843Copyright (c) 2014. Ingy döt Net. 2844 2845This program is free software; you can redistribute it and/or modify it 2846under the same terms as Perl itself. 2847 2848See L<http://www.perl.com/perl/misc/Artistic.html> 2849 2850=cut 2851