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, '&amp;');
295    text = text.replace(/</g, '&lt;');
296    text = text.replace(/>/g, '&gt;');
297    text = text.replace(/"/g, '&quot;'); // " 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 '&nbsp;'),
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 '&nbsp;'),
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