WIP: Thymoljs
[website] / dist / js / thymol-lite.js
1 /*-------------------- Thymol - the flavour of Thymeleaf --------------------*
2
3    Thymol version 2.0.0 Copyright (C) 2012-2015 James J. Benson
4    jjbenson .AT. users.sf.net (http://www.thymoljs.org/)
5
6    Licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8    You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" basis,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17
18  *---------------------------------------------------------------------------*/
19
20 thymol = function() {
21     thymol.thVersion = "2.0.0";
22     thymol.thReleaseDate = "2015-03-31";
23     thymol.thURL = "http://www.thymoljs.org";
24     thymol.thAltURL = "http://www.thymeleaf.org";
25     thymol.thUsingNullPrefix = false;
26     thymol.thThymeleafPrefixList = [];
27     thymol.thThymeleafElementsList = [];
28     thymol.objects = {};
29     var textFuncSynonym = "~~~~", varRefExpr = /([$#]{.*?})/, literalTokenExpr = /^[a-zA-Z0-9\[\]\.\-_]*$/, startParserLevelCommentExpr = /^\s*\/\*\s*$/, endParserLevelCommentExpr = /^\s*\*\/\s*$/, startParserLevelCommentExpr2 = /^\/\*[^\/].*/, endParserLevelCommentExpr2 = /.*[^\/]\*\/$/, prototypeOnlyCommentEscpExpr = /\/\*\/(.*)\/\*\//, varExpr3 = /[\$\*#@]{1}\{(.*)\}$/, nonURLExpr = /[\$\*#]{1}\{(?:!?[^}]*)\}/, numericExpr = /^[+\-]?[0-9]*?[.]?[0-9]*?$/, varParExpr = /([^(]*)\s*[(]([^)]*?)[)]/, domSelectExpr = /([\/]{1,2})?([A-Za-z0-9_\-]*(?:[\(][\)])?)?([^\[]\S[A-Za-z0-9_\-]*(?:[\(][\)])?[\/]*(?:[\.\/#]?[^\[]\S[A-Za-z0-9_\-]*(?:[\(][\)])?[\/]*)*)?([\[][^\]]*?[\]])?/, litSubstExpr = /\.*?([\|][^\|]*?[\|])\.*?/;
30     function Thymol() {}
31     function isClientSide() {
32         if (typeof thymol.isServerSide !== "undefined" && !!thymol.isServerSide()) {
33             thymol.isClientSide = function() {
34                 return false;
35             };
36             return false;
37         }
38         thymol.isClientSide = function() {
39             return true;
40         };
41         return true;
42     }
43     function execute(doc) {
44         if (typeof thymol.protocol === "undefined") {
45             thymol.protocol = "";
46         }
47         if (typeof thymol.root === "undefined") {
48             thymol.root = "";
49         }
50         if (typeof thymol.path === "undefined") {
51             thymol.path = "";
52         }
53         thymol.thDocument = doc;
54         var theWindow = thymol.thWindow;
55         if (typeof thymol.thWindow === "undefined") {
56             if (typeof doc.defaultView !== "undefined") {
57                 theWindow = doc.defaultView;
58             } else if (typeof doc.parentWindow !== "undefined") {
59                 theWindow = doc.parentWindow;
60             }
61         }
62         thymol.thWindow = theWindow;
63         var theTop = thymol.thTop;
64         if (typeof thymol.thTop === "undefined") {
65             if (typeof top !== "undefined") {
66                 theTop = top;
67             }
68         }
69         thymol.thTop = theTop;
70         thymol.init();
71         var base = new ThNode(thymol.thDocument, false, null, null, null, thymol.thDocument.nodeName, "::", false, thymol.thDocument);
72         Thymol.prototype.process(base);
73         postExecute();
74         return thymol.thDocument;
75     }
76     function jqSetup(jq) {
77         jq.fn.extend({
78             getComments: function() {
79                 return this.filter(function() {
80                     return this.nodeType === 8;
81                 });
82             },
83             getThDecorated: function(thInst) {
84                 var i, iAttrName, iLength, j, jLength, instances = [], result = null, expanded = false;
85                 if (thInst.escpName !== null) {
86                     instances = this.filter(thInst.escpName);
87                 }
88                 if (thInst.escpSynonym !== null) {
89                     instances = instances.add(this.filter(thInst.escpSynonym));
90                 }
91                 for (i = 0, iLength = instances.length; i < iLength; i++) {
92                     if (instances[i]) {
93                         for (j = 0, jLength = instances[i].attributes.length; j < jLength; j++) {
94                             if (instances[i].attributes[j]) {
95                                 iAttrName = instances[i].attributes[j].name;
96                                 if (iAttrName && (thInst.name == iAttrName || thInst.synonym == iAttrName)) {
97                                     expanded = thymol.ThUtils.processElement(thInst.process, instances[i], instances[i].attributes[j], thInst);
98                                     if (expanded) {
99                                         if (result === null) {
100                                             result = [];
101                                         }
102                                         result.push(instances[i]);
103                                     }
104                                 }
105                             }
106                         }
107                     }
108                 }
109                 return result;
110             }
111         });
112     }
113     function ready(func) {
114         if (typeof thymolDeferredFunctions === "undefined" || thymolDeferredFunctions === null) {
115             thymolDeferredFunctions = [];
116         }
117         thymolDeferredFunctions.push(func);
118     }
119     function setupEnv() {
120         thymol.prefix = Thymol.prototype.getThParam("thPrefix", false, false, thymol.thDefaultPrefix);
121         thymol.dataPrefix = Thymol.prototype.getThParam("thDataPrefix", false, false, thymol.thDefaultDataPrefix);
122         thymol.messagePath = Thymol.prototype.getThParam("thMessagePath", false, true, thymol.thDefaultMessagePath);
123         thymol.resourcePath = Thymol.prototype.getThParam("thResourcePath", false, true, thymol.thDefaultResourcePath);
124         thymol.messagesBaseName = Thymol.prototype.getThParam("thMessagesBaseName", false, false, thymol.thDefaultMessagesBaseName);
125         thymol.relativeRootPath = Thymol.prototype.getThParam("thRelativeRootPath", false, true, thymol.thDefaultRelativeRootPath);
126         thymol.extendedMapping = Thymol.prototype.getThParam("thExtendedMapping", true, false, thymol.thDefaultExtendedMapping);
127         thymol.localMessages = Thymol.prototype.getThParam("thLocalMessages", true, false, thymol.thDefaultLocalMessages);
128         thymol.disableMessages = Thymol.prototype.getThParam("thDisableMessages", true, false, thymol.thDefaultDisableMessages);
129         thymol.templateSuffix = Thymol.prototype.getThParam("thTemplateSuffix", false, false, thymol.thDefaultTemplateSuffix);
130         thymol.scriptPath = "";
131         if (typeof thymol.thScriptPath !== "undefined") {
132             thymol.scriptPath = Thymol.prototype.getThParam("thScriptPath", false, true, thymol.thScriptPath);
133         }
134         thymol.absolutePath = "";
135         if (typeof thymol.thAbsolutePath !== "undefined") {
136             thymol.absolutePath = Thymol.prototype.getThParam("thAbsolutePath", false, true, thymol.thAbsolutePath);
137         }
138         thymol.useAbsolutePath = false;
139         if (typeof thymol.thUseAbsolutePath !== "undefined") {
140             thymol.useAbsolutePath = Thymol.prototype.getThParam("thUseAbsolutePath", true, false, thymol.thUseAbsolutePath);
141         }
142         thymol.useFullURLPath = true;
143         if (typeof thymol.thUseFullURLPath !== "undefined") {
144             thymol.useFullURLPath = Thymol.prototype.getThParam("thUseFullURLPath", true, false, thymol.thUseFullURLPath);
145         }
146         thymol.indexFile = Thymol.prototype.getThParam("thIndexFile", false, false, null);
147         thymol.debug = Thymol.prototype.getThParam("thDebug", true, false, false);
148         thymol.allowNullText = Thymol.prototype.getThParam("thAllowNullText", true, false, true);
149         thymol.location = thymol.thLocation;
150         if ("" !== thymol.relativeRootPath) {
151             thymol.root = thymol.location + thymol.relativeRootPath;
152             thymol.messagePath = thymol.root + thymol.messagePath;
153         } else {
154             if (typeof thymol.thMessagePath !== "undefined") {
155                 thymol.messagePath = Thymol.prototype.getThParam("thMessagePath", false, true, thymol.thMessagePath);
156             }
157             if (typeof thymol.thRoot !== "undefined") {
158                 thymol.root = Thymol.prototype.getThParam("thRoot", false, true, thymol.thRoot);
159             }
160         }
161         thymol.root = Thymol.prototype.getThParam("thRoot", false, true, thymol.root);
162         if (typeof thymol.thPath !== "undefined") {
163             thymol.path = Thymol.prototype.getThParam("thPath", false, true, thymol.thPath);
164         }
165         thymol.path = Thymol.prototype.getThParam("thPath", false, true, thymol.path);
166         thymol.protocol = thymol.thDocument.location.protocol;
167         if ("" == thymol.protocol) {
168             thymol.protocol = thymol.thDefaultProtocol;
169         } else {
170             thymol.protocol += "//";
171             if ("" == thymol.thDocument.location.host) {
172                 thymol.protocol += "/";
173             }
174         }
175         thymol.protocol = Thymol.prototype.getThParam("thProtocol", false, false, thymol.protocol);
176         thymol.resourcePath = Thymol.prototype.getThParam("thResourcePath", false, true, thymol.resourcePath);
177     }
178     function updatePrefix(pref) {
179         thymol.prefix = pref;
180         thymol.thThymeleafPrefixList = [];
181         thymol.thThymeleafElementsList = [];
182     }
183     function init() {
184         this.messages = null;
185         this.mappings = null;
186         this.debug = null;
187         getLocations(this);
188         this.locale = new thymol.ThObject();
189         getLanguage();
190         var accessor = undefined, i, iLimit, j, jLimit;
191         if (typeof thVars !== "undefined") {
192             accessor = new thymol.ThVarsAccessor(thVars, "thVars");
193         }
194         this.applicationContext = thymol.makeContext("application", accessor);
195         this.sessionContext = thymol.makeContext("session", undefined);
196         this.sessionContext.persist = function() {
197             var save = this.serialise();
198             thymol.thTop.name = save;
199         };
200         this.requestContext = thymol.makeContext("request", undefined);
201         this.booleanAndNullTokens = new Array();
202         this.booleanAndNullTokens["null"] = this.applicationContext.createVariable("null", null);
203         this.booleanAndNullTokens["true"] = this.applicationContext.createVariable("true", true);
204         this.booleanAndNullTokens["false"] = this.applicationContext.createVariable("false", false);
205         this.allowNullText = null;
206         setupEnv();
207         this.thCache = {};
208         this.thExpressionObjects;
209         this.thDeferredFunctions;
210         this.thPreExecutionFunctions;
211         this.thPostExecutionFunctions;
212         if (typeof this.thExpressionObjects === "undefined" || this.thExpressionObjects === null) {
213             this.thExpressionObjects = {};
214         }
215         this.thExpressionObjects["#object"] = {};
216         this.thExpressionObjects["#locale"] = {};
217         this.thExpressionObjects["#ctx"] = [];
218         this.thExpressionObjects["#ctx"]["variables"] = {};
219         thymol.configureModule(thymol.objects.thHttpServletRequestObject);
220         thymol.configureModule(thymol.objects.thHttpSessionObject);
221         if (typeof thymol.thObjectsConfigureModules !== "undefined") {
222             thymol.thObjectsConfigureModules();
223         }
224         var scripts = thymol.thDocument.getElementsByTagName("script");
225         for (var i = 0, iLimit = scripts.length; i < iLimit; i++) {
226             var parameters = scripts[i].getAttribute("data-thymol-load");
227             if (!!parameters) {
228                 var splits = parameters.split(",");
229                 for (var j = 0, jLimit = splits.length; j < jLimit; j++) {
230                     thymol.ThUtils.loadScript(splits[j]);
231                 }
232             }
233         }
234         setupEnv();
235         if (typeof thymol.thPreExecutionFunctions === "undefined" || thymol.thPreExecutionFunctions === null) {
236             thymol.thPreExecutionFunctions = [];
237         }
238         if (typeof thymol.thPostExecutionFunctions === "undefined" || thymol.thPostExecutionFunctions === null) {
239             thymol.thPostExecutionFunctions = [];
240         }
241         $.ajaxSetup({
242             async: false,
243             isLocal: true,
244             dataType: "text"
245         });
246         if (!(typeof thVars === "undefined")) {
247             for (i = 0, iLimit = thVars.length; i < iLimit; i++) {
248                 this.applicationContext.createVariable(thVars[i][0], thVars[i][1]);
249             }
250         }
251         executeDeferred();
252         (function() {
253             var htmlTagAttrs = $("html")[0].attributes, tp = null, tu, nsspec;
254             $([ thymol.thURL, thymol.thAltURL ]).each(function() {
255                 tu = this;
256                 $(htmlTagAttrs).each(function() {
257                     if (this.value == tu) {
258                         nsspec = this.localName.split(":");
259                         if (nsspec.length > 0) {
260                             tp = nsspec[nsspec.length - 1];
261                             return false;
262                         }
263                     }
264                 });
265                 if (tp) {
266                     thymol.updatePrefix(tp);
267                     return false;
268                 }
269             });
270         })();
271         var defaultScriptUrl = "";
272         if (!!thymol.thRequest) {
273             thymol.thWindow.location.search = thymol.thRequest;
274         }
275         (function(app, req) {
276             var e, f, a = /\+/g, r = /([^&=]+)=?([^&]*)/g, d = function(s) {
277                 return decodeURIComponent(s.replace(a, " "));
278             }, q = thymol.thWindow.location.search.substring(1), surl, scriptUrl = defaultScriptUrl;
279             $("script").each(function() {
280                 surl = this.src;
281                 if (surl.indexOf(thymol.thScriptName) >= 0) {
282                     scriptUrl = d(surl);
283                     return false;
284                 }
285             });
286             while (e = r.exec(scriptUrl)) {
287                 f = e[1].split("?");
288                 switch (f[1]) {
289                   case "thPrefix":
290                     thymol.prefix = e[2];
291                     break;
292
293                   case "thDataPrefix":
294                     thymol.dataPrefix = e[2];
295                     break;
296
297                   case "thMessagePath":
298                     thymol.messagePath = e[2];
299                     break;
300
301                   case "thResourcePath":
302                     thymol.resourcePath = e[2];
303                     break;
304
305                   case "thMessagesBaseName":
306                     thymol.messagesBaseName = e[2];
307                     break;
308
309                   case "thRelativeRootPath":
310                     thymol.relativeRootPath = e[2];
311                     break;
312
313                   case "thExtendedMapping":
314                     thymol.extendedMapping = e[2];
315                     break;
316
317                   case "thTemplateSuffix":
318                     thymol.templateSuffix = e[2];
319                     break;
320
321                   case "thLocalMessages":
322                     thymol.localMessages = e[2];
323                     break;
324
325                   case "thDisableMessages":
326                     thymol.disableMessages = e[2];
327                     break;
328
329                   case "thIndexFile":
330                     thymol.indexFile = e[2];
331                     break;
332
333                   case "thProtocol":
334                     thymol.protocol = e[2];
335                     break;
336
337                   case "thDebug":
338                     thymol.debug = e[2];
339                     break;
340
341                   case "thRoot":
342                     thymol.root = e[2];
343                     break;
344
345                   case "thPath":
346                     thymol.path = e[2];
347                     break;
348
349                   case "thAllowNullText":
350                     thymol.allowNullText = e[2];
351                     break;
352
353                   case "thLocale":
354                     thymol.locale.value = e[2];
355                     break;
356
357                   case "thDefaultPrecision":
358                     thymol.thDefaultPrecision = e[2];
359                     break;
360
361                   case "thDefaultPrecedence":
362                     thymol.thDefaultPrecedence = e[2];
363                     break;
364
365                   default:
366                     app.createVariable(e[1], e[2]);
367                 }
368             }
369             while (e = r.exec(q)) {
370                 req.createVariable(d(e[1]), e[2], true);
371             }
372         })(this.applicationContext, this.requestContext);
373         thymol.thInclude = new thymol.ThAttr("include", null, 100, null, thymol.prefix);
374         thymol.thReplace = new thymol.ThAttr("replace", null, 100, null, thymol.prefix);
375         thymol.thSubstituteby = new thymol.ThAttr("substituteby", null, 100, null, thymol.prefix);
376         thymol.thFragment = new thymol.ThAttr("fragment", null, 2e4, null, thymol.prefix);
377         thymol.thRemove = null;
378         thymol.thBlock = new thymol.ThElement("block", function(element) {
379             var i, limit = element.childNodes.length;
380             for (i = 0; i < limit; i++) {
381                 if (element.childNodes[i].nodeType === 1) {
382                     element.childNodes[i].isBlockChild = true;
383                 }
384             }
385         }, thymol.prefix);
386         this.applicationContext.resolveJSONReferences();
387         thymol.setupAttrList();
388         preExecute(this.applicationContext);
389         this.thExpressionObjects["#vars"] = this.applicationContext;
390         this.thExpressionObjects["#root"] = this.applicationContext;
391         this.sessionContext.init();
392         this.sessionContext.resolveJSONReferences();
393         this.requestContext.resolveJSONReferences();
394         this.thExpressionObjects["#ctx"]["variables"] = this.applicationContext;
395         this.thExpressionObjects["#ctx"]["requestParameters"] = this.requestContext;
396         this.thExpressionObjects["#ctx"]["servletContext"] = this.applicationContext;
397         this.thExpressionObjects["#ctx"]["httpServletRequest"] = this.thExpressionObjects["#httpServletRequest"];
398         this.thExpressionObjects["#ctx"]["httpSession"] = this.thExpressionObjects["#httpSession"];
399         this.protocol = Thymol.prototype.override("thProtocol", this.protocol);
400         this.debug = Thymol.prototype.override("thDebug", this.debug);
401         this.root = Thymol.prototype.override("thRoot", this.root);
402         if ("" !== this.relativeRootPath) {
403             var rootURI = thymol.thDocument.location.href;
404             var quePos = rootURI.indexOf("?");
405             if (quePos >= 0) {
406                 rootURI = rootURI.substring(0, quePos);
407             }
408             var sepPos = rootURI.lastIndexOf("/");
409             if (sepPos >= 0) {
410                 rootURI = rootURI.substring(0, sepPos + 1);
411             }
412             var newThRoot = rootURI + this.thLocation + this.relativeRootPath;
413             this.thRoot = Thymol.prototype.getThParam("thRoot", false, true, newThRoot);
414         }
415         this.path = Thymol.prototype.override("thPath", this.path);
416         this.allowNullText = Thymol.prototype.override("thAllowNullText", this.allowNullText);
417         this.locale.value = Thymol.prototype.override("thLocale", this.locale.value);
418         if (!(typeof thMappings === "undefined")) {
419             this.mappings = [];
420             for (j = 0, jLimit = thMappings.length; j < jLimit; j++) {
421                 this.mappings.push([ thMappings[j][0], thMappings[j][1] ]);
422             }
423             this.mappings.sort(function(a, b) {
424                 return a[0].length > b[0].length ? -1 : 1;
425             });
426         }
427         this.messages = {};
428         setLocaleValue();
429         if (!(typeof thMessages === "undefined")) {
430             this.messages[""] = [];
431             for (j = 0, jLimit = thMessages.length; j < jLimit; j++) {
432                 this.messages[""][thMessages[j][0]] = thMessages[j][1];
433             }
434             for (var k in thMessages) {
435                 if (thMessages.hasOwnProperty(k)) {
436                     if (!k.match(numericExpr)) {
437                         this.messages[k] = [];
438                         for (j = 0, jLimit = thMessages[k].length; j < jLimit; j++) {
439                             this.messages[k][thMessages[k][j][0]] = thMessages[k][j][1];
440                         }
441                     }
442                 }
443             }
444         }
445         if (!(typeof thDisable === "undefined")) {
446             for (j = 0, jLimit = thDisable.length; j < jLimit; j++) {
447                 Thymol.prototype.doDisable(thDisable[j]);
448             }
449         }
450         thymol.thRemove = Thymol.prototype.getThAttrByName("remove");
451     }
452     function getLocations(thiz) {
453         thiz.templateName = "";
454         thiz.templatePath = "";
455         if (!!thymol.thDocument.location.href) {
456             var templateName = templatePath = thymol.thDocument.location.href;
457             thiz.templateName = templateName.substring(0, templateName.indexOf(".") == -1 ? templateName.length : templateName.lastIndexOf("."));
458             thiz.templatePath = templatePath.substring(0, templatePath.indexOf("/") == -1 ? 0 : templatePath.lastIndexOf("/") + 1);
459         }
460     }
461     function getCtx() {
462         return thymol.thExpressionObjects["#ctx"];
463     }
464     function configureModule(module) {
465         if (typeof thymol.thExpressionObjects === "undefined" || thymol.thExpressionObjects === null) {
466             thymol.thExpressionObjects = {};
467         }
468         thymol.thExpressionObjects[module.thExpressionObjectName] = module;
469     }
470     function configureAttributeProcessor(prefix, suffix, func, prec, dataAttr) {
471         var p = prefix + ":";
472         if (p !== null) {
473             if (thymol.thThymeleafPrefixList.indexOf(p) < 0) {
474                 thymol.thThymeleafPrefixList.push(p);
475             }
476         } else {
477             thymol.thUsingNullPrefix = true;
478         }
479         p = new thymol.ThAttr(suffix, func, prec, thymol.thThymeleafPrefixList, prefix, dataAttr);
480     }
481     function configureElementProcessor(prefix, suffix, func) {
482         var p = new thymol.ThElement(suffix, func, prefix);
483     }
484     function configurePreExecution(func) {
485         if (typeof thymol.thPreExecutionFunctions === "undefined" || thymol.thPreExecutionFunctions === null) {
486             thymol.thPreExecutionFunctions = [];
487         }
488         thymol.thPreExecutionFunctions.push(func);
489     }
490     function configurePostExecution(func) {
491         if (typeof thymol.thPostExecutionFunctions === "undefined" || thymol.thPostExecutionFunctions === null) {
492             thymol.thPostExecutionFunctions = [];
493         }
494         thymol.thPostExecutionFunctions.push(func);
495     }
496     function executeDeferred() {
497         if (typeof thymolDeferredFunctions !== "undefined" && thymolDeferredFunctions !== null) {
498             while (thymolDeferredFunctions.length > 0) {
499                 var func = thymolDeferredFunctions.pop();
500                 func();
501             }
502         }
503     }
504     function preExecute(context) {
505         while (thymol.thPreExecutionFunctions.length > 0) {
506             var func = thymol.thPreExecutionFunctions.pop();
507             func();
508             context.resolveJSONReferences();
509         }
510     }
511     function postExecute() {
512         while (thymol.thPostExecutionFunctions.length > 0) {
513             var func = thymol.thPostExecutionFunctions.pop();
514             func();
515         }
516     }
517     function preProcess(expr, element) {
518         var result = expr, fp, lp;
519         do {
520             fp = result.indexOf("__");
521             if (fp >= 0) {
522                 lp = -1;
523                 if (result.length > 4) {
524                     lp = result.lastIndexOf("__");
525                 }
526                 if (lp <= 0) {
527                     throw new thymol.ThError("Mismatched pre-processing indicators", element);
528                 }
529                 var head = result.substring(0, fp);
530                 var centre = result.substring(fp + 2, lp);
531                 centre = this.getParsedExpr(centre, element);
532                 var tail = result.substring(lp + 2);
533                 result = head + centre + tail;
534                 fp = result.indexOf("__");
535             }
536         } while (fp >= 0);
537         result = result.replace(/\\_\\_/g, "__");
538         return result;
539     }
540     function substituteParam(argValue, mode, element) {
541         var result = argValue, varName = argValue, subs = null, msg, expo;
542         if (result) {
543             if (mode === 4) {
544                 msg = thymol.getMessage(varName);
545                 if (msg) {
546                     subs = msg;
547                 }
548             } else if (mode === 6) {
549                 subs = argValue;
550             } else {
551                 var token = thymol.booleanAndNullTokens[result];
552                 if (!(typeof token === "undefined")) {
553                     if (token === null) {
554                         subs = null;
555                     } else {
556                         subs = token.value;
557                     }
558                 } else {
559                     if (varName.charAt(0) === "#") {
560                         if ("#object" === varName) {
561                             if (element.thObjectVar) {
562                                 subs = element.thObjectVar;
563                             }
564                         } else {
565                             expo = thymol.thExpressionObjects[varName];
566                             if (typeof expo !== "undefined" && expo !== null) {
567                                 subs = expo;
568                             }
569                         }
570                     }
571                     if ((typeof subs === "undefined" || subs == null) && element.thObjectVar) {
572                         subs = element.thObjectVar[varName];
573                     }
574                     if ((typeof subs === "undefined" || subs == null) && element.thLocalVars) {
575                         subs = element.thLocalVars[varName];
576                     }
577                     if (typeof subs === "undefined" || subs == null) {
578                         subs = thymol.ThUtils.getParameter(varName);
579                     }
580                     if (typeof subs === "undefined" || subs == null) {
581                         if ("param" === varName) {
582                             subs = thymol.requestContext;
583                         }
584                         if ("session" === varName) {
585                             subs = thymol.sessionContext;
586                         }
587                         if ("application" === varName) {
588                             subs = thymol.applicationContext;
589                         }
590                     }
591                     if (mode === 2 && (typeof subs === "undefined" || subs == null)) {
592                         subs = argValue;
593                     }
594                 }
595             }
596             result = subs;
597             if (subs instanceof thymol.ThParam) {
598                 result = subs.value;
599             }
600         }
601         return result;
602     }
603     function getStandardURL(initial) {
604         var result = initial.trim(), mapped, head;
605         mapped = thymol.getMapped(result, thymol.extendedMapping);
606         if (mapped) {
607             result = mapped;
608         }
609         if ("/" === result && !!thymol.indexFile) {
610             result += thymol.indexFile;
611         }
612         if (!/.*:\/\/.*/.test(result)) {
613             if (/^~?\/.*$/.test(result)) {
614                 if (/^~.*$/.test(result)) {
615                     result = result.substring(1);
616                 }
617                 if (!/^\/\/.*$/.test(result)) {
618                     if (thymol.useFullURLPath) {
619                         head = thymol.root + thymol.resourcePath;
620                         if (head != "") {
621                             if (head.charAt(head.length - 1) !== "/") {
622                                 head = head + "/";
623                             }
624                             if (result.charAt(0) === "/") {
625                                 result = head + result.substring(1);
626                             } else {
627                                 result = head + result;
628                             }
629                         }
630                     } else {
631                         result = thymol.resourcePath + result;
632                     }
633                 }
634             }
635         }
636         return result;
637     }
638     function getExpression(argValue, element) {
639         var result = argValue, subst = false, initial, shortCut, args, negate, token, lsp;
640         if (typeof argValue === "string") {
641             initial = argValue.trim();
642             result = initial;
643             if (result) {
644                 shortCut = thymol.ThUtils.getParameter(result);
645                 if (!shortCut) {
646                     args = result.match(varExpr3);
647                     if (args) {
648                         if (args[1] && args[1].length > 0) {
649                             shortCut = thymol.ThUtils.getParameter(args[1]);
650                         }
651                     }
652                 }
653                 if (shortCut) {
654                     if (shortCut instanceof thymol.ThParam) {
655                         result = shortCut.value;
656                     } else {
657                         result = shortCut;
658                     }
659                     if (typeof result === "string" && result.match(numericExpr)) {
660                         result = parseInt(result);
661                     }
662                 } else {
663                     initial = thymol.ThUtils.unParenthesise(result);
664                     negate = false;
665                     if (initial.charAt(0) == "!") {
666                         negate = true;
667                         initial = initial.substring(1, initial.length);
668                         initial = thymol.ThUtils.unParenthesise(initial);
669                     }
670                     if (literalTokenExpr.test(initial)) {
671                         token = thymol.booleanAndNullTokens[initial];
672                         if (!(typeof token === "undefined")) {
673                             result = token.value;
674                             subst = true;
675                         }
676                     }
677                     lsp = null;
678                     if (!subst) {
679                         lsp = initial.match(litSubstExpr);
680                         if (lsp && lsp.length > 0) {
681                             if (thymol.ThUtils.charOcurrences(lsp[1], "'") < 2) {
682                                 initial = Thymol.prototype.doLiteralSubstExpr(initial, lsp[1]);
683                             }
684                         }
685                         result = "";
686                         if (initial != "") {
687                             initial = thymol.ThUtils.unParenthesise(initial);
688                             initial = thymol.preProcess(initial, element);
689                             result = thymol.getParsedExpr(initial, element, true);
690                         }
691                     }
692                     if (result == initial && typeof result == typeof initial) {
693                         result = null;
694                     } else if (typeof result === "string") {
695                         if (!lsp) {
696                             result = result.replace(/[\\][\\]/g, "\\");
697                         }
698                         result = result.replace(/&#39;/g, "'").replace(/&apos;/gi, "'");
699                     }
700                     if (negate) {
701                         if (typeof result === "boolean") {
702                             result = !result;
703                         } else if (typeof result === "number") {
704                             result = result == 0;
705                         } else if (typeof result === "string") {
706                             result = !thymol.ThUtils.testLiteralFalse(result);
707                         }
708                     }
709                 }
710             }
711         }
712         return result;
713     }
714     function getMapped(uri, extended) {
715         var mapped = null, i, iLimit, key;
716         if (uri && typeof uri === "string") {
717             if (thymol.mappings) {
718                 for (i = 0, iLimit = thymol.mappings.length; i < iLimit; i++) {
719                     key = thymol.mappings[i][0];
720                     if (uri == key) {
721                         mapped = thymol.mappings[i][1];
722                         break;
723                     } else if (extended) {
724                         if (uri.indexOf(key) == 0) {
725                             mapped = uri.substring(key.length);
726                             mapped = thymol.mappings[i][1] + mapped;
727                             break;
728                         }
729                     }
730                 }
731             }
732         }
733         return mapped;
734     }
735     function substitute(initial, element, lenient) {
736         var argValue = initial, result, args, token, re, subs, saved;
737         if (typeof argValue === "string") {
738             argValue = argValue.trim();
739         }
740         result = argValue;
741         args = "";
742         while (args != null) {
743             args = argValue.match(/.*([$\*#@]{(!?[^}]*)}).*/);
744             if (args != null && args.length > 0) {
745                 if (args.length == 3) {
746                     token = args[1];
747                     token = token.replace(/[$]/g, "[$]").replace(/[*]/g, "[*]").replace(/[\']/g, "[']").replace(/[+]/g, "[+]").replace(/[\(]/g, "[(]").replace(/[\)]/g, "[)]");
748                     re = new RegExp(token);
749                     subs = this.getExpression(args[2], element);
750                     if (subs != args[2]) {
751                         result = result.replace(re, subs, "g");
752                         if (result == "null") {
753                             result = null;
754                         }
755                     } else {
756                         subs = "";
757                         if (thymol.debug && !lenient) {
758                             thymol.thWindow.alert('thymol variable substitution failed: "' + initial + '"');
759                         }
760                     }
761                     saved = argValue;
762                     argValue = argValue.replace(re, subs, "g");
763                     if (saved == argValue) {
764                         argValue = "";
765                     }
766                 }
767             }
768         }
769         return result;
770     }
771     function getWith(element, content) {
772         var argValue = content.trim(), argCount = 0;
773         if (argValue) {
774             do {
775                 var argsExpr = thymol.ThParser.parse(argValue, true, false);
776                 var identifier = argsExpr.tokens.shift();
777                 if (identifier.type_ === 3) {
778                     var result = argsExpr.evaluate(element);
779                     var varName = identifier.index_;
780                     if (!!varName) {
781                         argCount++;
782                         if (!element.thLocalVars) {
783                             element.thLocalVars = {};
784                         }
785                         element.thLocalVars[varName] = result;
786                     }
787                     argValue = argValue.substring(argsExpr.position);
788                 } else {
789                     break;
790                 }
791             } while (argValue.length > 0);
792         }
793         return argCount;
794     }
795     function getParsedExpr(initial, element, preprocessed) {
796         var expr, result = initial;
797         expr = thymol.ThParser.parse(result, false, preprocessed);
798         expr = expr.simplify();
799         result = expr.evaluate(element);
800         if (typeof result === "number") {
801             result = thymol.ThUtils.getToPrecision(result, expr.precision);
802         }
803         return result;
804     }
805     function getBooleanValue(param) {
806         var flag = false, val, args;
807         if (param != null) {
808             if (typeof param === "boolean") {
809                 flag = param;
810             } else if (typeof param === "number") {
811                 flag = param != 0;
812             } else {
813                 val = param;
814                 if (Object.prototype.toString.call(val) === "[object Array]") {
815                     if (val.length === 1) {
816                         val = val[0];
817                     } else {
818                         val = true;
819                     }
820                 }
821                 if (typeof val === "boolean") {
822                     flag = val;
823                 } else if (typeof val === "number") {
824                     flag = val != 0;
825                 } else if (typeof val === "string") {
826                     args = val.match(nonURLExpr);
827                     if (args) {
828                         val = args[1];
829                         flag = this.testParam(val);
830                     } else {
831                         flag = !thymol.ThUtils.testLiteralFalse(val);
832                     }
833                 } else if (val instanceof thymol.ThParam) {
834                     flag = val.getBooleanValue();
835                 } else {
836                     flag = typeof val !== "undefined" && val !== null;
837                 }
838             }
839         }
840         return flag;
841     }
842     function isFragmentChild(element) {
843         var result = false, parent = element.parentElement;
844         while (parent) {
845             if (parent.getAttribute(thymol.thFragment.name) || parent.getAttribute(thymol.thFragment.synonym)) {
846                 result = true;
847                 break;
848             }
849             parent = parent.parentElement;
850         }
851         return result;
852     }
853     function setLocale(locValue) {
854         thymol.locale.value = locValue;
855         setLocaleValue();
856     }
857     function getLocale() {
858         return thymol.locale.value;
859     }
860     function getLanguage() {
861         if (!thymol.locale.value) {
862             if (typeof navigator !== "undefined" && !!navigator) {
863                 var userLang = navigator.language || navigator.userLanguage || navigator.browserLanguage || navigator.systemLanguage;
864                 if (!!userLang) {
865                     thymol.locale.value = userLang.replace(/\-/g, "_");
866                 }
867             }
868         }
869     }
870     function setLocaleValue() {
871         if (!thymol.locale.value) {
872             thymol.locale.value = thymol.thDefaultLocale;
873         }
874         var sepPos;
875         var locale = thymol.locale.value.replace(/\-/g, "_");
876         var level = thymol.locale.value;
877         var levels = [];
878         var part, parts = [];
879         do {
880             levels.push(level);
881             sepPos = locale.lastIndexOf("_");
882             if (sepPos >= 0) {
883                 part = locale.substring(sepPos + 1);
884                 parts.push(part);
885                 locale = locale.substring(0, sepPos);
886                 level = level.substring(0, sepPos);
887             }
888         } while (sepPos >= 0);
889         thymol.locale.language = level;
890         if (!!parts) {
891             parts.reverse();
892             for (var i = 0, iLimit = parts.length; i < iLimit; i++) {
893                 if (i === 0) {
894                     thymol.locale.country = parts[i];
895                 } else if (i === 1) {
896                     thymol.locale.variant = parts[i];
897                 }
898             }
899         }
900         thymol.locale.levels = levels;
901         thymol.thExpressionObjects["#ctx"]["locale"] = thymol.locale;
902         thymol.thExpressionObjects["#locale"] = thymol.locale;
903     }
904     function getMessage(varName, parameters, returnStringAlways) {
905         if (thymol.disableMessages) {
906             return undefined;
907         }
908         var msgKey = null;
909         var locale;
910         if (!!thymol.locale.levels) {
911             var prefix = "$";
912             var ident, section, jLower = thymol.localMessages ? 0 : 1;
913             for (var j = jLower; j < 2; j++) {
914                 for (var i = 0, iLimit = thymol.locale.levels.length; i < iLimit + 1; i++) {
915                     ident = prefix;
916                     if (i < iLimit) {
917                         locale = thymol.locale.levels[i];
918                     } else {
919                         locale = "";
920                     }
921                     ident = ident + locale;
922                     section = thymol.messages[ident];
923                     if (!section) {
924                         if (j < 1) {
925                             section = getLocalMessages(locale);
926                         } else {
927                             section = getDefaultMessages(locale);
928                         }
929                     }
930                     if (!!section) {
931                         thymol.messages[ident] = section;
932                         msgKey = section[varName];
933                         if (!!msgKey) {
934                             break;
935                         }
936                     }
937                 }
938                 if (!!msgKey) {
939                     break;
940                 }
941                 prefix += "$";
942             }
943         }
944         if (!msgKey) {
945             for (var i = 0, iLimit = thymol.locale.levels.length; i <= iLimit; i++) {
946                 if (i < iLimit) {
947                     locale = thymol.locale.levels[i];
948                 } else {
949                     locale = "";
950                 }
951                 if (!!thymol.messages[locale]) {
952                     msgKey = thymol.messages[locale][varName];
953                     if (!!msgKey) {
954                         break;
955                     }
956                 }
957             }
958         }
959         if (!!msgKey) {
960             if (typeof parameters === "undefined") {
961                 return msgKey;
962             } else {
963                 return thymol.ThUtils.renderMessage(msgKey, parameters);
964             }
965         } else if (returnStringAlways !== undefined && returnStringAlways) {
966             return "??" + varName + "_" + thymol.locale.value + "??";
967         }
968         return null;
969     }
970     function getProperties(propFile) {
971         var props = null;
972         var messages = [];
973         $.get(propFile, function(textContent, status) {
974             var err = null;
975             try {
976                 if ("success" == status) {
977                     props = textContent;
978                 } else if (thymol.debug) {
979                     thymol.thWindow.alert("read failed: " + propFile);
980                 }
981             } catch (err) {
982                 if (thymol.debug) {
983                     thymol.thWindow.alert("properties file read failed: " + propFile + " error: " + err);
984                 }
985             }
986         }, "text");
987         if (props !== null) {
988             var splits = props.split("\n");
989             if (splits.length > 0) {
990                 for (var i = 0, iLimit = splits.length; i < iLimit; i++) {
991                     var line = splits[i].trim();
992                     if (line.charAt(0) !== "#") {
993                         var p = line.split("=");
994                         if (p.length > 1) {
995                             messages[p[0].trim()] = thymol.ThUtils.unicodeUnescape(p[1].trim());
996                         }
997                     }
998                 }
999             }
1000         }
1001         return messages;
1002     }
1003     function getLocalMessages(locale) {
1004         var messages = [];
1005         if (!!thymol.thDocument.location.href) {
1006             var propsFile = thymol.templateName;
1007             if (!!locale && locale !== "") {
1008                 propsFile += "_" + locale;
1009             }
1010             propsFile += ".properties";
1011             messages = getProperties(propsFile);
1012         }
1013         return messages;
1014     }
1015     function getDefaultMessages(locale) {
1016         var messages = null;
1017         var propsPath = "";
1018         if (thymol.useAbsolutePath) {
1019             propsPath += thymol.protocol + thymol.root + thymol.path;
1020         }
1021         propsPath += thymol.messagePath;
1022         if (propsPath !== "") {
1023             propsPath += "/";
1024         }
1025         var propsFile = propsPath + thymol.messagesBaseName;
1026         if (!!locale && locale !== "") {
1027             propsFile += "_" + locale;
1028         }
1029         propsFile += ".properties";
1030         messages = getProperties(propsFile);
1031         return messages;
1032     }
1033     Thymol.prototype = {
1034         process: function(rootNode) {
1035             var n = rootNode;
1036             try {
1037                 while (n.thDoc) {
1038                     this.getChildren(n);
1039                     if (n.firstChild && n.firstChild.thDoc && !n.visited) {
1040                         n.visited = true;
1041                         n = n.firstChild;
1042                     } else {
1043                         if (n.element != n.thDoc) {
1044                             this.doReplace(n.isNode, n.element, n.thDoc);
1045                             if (!n.isNode) {
1046                                 n.thDoc = n.element;
1047                             }
1048                         }
1049                         if (n.nextSibling && n.nextSibling.thDoc) {
1050                             n = n.nextSibling;
1051                         } else {
1052                             if (n == rootNode) {
1053                                 break;
1054                             }
1055                             n = n.parentDoc;
1056                         }
1057                     }
1058                 }
1059                 this.processChildren(rootNode);
1060             } catch (err) {
1061                 if (thymol.debug) {
1062                     if (err instanceof thymol.ThError) {
1063                         if (!err.suppress) {
1064                             thymol.thWindow.alert(err);
1065                         }
1066                     } else {
1067                         thymol.thWindow.alert(err);
1068                     }
1069                 }
1070             }
1071         },
1072         getChildren: function(rootNode) {
1073             var count = 0, last = null, changed = false, child, froot, fstar, fchildren, i, iLimit, j, jLimit, element, matches, theAttr;
1074             if (!rootNode.visited) {
1075                 this.processComments(rootNode);
1076                 var rnd = this.getContentRoot(rootNode);
1077                 froot = $(rnd);
1078                 fstar = $(froot).add(froot.find("*"));
1079                 fchildren = fstar.filter(thymol.thInclude.escpName).add(fstar.filter(thymol.thInclude.escpSynonym)).add(fstar.filter(thymol.thReplace.escpName)).add(fstar.filter(thymol.thReplace.escpSynonym)).add(fstar.filter(thymol.thSubstituteby.escpName)).add(fstar.filter(thymol.thSubstituteby.escpSynonym));
1080                 for (i = 0, iLimit = fchildren.length; i < iLimit; i++) {
1081                     element = fchildren[i], matches = [];
1082                     for (j = 0, jLimit = element.attributes.length; j < jLimit; j++) {
1083                         theAttr = element.attributes[j];
1084                         if (thymol.thInclude.name == theAttr.name || thymol.thInclude.synonym == theAttr.name || thymol.thReplace.name == theAttr.name || thymol.thReplace.synonym == theAttr.name || thymol.thSubstituteby.name == theAttr.name || thymol.thSubstituteby.synonym == theAttr.name) {
1085                             matches.push(theAttr);
1086                         }
1087                     }
1088                     for (j = 0, jLimit = matches.length; j < jLimit; j++) {
1089                         child = this.processImport(element, rootNode, matches[j]);
1090                         if (child != null) {
1091                             changed = true;
1092                             if (count == 0) {
1093                                 rootNode.firstChild = child;
1094                             } else {
1095                                 last.nextSibling = child;
1096                             }
1097                             last = child;
1098                             count++;
1099                         }
1100                     }
1101                 }
1102             }
1103             return changed;
1104         },
1105         processChildren: function(rootNode) {
1106             var i, iLimit, j, jLimit, k, kLimit;
1107             var elements = rootNode.thDoc.getElementsByTagName("*");
1108             for (k = 0, kLimit = elements.length; k < kLimit; k++) {
1109                 var element = elements[k];
1110                 for (j = 0, jLimit = thymol.thThymeleafElementsList.length; j < jLimit; j++) {
1111                     if (element.localName == thymol.thThymeleafElementsList[j].name || element.localName == thymol.thThymeleafElementsList[j].synonym) {
1112                         var updated = thymol.thThymeleafElementsList[j].process(element);
1113                         if (updated) {
1114                             elements = rootNode.thDoc.getElementsByTagName("*");
1115                             k--;
1116                             kLimit = elements.length;
1117                         }
1118                         break;
1119                     }
1120                 }
1121                 var allAttributes = element.attributes;
1122                 if (allAttributes && allAttributes.length > 0) {
1123                     var attributes = [], aii = 0;
1124                     if (!thymol.thUsingNullPrefix) {
1125                         for (i = 0, iLimit = allAttributes.length; i < iLimit; i++) {
1126                             var ai = allAttributes[i];
1127                             if (ai) {
1128                                 for (j = 0, jLimit = thymol.thThymeleafPrefixList.length; j < jLimit; j++) {
1129                                     var attrName = ai.name.toString();
1130                                     if (attrName.length > thymol.thThymeleafPrefixList[j].length) {
1131                                         attrName = attrName.substring(0, thymol.thThymeleafPrefixList[j].length);
1132                                         if (attrName === thymol.thThymeleafPrefixList[j]) {
1133                                             ai.order = i;
1134                                             attributes[aii++] = ai;
1135                                         }
1136                                     }
1137                                 }
1138                             }
1139                         }
1140                     } else {
1141                         attributes = allAttributes;
1142                     }
1143                     if (attributes.length > 0) {
1144                         attributes.sort(function(a, b) {
1145                             return b.order - a.order;
1146                         });
1147                         var matchedAttributes = [];
1148                         for (i = 0, iLimit = attributes.length; i < iLimit; i++) {
1149                             var splits = attributes[i].name.toString().split(":");
1150                             if (splits && splits.length > 0) {
1151                                 var prefix = "", name;
1152                                 if (splits.length > 1) {
1153                                     prefix = splits[0];
1154                                     name = splits[1];
1155                                 } else {
1156                                     name = splits[0];
1157                                     var hpos = name.lastIndexOf("-");
1158                                     if (hpos >= 0) {
1159                                         prefix = name.substring(0, hpos + 1);
1160                                     }
1161                                 }
1162                                 var attrList = thymol.thThymeleafPrefixList[prefix];
1163                                 if (splits.length > 1) {
1164                                     prefix += ":";
1165                                 }
1166                                 if (attrList) {
1167                                     for (j = 0, jLimit = attrList.length; j < jLimit; j++) {
1168                                         var matched = false;
1169                                         if (name === attrList[j].suffix || name === attrList[j].synonym) {
1170                                             matched = true;
1171                                         } else if (attrList[j].regex !== null) {
1172                                             var fqn = prefix + name;
1173                                             matched = attrList[j].regex.test(fqn);
1174                                         }
1175                                         if (matched) {
1176                                             var matchedAttribute = {};
1177                                             matchedAttribute.attr = attrList[j];
1178                                             matchedAttribute.elementAttr = attributes[i];
1179                                             matchedAttributes.push(matchedAttribute);
1180                                             break;
1181                                         }
1182                                     }
1183                                 }
1184                             }
1185                         }
1186                         if (matchedAttributes.length > 0) {
1187                             matchedAttributes.sort(function(a, b) {
1188                                 return a.attr.precedence - b.attr.precedence;
1189                             });
1190                             var updated = false;
1191                             for (i = 0, iLimit = matchedAttributes.length; i < iLimit; i++) {
1192                                 var exp = thymol.ThUtils.processElement(matchedAttributes[i].attr.process, element, matchedAttributes[i].elementAttr, matchedAttributes[i].attr, 1);
1193                                 updated = exp || updated;
1194                             }
1195                             if (updated) {
1196                                 elements = rootNode.thDoc.getElementsByTagName("*");
1197                                 k--;
1198                                 kLimit = elements.length;
1199                             }
1200                         }
1201                     }
1202                 }
1203             }
1204             elements = rootNode.thDoc.getElementsByTagName("*");
1205             var kc = 0;
1206             for (k = 0, kLimit = elements.length; k < kLimit; k++) {
1207                 var element = elements[kc];
1208                 var elName = element.nodeName.toLowerCase();
1209                 if (elName == thymol.thBlock.name || elName == thymol.thBlock.synonym) {
1210                     thymol.ThUtils.removeTag(element);
1211                     elements = rootNode.thDoc.getElementsByTagName("*");
1212                 } else {
1213                     kc++;
1214                 }
1215             }
1216         },
1217         override: function(paramName, paramValue) {
1218             var param = paramValue, thv;
1219             thv = thymol.thWindow[paramName];
1220             if (typeof thv === "undefined") {
1221                 thv = thymol.applicationContext.javascriptify(paramName);
1222             }
1223             if (thv) {
1224                 if (thv instanceof thymol.ThParam) {
1225                     param = thv.value;
1226                 } else {
1227                     param = thv;
1228                 }
1229             }
1230             thv = thymol.applicationContext[paramName];
1231             if (thv) {
1232                 if (thv instanceof thymol.ThParam) {
1233                     param = thv.value;
1234                 } else {
1235                     param = thv;
1236                 }
1237             }
1238             thv = thymol.requestContext[paramName];
1239             if (thv) {
1240                 if (thv instanceof thymol.ThParam) {
1241                     param = thv.value;
1242                 } else {
1243                     param = thv;
1244                 }
1245             }
1246             return param;
1247         },
1248         doDisable: function(attrName) {
1249             var tha = this.getThAttrByName(attrName);
1250             if (tha !== null) {
1251                 tha.disable();
1252             } else {
1253                 if (thymol.debug) {
1254                     thymol.thWindow.alert('cannot disable unknown attribute "' + attrName + '"');
1255                 }
1256             }
1257         },
1258         getThAttrByName: function(name) {
1259             var attrList = thymol.thThymeleafPrefixList[thymol.prefix];
1260             attrList.push(thymol.thInclude);
1261             attrList.push(thymol.thReplace);
1262             attrList.push(thymol.thSubstituteby);
1263             attrList.push(thymol.thFragment);
1264             var i, iLimit = attrList.length;
1265             for (i = 0; i < iLimit; i++) {
1266                 if (name === attrList[i].suffix) {
1267                     return attrList[i];
1268                 }
1269             }
1270             return null;
1271         },
1272         getContents: function(rootNode) {
1273             var rnd = this.getContentRoot(rootNode);
1274             var froot = $(rnd);
1275             var fstar = froot.find("*");
1276             return fstar;
1277         },
1278         getContentRoot: function(rn) {
1279             var rnd = rn.thDoc;
1280             if (rnd.nodeName !== "#document") {
1281                 rnd = rnd.childNodes;
1282             }
1283             return rnd;
1284         },
1285         processComments: function(rootNode) {
1286             var comments = null, fstar, changed, i, iLimit, startComment, parent, startValue, pointer, nextPointer;
1287             do {
1288                 fstar = this.getContents(rootNode);
1289                 comments = fstar.contents().getComments();
1290                 changed = false;
1291                 for (i = 0, iLimit = comments.length; i < iLimit; i++) {
1292                     startComment = comments[i];
1293                     parent = startComment.parentNode;
1294                     startValue = startComment.nodeValue.trim();
1295                     if (startParserLevelCommentExpr.test(startValue)) {
1296                         pointer = startComment;
1297                         while (pointer != null) {
1298                             if (endParserLevelCommentExpr.test(pointer.nodeValue)) {
1299                                 changed = parent.removeChild(pointer) != null;
1300                                 break;
1301                             }
1302                             nextPointer = pointer.nextSibling;
1303                             changed = parent.removeChild(pointer) != null;
1304                             pointer = nextPointer;
1305                         }
1306                     } else if (startParserLevelCommentExpr2.test(startValue) && endParserLevelCommentExpr2.test(startValue)) {
1307                         parent.removeChild(startComment);
1308                         changed = true;
1309                     }
1310                 }
1311             } while (changed);
1312             this.processPrototypeOnlyComments(rootNode);
1313         },
1314         processPrototypeOnlyComments: function(rootNode) {
1315             var comments = null, fstar, changed, indexOfLast, i, iLimit, j, jLimit, k, kLimit, startComment, parent, deletions, res, fullText, innerNodes, done, next, commentText, res2, blockElement, blockDoc, blockDocBody, blockBase, newNode, newDoc;
1316             do {
1317                 fstar = this.getContents(rootNode);
1318                 comments = fstar.contents().getComments();
1319                 changed = false;
1320                 indexOfLast = comments.length - 1;
1321                 for (i = 0, iLimit = comments.length; i < iLimit; i++) {
1322                     startComment = comments[i];
1323                     parent = startComment.parentNode;
1324                     if (parent != null) {
1325                         startValue = startComment.nodeValue.trim();
1326                         deletions = [];
1327                         deletions.push(startComment);
1328                         startValue = startValue.replace(/\n/g, "");
1329                         res = startValue.match(prototypeOnlyCommentEscpExpr);
1330                         if (res) {
1331                             fullText = startValue;
1332                             if (parent.localName == "table" || parent.localName == "tbody") {
1333                                 if (startValue.indexOf(thymol.thBlock.name) >= 0 || startValue.indexOf(thymol.thBlock.synonym) >= 0) {
1334                                     if (startValue.indexOf(thymol.thBlock.endName) < 0 || startValue.indexOf(thymol.thBlock.endSynonym) < 0) {
1335                                         fullText = fullText.replace(res[0], res[1]);
1336                                         innerNodes = [];
1337                                         done = false;
1338                                         next = startComment;
1339                                         do {
1340                                             next = next.nextSibling;
1341                                             if (next != null) {
1342                                                 deletions.push(next);
1343                                                 if (i < indexOfLast) {
1344                                                     if (next == comments[i + 1]) {
1345                                                         commentText = next.nodeValue;
1346                                                         if (commentText.indexOf(thymol.thBlock.endName) >= 0 || commentText.indexOf(thymol.thBlock.endSynonym) >= 0) {
1347                                                             res2 = commentText.match(prototypeOnlyCommentEscpExpr);
1348                                                             if (res2) {
1349                                                                 commentText = commentText.replace(res2[0], res2[1]);
1350                                                                 fullText = fullText + commentText;
1351                                                             }
1352                                                             done = true;
1353                                                         }
1354                                                     } else {
1355                                                         innerNodes.push(next);
1356                                                     }
1357                                                 }
1358                                             } else {
1359                                                 done = true;
1360                                             }
1361                                         } while (!done);
1362                                         blockElement = null;
1363                                         blockDoc = new thymol.thDomParser().parseFromString(fullText, "text/html");
1364                                         blockDocBody = $(blockDoc).find("body")[0];
1365                                         for (j = 0, jLimit = blockDocBody.childNodes.length; j < jLimit; j++) {
1366                                             if (blockDocBody.childNodes[j].localName == thymol.thBlock.name || blockDocBody.childNodes[j].localName == thymol.thBlock.synonym) {
1367                                                 blockElement = blockDocBody.childNodes[j];
1368                                                 for (k = 0, kLimit = innerNodes.length; k < kLimit; k++) {
1369                                                     newNode = blockDoc.importNode(innerNodes[k], true);
1370                                                     blockElement.appendChild(newNode);
1371                                                 }
1372                                             }
1373                                         }
1374                                         if (blockElement != null) {
1375                                             blockBase = new ThNode(blockDoc, false, null, null, null, blockDoc.nodeName, "::", false, blockDoc);
1376                                             this.processChildren(blockBase);
1377                                             changed = this.insertUncommented(blockBase.thDoc, deletions, parent);
1378                                         } else {
1379                                             parent.removeChild(startComment);
1380                                             changed = true;
1381                                         }
1382                                     } else {
1383                                         parent.removeChild(startComment);
1384                                         changed = true;
1385                                     }
1386                                 }
1387                             } else {
1388                                 startValue = startValue.substring(3, startValue.length - 3);
1389                                 newDoc = new thymol.thDomParser().parseFromString(startValue, "text/html");
1390                                 changed = this.insertUncommented(newDoc, deletions, parent);
1391                             }
1392                         }
1393                     }
1394                 }
1395             } while (changed);
1396         },
1397         insertUncommented: function(doc, deletions, parent) {
1398             var docBody = $(doc).find("body")[0], i, iLimit, newNode;
1399             for (i = 0, iLimit = docBody.childNodes.length; i < iLimit; i++) {
1400                 if (parent.ownerDocument === doc) {
1401                     newNode = docBody.childNodes[i].cloneNode(true);
1402                 } else {
1403                     newNode = parent.ownerDocument.importNode(docBody.childNodes[i], true);
1404                     newNode.parentNode = parent;
1405                 }
1406                 parent.insertBefore(newNode, deletions[0]);
1407             }
1408             for (i = 0, iLimit = deletions.length; i < iLimit; i++) {
1409                 parent.removeChild(deletions[i]);
1410             }
1411             return true;
1412         },
1413         getList: function(element, content) {
1414             var argValue = content.trim(), argsCount = 0, argsList = [], assigs, i, iLimit, val;
1415             if (argValue) {
1416                 assigs = argValue.split(",");
1417                 for (i = 0, iLimit = assigs.length; i < iLimit; i++) {
1418                     val = thymol.getExpression(assigs[i], element);
1419                     argsList[i] = val;
1420                 }
1421                 if (!element.thLocalVars) {
1422                     element.thLocalVars = {};
1423                 }
1424                 element.thLocalVars["..."] = argsList;
1425                 argsCount = argsList.length;
1426             }
1427             return argsCount;
1428         },
1429         testParam: function(param) {
1430             var initial = param, result = false, theParam = null, negate = false;
1431             if (typeof initial === "boolean") {
1432                 result = initial;
1433             } else {
1434                 theParam = null;
1435                 negate = false;
1436                 if (typeof initial === "object" && initial instanceof thymol.ThParam) {
1437                     theParam = initial;
1438                 } else {
1439                     initial = initial.valueOf();
1440                     if (initial.charAt(0) == "!") {
1441                         negate = true;
1442                         initial = initial.substring(1);
1443                     }
1444                 }
1445                 theParam = thymol.applicationContext[initial];
1446                 if (theParam != null) {
1447                     result = theParam.getBooleanValue();
1448                 }
1449                 if (negate) {
1450                     result = !result;
1451                 }
1452             }
1453             return result ? true : false;
1454         },
1455         processImport: function(element, rootNode, attr) {
1456             var importNode = null, filePart, fragmentPart, names, parts, fragmentArgsList, isNode, fragment, fileName, content, importError;
1457             filePart = null;
1458             if (attr.value.indexOf("::") < 0) {
1459                 filePart = attr.value;
1460                 fragmentPart = "::";
1461             } else {
1462                 names = attr.value.split("::");
1463                 filePart = names[0].trim();
1464                 fragmentPart = names[1].trim();
1465             }
1466             if ("this" === filePart) {
1467                 filePart = "";
1468             } else {
1469                 filePart = this.getFilePath(filePart, element);
1470             }
1471             if (filePart != null) {
1472                 parts = filePart.match(varParExpr);
1473                 fragmentArgsList = null;
1474                 if (parts) {
1475                     if (parts.length > 1) {
1476                         filePart = parts[1].trim();
1477                     }
1478                     if (parts.length > 2) {
1479                         fragmentArgsList = parts[2].trim();
1480                     }
1481                 }
1482                 if (filePart != "" || !isFragmentChild(element)) {
1483                     isNode = thymol.thReplace.name == attr.localName || thymol.thReplace.synonym == attr.localName || thymol.thSubstituteby.name == attr.localName || thymol.thSubstituteby.synonym == attr.localName;
1484                     if (thymol.thCache[filePart] != null && thymol.thCache[filePart][fragmentPart] != null) {
1485                         isNode = isNode || fragmentPart == "::";
1486                         importNode = new ThNode(thymol.thCache[filePart][fragmentPart], false, rootNode, null, null, filePart, fragmentPart, isNode, element);
1487                     } else {
1488                         fragment = null;
1489                         importError = null;
1490                         if (filePart != "") {
1491                             fileName = filePart + thymol.templateSuffix;
1492                             $.get(fileName, function(textContent, status) {
1493                                 try {
1494                                     if ("success" == status) {
1495                                         content = new thymol.thDomParser().parseFromString(textContent, "text/html");
1496                                         fragment = Thymol.prototype.getImportNode(element, filePart, fragmentPart, fragmentArgsList, content);
1497                                     } else if (thymol.debug) {
1498                                         thymol.thWindow.alert("thymol.processImport file read failed: " + filePart + " fragment: " + fragmentPart);
1499                                     }
1500                                 } catch (err) {
1501                                     importError = err;
1502                                 }
1503                             }, "text");
1504                         } else {
1505                             fragment = this.getImportNode(element, filePart, fragmentPart, fragmentArgsList, thymol.thDocument);
1506                         }
1507                         if (fragment == null) {
1508                             if (importError !== null) {
1509                                 throw importError;
1510                             }
1511                             if (thymol.debug) {
1512                                 thymol.thWindow.alert("thymol.processImport fragment import failed: " + filePart + " fragment: " + fragmentPart);
1513                             }
1514                         } else {
1515                             importNode = new ThNode(fragment, false, rootNode, null, null, filePart, fragmentPart, isNode, element);
1516                         }
1517                     }
1518                 }
1519             }
1520             element.removeAttribute(attr.name);
1521             return importNode;
1522         },
1523         getImportNode: function(element, filePart, fragmentArg, fragmentArgsList, content) {
1524             var result = null, fragmentName = fragmentArg.trim(), fragmentPart = fragmentName, parts, argsCount, matched, fragment, htmlContent, fragArray, i, iLimit, j, jLimit, k, clean, bare, vlParts, vlArgs, argsList, varName, newElement;
1525             fragmentName = fragmentName.replace(/text\(\)/g, textFuncSynonym);
1526             parts = fragmentName.match(varParExpr);
1527             if (parts == null && fragmentArgsList != null) {
1528                 parts = [];
1529                 parts[1] = fragmentName;
1530                 parts[2] = fragmentArgsList;
1531             }
1532             argsCount = 0;
1533             if (parts) {
1534                 if (parts.length > 1) {
1535                     fragmentName = parts[1].trim();
1536                     if (parts.length > 2) {
1537                         if (parts[2].indexOf("=") > 0) {
1538                             argsCount = thymol.getWith(element, parts[2]);
1539                         } else {
1540                             argsCount = this.getList(element, parts[2]);
1541                         }
1542                     }
1543                 }
1544             }
1545             if (thymol.thCache[filePart] == null) {
1546                 thymol.thCache[filePart] = new Object();
1547             }
1548             matched = false;
1549             fragment = null;
1550             if (fragmentName == "::") {
1551                 htmlContent = $("html", content)[0];
1552                 result = htmlContent;
1553                 matched = true;
1554             } else {
1555                 fragArray = $(thymol.thFragment.escpName, content);
1556                 for (i = 0, iLimit = fragArray.length; i < iLimit; i++) {
1557                     fragment = fragArray[i];
1558                     for (j = 0, jLimit = fragment.attributes.length; j < jLimit; j++) {
1559                         clean = fragment.attributes[j];
1560                         clean = clean.value.replace(/\s/g, "");
1561                         bare = null;
1562                         vlParts = clean.match(varParExpr);
1563                         if (vlParts) {
1564                             if (vlParts.length > 1) {
1565                                 bare = vlParts[1].trim();
1566                             }
1567                         }
1568                         if (fragmentName == bare && argsCount > 0) {
1569                             if (vlParts.length > 2) {
1570                                 vlArgs = vlParts[2].trim().split(",");
1571                                 if (vlArgs) {
1572                                     if (vlArgs.length == argsCount) {
1573                                         argsList = element.thLocalVars["..."];
1574                                         if (argsList != null) {
1575                                             for (k = 0; k < argsCount; k++) {
1576                                                 varName = vlArgs[k].trim();
1577                                                 element.thLocalVars[varName] = argsList[k];
1578                                             }
1579                                             element.thLocalVars["..."] = null;
1580                                         }
1581                                         matched = true;
1582                                         break;
1583                                     } else if (vlArgs.length > argsCount) {
1584                                         break;
1585                                     }
1586                                 }
1587                             }
1588                         }
1589                         if (fragmentName == clean || fragmentPart == clean || fragmentName == bare) {
1590                             matched = true;
1591                             break;
1592                         }
1593                     }
1594                     if (matched) {
1595                         result = fragment;
1596                         break;
1597                     }
1598                 }
1599             }
1600             if (!matched) {
1601                 fragment = this.getDOMSelection(fragmentName, content);
1602                 if (fragment) {
1603                     matched = true;
1604                     result = fragment;
1605                 } else {
1606                     if (!element.isBlockChild) {
1607                         throw new thymol.ThError('getImportNode cannot match fragment: "' + fragmentName + '"', element);
1608                     }
1609                 }
1610             }
1611             thymol.thCache[filePart][fragmentPart] = result;
1612             if (matched) {
1613                 newElement = result.cloneNode(true);
1614                 if (newElement.nodeType == 1) {
1615                     newElement.removeAttribute(thymol.thFragment.name);
1616                     newElement.removeAttribute(thymol.thFragment.synonym);
1617                 }
1618                 result = newElement;
1619                 result.thLocalVars = element.thLocalVars;
1620             }
1621             return result;
1622         },
1623         getDOMSelection: function(initial, content) {
1624             var spec = initial, result = null, scope = "", query = new Array(), parts = "", innr = thymol.ThUtils.unBracket(spec), i, iLimit, j, jLimit, k, kLimit, m, mLimit, token, indx, saved, indxed, start, selection, descend, subQuery, exprFrags, classSpecs, qTerms, subSelect, partial, html, newNode;
1625             if (spec != innr && innr.charAt(innr.length - 1) == "]") {
1626                 spec = innr;
1627             }
1628             while (spec != "") {
1629                 parts = spec.match(domSelectExpr);
1630                 if (parts != null && parts.length > 1) {
1631                     for (i = 1, iLimit = parts.length; i < iLimit; i++) {
1632                         if (parts[i] != null) {
1633                             token = parts[i];
1634                             indx = null;
1635                             innr = thymol.ThUtils.unBracket(token);
1636                             if (token != innr) {
1637                                 if (innr.match(numericExpr)) {
1638                                     indx = innr;
1639                                 }
1640                             }
1641                             saved = spec;
1642                             spec = spec.replace(token, "");
1643                             if (saved == spec) {
1644                                 spec = "";
1645                             }
1646                             if (indx) {
1647                                 token = query[query.length - 1];
1648                                 indxed = new String(token);
1649                                 indxed.indx = indx;
1650                                 query[query.length - 1] = indxed;
1651                             } else {
1652                                 query.push(token.trim());
1653                             }
1654                             break;
1655                         }
1656                     }
1657                 } else {
1658                     break;
1659                 }
1660             }
1661             start = 0;
1662             if (query.length > 0 && query[0] != "" && query[0].charAt(0) == "/") {
1663                 scope = query[0];
1664                 start = 1;
1665             }
1666             selection = [];
1667             selection.push(content);
1668             descend = false;
1669             for (i = start, iLimit = query.length; i < iLimit; i++) {
1670                 subQuery = query[i];
1671                 innr = thymol.ThUtils.unBracket(subQuery);
1672                 if (subQuery != innr) {
1673                     innr = innr.replace(/[']/g, '"');
1674                     subQuery = "";
1675                     exprFrags = innr.split(/\s{1}\s*((?:and)|(?:or))\s{1}\s*/);
1676                     for (j = 0, jLimit = exprFrags.length; j < jLimit; j++) {
1677                         if (exprFrags[j] != "and" && exprFrags[j] != "or") {
1678                             classSpecs = exprFrags[j].match(/[@]?\s*(?:class)\s*(\W?[=])\s*[\"]((?:\w*[\-_]*)*)[\"]/);
1679                             if (classSpecs && classSpecs.length > 0) {
1680                                 if (classSpecs[1] == "=") {
1681                                     subQuery = subQuery + "[class~='" + classSpecs[2] + "']";
1682                                 }
1683                                 if (classSpecs[1] == "^=") {
1684                                     subQuery = subQuery + "[class^='" + classSpecs[2] + "'],[class*=' " + classSpecs[2] + "']";
1685                                 }
1686                             } else {
1687                                 subQuery = subQuery + "[" + exprFrags[j] + "]";
1688                             }
1689                         } else if (exprFrags[j] == "or") {
1690                             subQuery = subQuery + ",";
1691                         }
1692                     }
1693                 }
1694                 qTerms = subQuery.split("/");
1695                 for (j = 0, jLimit = qTerms.length; j < jLimit; j++) {
1696                     if (qTerms[j] != "") {
1697                         qTerms[j] = qTerms[j].replace(/[@]/g, "");
1698                         if (subQuery.indx != null) {
1699                             qTerms[j] = qTerms[j] + ":eq(" + subQuery.indx + ")";
1700                         }
1701                         subSelect = [];
1702                         for (k = 0, kLimit = selection.length; k < kLimit; k++) {
1703                             partial = null;
1704                             if (qTerms[j] == textFuncSynonym) {
1705                                 partial = $(selection[k]).contents().filter(function() {
1706                                     return this.nodeType === 3;
1707                                 });
1708                             } else if (descend) {
1709                                 partial = $(selection[k]).children(qTerms[j]);
1710                             } else if (j == 0) {
1711                                 if (scope == "/") {
1712                                     html = $("html", selection[k]);
1713                                     if (html.length > 0) {
1714                                         selection[k] = html;
1715                                     }
1716                                     partial = $(selection[k]).children("body").children(qTerms[j]);
1717                                     scope = "";
1718                                 } else {
1719                                     if (i == 0 || scope == "//") {
1720                                         partial = $(selection[k]).find(qTerms[j]);
1721                                         scope = "";
1722                                     } else {
1723                                         partial = $(selection[k]).filter(qTerms[j]);
1724                                     }
1725                                 }
1726                             } else {
1727                                 partial = $(selection[k]).children(qTerms[j]);
1728                             }
1729                             if (partial != null) {
1730                                 for (m = 0, mLimit = partial.length; m < mLimit; m++) {
1731                                     subSelect.push(partial[m]);
1732                                 }
1733                             }
1734                         }
1735                         selection = subSelect;
1736                     }
1737                 }
1738                 descend = qTerms[qTerms.length - 1] == "";
1739             }
1740             result = selection;
1741             if (result != null && !(result.length === undefined)) {
1742                 if (result.length > 1) {
1743                     newNode = thymol.thDocument.createDocumentFragment();
1744                     for (i = 0, iLimit = result.length; i < iLimit; i++) {
1745                         var newChild = thymol.thDocument.importNode(result[i], true);
1746                         newNode.appendChild(newChild);
1747                     }
1748                     result = newNode;
1749                 } else {
1750                     result = result[0];
1751                 }
1752             }
1753             return result;
1754         },
1755         getFilePath: function(part, element) {
1756             var result = thymol.substitute(part, element), mapped = null, slashpos;
1757             if (result) {
1758                 if (thymol.mappings) {
1759                     mapped = thymol.getMapped(result, false);
1760                 }
1761             }
1762             if (mapped) {
1763                 result = mapped;
1764             } else {
1765                 var dotFirst = result.charAt(0) === ".";
1766                 if (result && (thymol.useAbsolutePath || !dotFirst)) {
1767                     slashpos = result.indexOf("/");
1768                     if (thymol.useAbsolutePath || slashpos >= 0) {
1769                         if (slashpos == 0 && !thymol.useAbsolutePath) {
1770                             result = result.substring(1);
1771                         }
1772                         var proto = "";
1773                         if (thymol.useAbsolutePath) {
1774                             proto = thymol.protocol;
1775                         }
1776                         if (thymol.useAbsolutePath && !!thymol.absolutePath) {
1777                             result = proto + thymol.absolutePath + result;
1778                         } else {
1779                             if (dotFirst) {
1780                                 result = thymol.templatePath + result;
1781                             } else {
1782                                 result = proto + thymol.root + thymol.path + result;
1783                             }
1784                         }
1785                     }
1786                 }
1787             }
1788             return result;
1789         },
1790         doLiteralSubstExpr: function(param, primary) {
1791             var result = param.trim(), term, subst, lsp;
1792             if (thymol.ThUtils.isLiteralSubst(result)) {
1793                 result = this.decodeLiteralSubst(result);
1794             } else {
1795                 term = primary;
1796                 while (term != null) {
1797                     if (thymol.ThUtils.isLiteralSubst(term)) {
1798                         subst = this.decodeLiteralSubst(term);
1799                         result = result.replace(term, subst);
1800                         lsp = result.match(litSubstExpr);
1801                         if (lsp && lsp.length > 0) {
1802                             term = lsp[1];
1803                         } else {
1804                             break;
1805                         }
1806                     } else {
1807                         break;
1808                     }
1809                 }
1810             }
1811             return result;
1812         },
1813         decodeLiteralSubst: function(param) {
1814             var result = param, parts, rep, i, iLimit;
1815             result = result.trim();
1816             result = result.substring(1, result.length - 1);
1817             result = result.replace(/[\']/g, "&#39;");
1818             parts = result.split(varRefExpr);
1819             if (parts && parts.length > 0) {
1820                 rep = "";
1821                 for (i = 0, iLimit = parts.length; i < iLimit; i++) {
1822                     if (parts[i] != "") {
1823                         if (!parts[i].match(varRefExpr)) {
1824                             parts[i] = "'" + parts[i] + "'";
1825                         }
1826                         if (rep == "") {
1827                             rep = parts[i];
1828                         } else {
1829                             rep = rep + "+" + parts[i];
1830                         }
1831                     }
1832                 }
1833                 result = rep;
1834             }
1835             return result;
1836         },
1837         doReplace: function(isNode, element, content) {
1838             if (isNode) {
1839                 var parent = element.parentNode;
1840                 if (content.nodeName.toLowerCase() == "html") {
1841                     this.doInsertion(element, content, function(e, n) {
1842                         if (n.nodeType == 1) {
1843                             n.removeAttribute(thymol.thFragment.name);
1844                             n.removeAttribute(thymol.thFragment.synonym);
1845                         }
1846                         e.parentNode.insertBefore(n, e);
1847                     });
1848                     parent.removeChild(element);
1849                 } else {
1850                     var node = this.doClone(content, parent.ownerDocument);
1851                     if (node.nodeType == 1) {
1852                         node.removeAttribute(thymol.thFragment.name);
1853                         node.removeAttribute(thymol.thFragment.synonym);
1854                     }
1855                     parent.replaceChild(node, element);
1856                     node.parentNode = parent;
1857                 }
1858             } else {
1859                 try {
1860                     while (element.firstChild != null) {
1861                         element.removeChild(element.firstChild);
1862                         if (element.firstChild == null) {
1863                             break;
1864                         }
1865                     }
1866                     this.doInsertion(element, content, function(e, n) {
1867                         if (n.nodeType == 1) {
1868                             n.removeAttribute(thymol.thFragment.name);
1869                             n.removeAttribute(thymol.thFragment.synonym);
1870                         }
1871                         e.appendChild(n);
1872                     });
1873                 } catch (err) {
1874                     element.innerHTML = content.innerHTML;
1875                 }
1876             }
1877         },
1878         doClone: function(old, targetDoc) {
1879             var node, cNodes, i, iNode, aNode;
1880             if (!!old.parentNode && old.parentNode.ownerDocument === targetDoc) {
1881                 node = old.cloneNode(false);
1882             } else {
1883                 node = targetDoc.importNode(old, false);
1884             }
1885             if (node !== null) {
1886                 if (node.nodeType == 1) {
1887                     if (old.thLocalVars !== null) {
1888                         node.thLocalVars = old.thLocalVars;
1889                     }
1890                 }
1891                 if (old.childNodes !== null) {
1892                     cNodes = old.childNodes.length;
1893                     if (cNodes > 0) {
1894                         for (i = 0; i < cNodes; i++) {
1895                             iNode = old.childNodes[i];
1896                             if (iNode !== null) {
1897                                 aNode = this.doClone(iNode, targetDoc);
1898                                 if (aNode !== null) {
1899                                     node.appendChild(aNode);
1900                                 }
1901                             }
1902                         }
1903                     }
1904                 }
1905             }
1906             return node;
1907         },
1908         doInsertion: function(element, content, func) {
1909             var topLevel = true, parent = element.parentElement, i, iLimit, iNode, elementName, j, jLimit, jNode, cJNode, cINode;
1910             if (parent != null) {
1911                 topLevel = element.parentElement.nodeName.toLowerCase() == "html";
1912             }
1913             for (i = 0, iLimit = content.childNodes.length; i < iLimit; i++) {
1914                 iNode = content.childNodes[i];
1915                 if (iNode) {
1916                     if (!topLevel) {
1917                         elementName = iNode.nodeName.toLowerCase();
1918                         if (elementName != "head") {
1919                             if (elementName == "body") {
1920                                 for (j = 0, jLimit = iNode.childNodes.length; j < jLimit; j++) {
1921                                     jNode = iNode.childNodes[j];
1922                                     if (jNode) {
1923                                         cJNode = this.doClone(jNode, parent.ownerDocument);
1924                                         func(element, cJNode);
1925                                     }
1926                                 }
1927                             } else {
1928                                 cINode = this.doClone(iNode, parent.ownerDocument);
1929                                 func(element, cINode);
1930                             }
1931                         }
1932                     } else {
1933                         cINode = this.doClone(iNode, parent.ownerDocument);
1934                         func(element, cINode);
1935                     }
1936                 }
1937             }
1938         },
1939         getThParam: function(paramName, isBoolean, isPath, defaultValue) {
1940             var localValue = defaultValue, globalValue = thymol.thWindow[paramName], theParam = thymol.ThUtils.getParameter(paramName);
1941             if (typeof globalValue === "undefined") {
1942                 globalValue = thymol.applicationContext.javascriptify(paramName);
1943             }
1944             if (!!theParam) {
1945                 if (theParam instanceof ThParam) {
1946                     if (theParam.globalValue !== globalValue) {
1947                         theParam.globalValue = globalValue;
1948                         theParam.value = globalValue;
1949                         localValue = globalValue;
1950                     }
1951                 }
1952                 if (isBoolean) {
1953                     localValue = theParam.getBooleanValue();
1954                 }
1955             } else {
1956                 if (!(typeof globalValue === "undefined")) {
1957                     if (globalValue != null) {
1958                         if (isBoolean) {
1959                             localValue = globalValue == true;
1960                         } else {
1961                             localValue = globalValue;
1962                         }
1963                     }
1964                 }
1965             }
1966             if (!isBoolean && isPath && localValue.length > 0 && localValue.charAt(localValue.length - 1) != "/") {
1967                 localValue = localValue + "/";
1968             }
1969             thymol.applicationContext.createVariable(paramName, localValue);
1970             return localValue;
1971         }
1972     };
1973     function addDialect(spec) {
1974         var i, iLimit, prec = thymol.thDefaultPrecedence;
1975         if (spec !== null && typeof spec !== "undefined") {
1976             if (spec.attributeProcessors !== null && typeof spec.attributeProcessors !== "undefined") {
1977                 for (i = 0, iLimit = spec.attributeProcessors.length; i < iLimit; i++) {
1978                     if (spec.attributeProcessors[i].precedence !== null && typeof spec.attributeProcessors[i].precedence !== "undefined") {
1979                         prec = spec.attributeProcessors[i].precedence;
1980                     } else {
1981                         prec = thymol.thDefaultPrecedence;
1982                     }
1983                     configureAttributeProcessor(spec.prefix, spec.attributeProcessors[i].name, spec.attributeProcessors[i].processor, prec, null);
1984                 }
1985             }
1986             if (spec.elementProcessors !== null && typeof spec.elementProcessors !== "undefined") {
1987                 for (i = 0, iLimit = spec.elementProcessors.length; i < iLimit; i++) {
1988                     configureElementProcessor(spec.prefix, spec.elementProcessors[i].name, spec.elementProcessors[i].processor);
1989                 }
1990             }
1991             if (spec.objects !== null && typeof spec.objects !== "undefined") {
1992                 for (i = 0, iLimit = spec.objects.length; i < iLimit; i++) {
1993                     if (spec.objects[i].name !== null && typeof spec.objects[i].name !== "undefined") {
1994                         spec.objects[i].object.thExpressionObjectName = spec.objects[i].name;
1995                         configureModule(spec.objects[i].object);
1996                     } else {
1997                         configureModule(spec.objects[i]);
1998                     }
1999                 }
2000             }
2001         }
2002     }
2003     function ThNode(thDocParam, visitedParam, parentDocParam, firstChildParam, nextSiblingParam, fileNameParam, fragNameParam, isNodeParam, elementParam) {
2004         this.thDoc = thDocParam;
2005         this.visited = visitedParam;
2006         this.parentDoc = parentDocParam;
2007         this.firstChild = firstChildParam;
2008         this.nextSibling = nextSiblingParam;
2009         this.fileName = fileNameParam;
2010         this.fragName = fragNameParam;
2011         this.isNode = isNodeParam;
2012         this.element = elementParam;
2013     }
2014     function ThError(message, element, source) {
2015         this.name = "ThError";
2016         this.message = message || "Default Message";
2017         if (element !== null && typeof element !== "undefined" && element.isBlockChild) {
2018             this.suppress = true;
2019         } else {
2020             this.element = element || {};
2021             this.suppress = false;
2022         }
2023         if (!!source) {
2024             if (!!source.stack) {
2025                 this.stack = source.stack;
2026             }
2027         }
2028     }
2029     ThError.prototype = new Error();
2030     ThError.prototype.constructor = ThError;
2031     function ThParam(valueArg) {
2032         this.value = valueArg;
2033         this.globalValue;
2034         this["class"] = new thymol.ThClass("Thymol.ThParam");
2035         this.getBooleanValue = function() {
2036             return !thymol.ThUtils.testLiteralFalse(this.value);
2037         };
2038         this.toString = function() {
2039             return this.value;
2040         };
2041         this.getNumericValue = function() {
2042             return Number(this.value);
2043         };
2044     }
2045     function ThAttr(suffix, func, prec, list, pref, dataAttr) {
2046         var prefix = "", dataPrefix = null, escpPrefix = "";
2047         if (typeof pref !== "undefined" && pref !== null) {
2048             prefix = pref + ":";
2049             if (thymol.thThymeleafPrefixList.indexOf(prefix) < 0) {
2050                 thymol.thThymeleafPrefixList.push(prefix);
2051             }
2052             escpPrefix = pref + "\\:";
2053             if (typeof dataAttr === "undefined" || dataAttr === null) {
2054                 dataPrefix = thymol.dataPrefix + "-" + pref + "-";
2055                 if (thymol.thThymeleafPrefixList.indexOf(dataPrefix) < 0) {
2056                     thymol.thThymeleafPrefixList.push(dataPrefix);
2057                 }
2058             } else {
2059                 dataPrefix = dataAttr;
2060             }
2061         }
2062         this.suffix = suffix;
2063         this.name = prefix + suffix;
2064         this.regex = null;
2065         if (suffix.indexOf("*") >= 0 || suffix.indexOf("?") >= 0 || suffix.indexOf("+") >= 0 || suffix.indexOf("\\") >= 0 || suffix.indexOf("|") >= 0 || suffix.indexOf("[") >= 0 || suffix.indexOf("]") >= 0 || suffix.indexOf("{") >= 0 || suffix.indexOf("}") >= 0) {
2066             if ("*" === suffix) {
2067                 suffix = ".*";
2068             }
2069             suffix = prefix + suffix;
2070             this.regex = new RegExp(suffix);
2071         }
2072         this.escpName = "[" + escpPrefix + suffix + "]";
2073         if (dataPrefix !== null) {
2074             this.synonym = dataPrefix + suffix;
2075             this.escpSynonym = "[" + this.synonym + "]";
2076         } else {
2077             this.synonym = null;
2078             this.escpSynonym = null;
2079         }
2080         if (typeof prec !== "undefined" && prec !== null) {
2081             this.precedence = prec;
2082         } else {
2083             this.precedence = thymol.thDefaultPrecedence;
2084         }
2085         if (!!list) {
2086             var attrList = list[pref];
2087             if (!attrList) {
2088                 attrList = [];
2089                 list[pref] = attrList;
2090                 if (dataPrefix !== null) {
2091                     list[dataPrefix] = attrList;
2092                 }
2093             }
2094             attrList.push(this);
2095         }
2096         this.process = function() {
2097             thymol.thWindow.alert('unsupported processing function for attribute "' + this.name + '"');
2098         };
2099         if (!(typeof func === "undefined")) {
2100             this.process = func;
2101         }
2102         this.disable = function() {
2103             this.name = null;
2104             this.escpName = null;
2105             this.escpSynonym = null;
2106             this.process = function() {};
2107         };
2108     }
2109     function ThElement(suffix, func, pref) {
2110         var tha = new thymol.ThAttr(suffix, null, 0, null, pref);
2111         this.name = tha.name;
2112         this.synonym = tha.synonym;
2113         this.endName = "/" + tha.name;
2114         this.endSynonym = "/" + tha.synonym;
2115         this.process = function() {
2116             thymol.thWindow.alert('unsupported processing function for element "' + this.name + '"');
2117         };
2118         if (!(typeof func === "undefined")) {
2119             this.process = func;
2120         }
2121         this.disable = function() {
2122             this.name = null;
2123             this.synonym = null;
2124             this.endName = null;
2125             this.endSynonym = null;
2126             this.process = null;
2127         };
2128         thymol.thThymeleafElementsList.push(this);
2129     }
2130     function ThSet() {
2131         this.that = this;
2132         this.setSize = 0;
2133         this.isContent = function(k) {
2134             return this.hasOwnProperty(k) && typeof this[k] !== "function" && k !== "that" && k !== "setSize";
2135         };
2136         this.add = function(k) {
2137             var contained = typeof this[k] !== "undefined";
2138             this[k] = k;
2139             if (contained !== (typeof this[k] !== "undefined")) {
2140                 this.setSize++;
2141             }
2142         };
2143         this.addAll = function(other) {
2144             var k = null, value;
2145             for (k in other) {
2146                 if (other.hasOwnProperty(k)) {
2147                     value = other[k];
2148                     if (typeof value !== "function") {
2149                         add(value);
2150                     }
2151                 }
2152             }
2153         };
2154         this.clear = function() {
2155             for (var k in this) {
2156                 if (this.hasOwnProperty(k)) {
2157                     delete this[k];
2158                 }
2159             }
2160             setSize = 0;
2161         };
2162         this.contains = function(k) {
2163             return typeof this[k] !== "undefined";
2164         };
2165         this.containsAll = function(keys) {
2166             var keySet = keys, k = null;
2167             if (typeof keys === "Array" || Object.prototype.toString.call(keys) === "[object Array]") {
2168                 keySet = ThSet.prototype.fromArray(keys);
2169             }
2170             for (k in keySet) {
2171                 if (keySet.hasOwnProperty(k)) {
2172                     if (typeof this[k] === "undefined") {
2173                         return false;
2174                     }
2175                 }
2176             }
2177             return true;
2178         };
2179         this.isEmpty = function() {
2180             return this.setSize === 0;
2181         };
2182         this.size = function() {
2183             return this.setSize;
2184         };
2185         this.remove = function(k) {
2186             var contained = typeof this[k] !== "undefined";
2187             delete this[k];
2188             if (contained !== (typeof this[k] !== "undefined")) {
2189                 this.setSize--;
2190             }
2191         };
2192         this.toArray = function() {
2193             return getArray(this);
2194         };
2195         this.toString = function() {
2196             var array = getArray();
2197             return array.toString();
2198         };
2199         function getArray(obj) {
2200             var array = [], k = null, value;
2201             for (k in obj) {
2202                 if (obj.hasOwnProperty(k) && k !== "that" && k !== "setSize") {
2203                     value = obj[k];
2204                     if (typeof value !== "function") {
2205                         array.push(value);
2206                     }
2207                 }
2208             }
2209             return array;
2210         }
2211     }
2212     ThSet.prototype.fromArray = function(array) {
2213         var set = new thymol.ThSet(), i, iLimit;
2214         for (i = 0, iLimit = array.length; i < iLimit; i++) {
2215             set.add(array[i]);
2216         }
2217         return set;
2218     };
2219     function ThMap() {
2220         ThSet.apply(this);
2221         this.containsKey = function(k) {
2222             return this.contains(k);
2223         };
2224         this.containsValue = function(target) {
2225             var k = null, value;
2226             for (k in this.that) {
2227                 if (this.that.hasOwnProperty(k) && k !== "that") {
2228                     value = this.that[k];
2229                     if (value === target) {
2230                         return true;
2231                     }
2232                 }
2233             }
2234             return false;
2235         };
2236         this.entrySet = function() {
2237             return this.that;
2238         };
2239         this.get = function(k) {
2240             return this.that[k];
2241         };
2242         this.keySet = function() {
2243             return this.that;
2244         };
2245         this.put = function(k, v) {
2246             var contained = typeof this[k] !== "undefined";
2247             this.that[k] = v;
2248             if (contained !== (typeof this[k] !== "undefined")) {
2249                 this.setSize++;
2250             }
2251         };
2252         this.putAll = function(t) {
2253             for (var k in t) {
2254                 put(k, t[k]);
2255             }
2256         };
2257         this.values = function() {
2258             return this.that;
2259         };
2260     }
2261     ThMap.prototype = new ThSet();
2262     ThMap.prototype.constructor = ThMap;
2263     function ThObject(dolly) {
2264         for (prop in dolly) {
2265             if (dolly.hasOwnProperty(prop)) {
2266                 if (prop) {
2267                     if (!this[prop]) {
2268                         this[prop] = dolly[prop];
2269                     }
2270                 }
2271             }
2272         }
2273         this["class"] = new thymol.ThClass("Thymol.ThObject");
2274         this.toNonThObject = function() {
2275             var plain = {};
2276             for (prop in this) {
2277                 if (this.hasOwnProperty(prop)) {
2278                     if (prop) {
2279                         if (!plain[prop]) {
2280                             if (prop !== "toNonThObject") {
2281                                 if (prop !== "class" || prop === "class" && this[prop] !== null && this[prop].name !== "Thymol.ThObject") {
2282                                     plain[prop] = this[prop];
2283                                 }
2284                             }
2285                         }
2286                     }
2287                 }
2288             }
2289             return plain;
2290         };
2291     }
2292     function ThVarsAccessor(storeArg, storeNameArg) {
2293         this.store = storeArg;
2294         this.arrayName = storeNameArg;
2295         this.length = function() {
2296             return this.store.length;
2297         };
2298         this.get = function(name) {
2299             return this.store[name];
2300         };
2301         this.set = function(name, value) {
2302             this.store[name] = value;
2303         };
2304     }
2305     function ThClass(nValue) {
2306         this.name = nValue;
2307     }
2308     return {
2309         Thymol: Thymol,
2310         ThError: ThError,
2311         ThParam: ThParam,
2312         ThAttr: ThAttr,
2313         ThElement: ThElement,
2314         ThSet: ThSet,
2315         ThMap: ThMap,
2316         ThObject: ThObject,
2317         ThVarsAccessor: ThVarsAccessor,
2318         ThClass: ThClass,
2319         thDomParser: thymol.thDomParser,
2320         thDocument: thymol.thDocument,
2321         thWindow: thymol.thWindow,
2322         thTop: thymol.thTop,
2323         thRequest: thymol.thRequest,
2324         thVersion: thymol.thVersion,
2325         thReleaseDate: thymol.thReleaseDate,
2326         thURL: thymol.thURL,
2327         thAltURL: thymol.thAltURL,
2328         thInclude: thymol.thInclude,
2329         thReplace: thymol.thReplace,
2330         thSubstituteby: thymol.thSubstituteby,
2331         thFragment: thymol.thFragment,
2332         thRemove: thymol.thRemove,
2333         thBlock: thymol.thBlock,
2334         thScriptName: thymol.thScriptName,
2335         thDefaultPrefix: thymol.thDefaultPrefix,
2336         thDefaultDataPrefix: thymol.thDefaultDataPrefix,
2337         thDefaultPrecision: thymol.thDefaultPrecision,
2338         thDefaultProtocol: thymol.thDefaultProtocol,
2339         thDefaultLocale: thymol.thDefaultLocale,
2340         thDefaultPrecedence: thymol.thDefaultPrecedence,
2341         thDefaultMessagePath: thymol.thDefaultMessagePath,
2342         thDefaultResourcePath: thymol.thDefaultResourcePath,
2343         thDefaultMessagesBaseName: thymol.thDefaultMessagesBaseName,
2344         thDefaultRelativeRootPath: thymol.thDefaultRelativeRootPath,
2345         thDefaultExtendedMapping: thymol.thDefaultExtendedMapping,
2346         thDefaultLocalMessages: thymol.thDefaultLocalMessages,
2347         thDefaultDisableMessages: thymol.thDefaultDisableMessages,
2348         thDefaultTemplateSuffix: thymol.thDefaultTemplateSuffix,
2349         thThymeleafPrefixList: thymol.thThymeleafPrefixList,
2350         thThymeleafElementsList: thymol.thThymeleafElementsList,
2351         thLocation: thymol.thLocation,
2352         messagePath: thymol.messagePath,
2353         resourcePath: thymol.resourcePath,
2354         relativeRootPath: thymol.relativeRootPath,
2355         messagesBaseName: thymol.messagesBaseName,
2356         extendedMapping: thymol.extendedMapping,
2357         scriptPath: thymol.scriptPath,
2358         absolutePath: thymol.absolutePath,
2359         useAbsolutePath: thymol.useAbsolutePath,
2360         useFullURLPath: thymol.useFullURLPath,
2361         localMessages: thymol.localMessages,
2362         indexFile: thymol.indexFile,
2363         disableMessages: thymol.disableMessages,
2364         templateSuffix: thymol.templateSuffix,
2365         prefix: thymol.prefix,
2366         dataPrefix: thymol.dataPrefix,
2367         templateName: thymol.templateName,
2368         templatePath: thymol.templatePath,
2369         objects: thymol.objects,
2370         jqSetup: jqSetup,
2371         isClientSide: isClientSide,
2372         execute: execute,
2373         updatePrefix: updatePrefix,
2374         init: init,
2375         ready: ready,
2376         addDialect: addDialect,
2377         isFragmentChild: isFragmentChild,
2378         preProcess: preProcess,
2379         substitute: substitute,
2380         substituteParam: substituteParam,
2381         configureModule: configureModule,
2382         configureAttributeProcessor: configureAttributeProcessor,
2383         configureElementProcessor: configureElementProcessor,
2384         configurePreExecution: configurePreExecution,
2385         configurePostExecution: configurePostExecution,
2386         getStandardURL: getStandardURL,
2387         getMessage: getMessage,
2388         getExpression: getExpression,
2389         getWith: getWith,
2390         getParsedExpr: getParsedExpr,
2391         getLocale: getLocale,
2392         getMapped: getMapped,
2393         getBooleanValue: getBooleanValue,
2394         setLocale: setLocale
2395     };
2396 }();
2397
2398 thymol.makeContext = function(contextNameParam, varAccessorParam) {
2399     var jsonDeclSpec = "(?:\\W*([\\'][A-Za-z]+(?:\\w|[$])*[\\'])\\s*[:])?\\s*([#][A-Za-z]+(?:\\w|[$])*)(?:\\W|[^$])*", jsonDeclExpr = new RegExp(jsonDeclSpec), context = new Array();
2400     context.contextName = contextNameParam;
2401     context.varAccessor = varAccessorParam;
2402     context.varStore = [];
2403     context.varNamePrefix = "";
2404     if (typeof varAccessorParam === "undefined") {
2405         context.varAccessor = new thymol.ThVarsAccessor(context.varStore, "varStore");
2406     }
2407     context.varNamePrefix = context.varAccessor.arrayName + "[";
2408     context.getJSONView = function(param, rootVal) {
2409         var pType = typeof param, view = "", objType;
2410         if (pType === "string") {
2411             view = view + "'" + param + "'";
2412         } else if (pType === "number" || pType === "boolean") {
2413             view = view + param;
2414         } else if (pType === "object") {
2415             if (param instanceof Object) {
2416                 objType = Object.prototype.toString.call(param);
2417                 if ("[object Array]" === objType) {
2418                     view = this.getJSONViewArray(param, false);
2419                 } else if ("[object Object]" === objType) {
2420                     view = this.getJSONViewObject(param, false);
2421                 }
2422                 view = "#" + view;
2423             }
2424         }
2425         return view;
2426     };
2427     context.init = function() {
2428         var persisted = thymol.thTop.name, paramRow, paramName, params, i, iLimit, paramValue;
2429         if (persisted && persisted !== "") {
2430             params = this.javascriptify(persisted);
2431             if (params && params.length > 0) {
2432                 for (i = 0, iLimit = params.length; i < iLimit; i++) {
2433                     paramRow = params[i];
2434                     if (paramRow) {
2435                         paramName = paramRow[0];
2436                         if (paramName) {
2437                             paramValue = paramRow[1];
2438                             this.createVariable(paramName, paramValue);
2439                         }
2440                     }
2441                 }
2442             }
2443         }
2444     };
2445     context.getJSONViewObject = function(param, rootVal) {
2446         var isRoot = true, key = null, view = "{", value, identifier, definition, suffix, instanceNamePrefix, isTaken, i, iLimit, instanceValue;
2447         if (typeof rootVal === "boolean") {
2448             isRoot = rootVal;
2449         }
2450         for (key in param) {
2451             if (key) {
2452                 value = param[key];
2453                 if (typeof value !== "function") {
2454                     if (view != "{") {
2455                         view = view + ",";
2456                     }
2457                     identifier = this.getJSONView(key, false);
2458                     definition = this.getJSONView(value, false);
2459                     view = view + identifier + ":";
2460                     if (!isRoot && typeof value === "object") {
2461                         suffix = 1;
2462                         instanceNamePrefix = key + "$";
2463                         instanceName = null;
2464                         isTaken = false;
2465                         do {
2466                             instanceName = instanceNamePrefix + suffix++;
2467                             instanceValue = context[instanceName];
2468                             if (instanceValue === null || typeof instanceValue === "undefined") {
2469                                 isTaken = false;
2470                                 for (i = 0, iLimit = varStore.length; i < iLimit; i++) {
2471                                     if (instanceName === varStore[i][0]) {
2472                                         isTaken = true;
2473                                         break;
2474                                     }
2475                                 }
2476                                 if (!isTaken) {
2477                                     this.addAttribute(instanceName, definition);
2478                                 }
2479                             }
2480                         } while (isTaken);
2481                         if (instanceName !== null) {
2482                             view = view + "#" + instanceName;
2483                         }
2484                     } else {
2485                         view = view + definition;
2486                     }
2487                 }
2488             }
2489         }
2490         view = view + "}";
2491         return view;
2492     };
2493     context.getJSONViewArray = function(param, rootVal) {
2494         var view = "[", i;
2495         for (i = 0; i < param.length; i++) {
2496             view = view + this.getJSONView(param[i], false);
2497             if (i < param.length - 1) {
2498                 view = view + ",";
2499             }
2500         }
2501         view = view + "]";
2502         return view;
2503     };
2504     context.getAttribute = function(name) {
2505         return context[name];
2506     };
2507     context.addAttribute = function(name, value) {
2508         var entry = [];
2509         entry[0] = name;
2510         entry[1] = value;
2511         varStore.push(entry);
2512     };
2513     context.serialise = function() {
2514         varStore = [];
2515         var serialised = "[", key = null, value, cn, view, name, i, iLimit;
2516         for (key in context) {
2517             if (key) {
2518                 value = context[key];
2519                 if (value != null && typeof value === "object") {
2520                     cn = Object.prototype.toString.call(value);
2521                     if ("[object Array]" !== cn && !(value instanceof thymol.ThClass) && !(value instanceof thymol.ThVarsAccessor)) {
2522                         if (serialised !== "[") {
2523                             serialised = serialised + ",";
2524                         }
2525                         view = this.getJSONView(value, true);
2526                         serialised = serialised + "[";
2527                         serialised = serialised + '"' + key + '"';
2528                         serialised = serialised + ",";
2529                         serialised = serialised + '"' + view + '"';
2530                         serialised = serialised + "]";
2531                     }
2532                 }
2533             }
2534         }
2535         for (i = 0, iLimit = varStore.length; i < iLimit; i++) {
2536             name = varStore[i][0];
2537             view = varStore[i][1];
2538             serialised = serialised + ",[";
2539             serialised = serialised + '"' + name + '"';
2540             serialised = serialised + ",";
2541             serialised = serialised + '"' + view + '"';
2542             serialised = serialised + "]";
2543         }
2544         serialised = serialised + "]";
2545         return serialised;
2546     };
2547     context.javascriptify = function(fn) {
2548         try {
2549             return new Function("return " + fn)();
2550         } catch (err) {
2551             return undefined;
2552         }
2553     };
2554     context.createVariable = function(name, valParam, isReq) {
2555         var value = valParam, param, tt, literalBoolean, strValue, initial, existing, newArray;
2556         param = value;
2557         if (!(value instanceof thymol.ThParam)) {
2558             tt = typeof valParam;
2559             if (tt !== "function" && tt !== "object") {
2560                 if (tt === "string") {
2561                     try {
2562                         value = isReq ? decodeURIComponent(value) : decodeURI(value);
2563                     } catch (err) {}
2564                 }
2565                 if (tt === "boolean" || tt === "number") {
2566                     param = new thymol.ThParam(value);
2567                 } else if (value || value === "") {
2568                     literalBoolean = thymol.ThUtils.testLiteralFalse(value);
2569                     if (literalBoolean) {
2570                         param = false;
2571                     } else {
2572                         strValue = new String(value);
2573                         initial = strValue.trim();
2574                         if (initial.charAt(0) === "#") {
2575                             initial = initial.substring(1);
2576                             try {
2577                                 param = this.createJSONVariable(initial);
2578                             } catch (err) {
2579                                 if (err instanceof ReferenceError) {}
2580                                 if (err instanceof EvalError) {}
2581                                 if (param == null || isReq) {
2582                                     param = new thymol.ThParam(value);
2583                                 }
2584                             }
2585                         } else {
2586                             param = new thymol.ThParam(strValue.toString());
2587                         }
2588                     }
2589                 }
2590             }
2591         }
2592         if (isReq) {
2593             existing = context[name];
2594             if (typeof existing !== "undefined" && existing !== null) {
2595                 if (Object.prototype.toString.call(existing) === "[object Array]") {
2596                     existing.push(param);
2597                 } else {
2598                     if (thymol.debug) {
2599                         thymol.thWindow.alert('request parameters should be of type string array "' + name + '"');
2600                     }
2601                 }
2602             } else {
2603                 newArray = new Array();
2604                 newArray["class"] = {};
2605                 newArray["class"]["name"] = "[Thymol.ThParam]";
2606                 newArray.push(param);
2607                 context[name] = newArray;
2608             }
2609         } else {
2610             context[name] = param;
2611         }
2612         return param;
2613     };
2614     context.createJSONVariable = function(initial) {
2615         var current = initial.trim(), parts = " ", substIndex, token, re, vName, obj, result;
2616         substIndex = this.varAccessor.length() + 1;
2617         while (parts) {
2618             parts = current.match(jsonDeclExpr);
2619             if (parts && parts.length > 2) {
2620                 token = parts[2];
2621                 token = token.replace(/[\']/g, "[']").replace(/[$]/g, "[$]");
2622                 re = new RegExp(token);
2623                 vName = this.varNamePrefix + substIndex + "]";
2624                 obj = new Object();
2625                 obj.name = parts[2].substring(1);
2626                 this.varAccessor.set(substIndex, obj);
2627                 substIndex = substIndex + 1;
2628                 current = current.replace(re, "'" + vName + "'", "g");
2629             }
2630         }
2631         current = current.replace(/[\']/g, '"');
2632         result = $.parseJSON(current);
2633         if ("[object Array]" !== Object.prototype.toString.call(result)) {
2634             result = new thymol.ThObject(result);
2635         }
2636         return result;
2637     };
2638     context.resolveJSONReferences = function() {
2639         var key = null, param, prop = null, val, ref, subst, isReq = "request" === this.contextName;
2640         for (key in context) {
2641             if (key) {
2642                 param = context[key];
2643                 if (param != null && typeof param === "object") {
2644                     if (!(param instanceof thymol.ThVarsAccessor) && !(param instanceof thymol.ThClass)) {
2645                         if (!(param instanceof thymol.ThParam)) {
2646                             if (isReq && Object.prototype.toString.call(param) === "[object Array]") {
2647                                 for (var i = 0, iLimit = param.length; i < iLimit; i++) {
2648                                     var pi = param[i];
2649                                     if (!!pi && typeof pi.value === "string" && pi.value.charAt(0) == "#") {
2650                                         var pv = thymol.ThUtils.getParameter(pi.value.substring(1));
2651                                         param[i] = pv;
2652                                     }
2653                                 }
2654                             } else {
2655                                 for (prop in param) {
2656                                     if (prop) {
2657                                         val = param[prop];
2658                                         if (typeof val === "string") {
2659                                             if (val.indexOf(this.varNamePrefix) == 0) {
2660                                                 subst = null;
2661                                                 if (prop.match(/\d*/)) {
2662                                                     ref = val.substring(this.varNamePrefix.length, val.length - 1);
2663                                                     ref = this.varAccessor.get(ref);
2664                                                     subst = context[ref.name];
2665                                                 } else {
2666                                                     subst = context[prop];
2667                                                 }
2668                                                 param[prop] = subst;
2669                                             }
2670                                         }
2671                                     }
2672                                 }
2673                             }
2674                         } else if (typeof param.value === "string" && param.value.charAt(0) == "#") {
2675                             subst = context[param.value.substring(1)];
2676                             context[key] = subst;
2677                         }
2678                     }
2679                 }
2680             }
2681         }
2682     };
2683     return context;
2684 };
2685
2686 thymol.ThUtils = function() {
2687     function mergeVars(thiz, other) {
2688         var current = thiz, prop = null;
2689         if (!current) {
2690             current = {};
2691         }
2692         for (prop in other) {
2693             if (other.hasOwnProperty(prop)) {
2694                 if (prop) {
2695                     if (!current[prop]) {
2696                         current[prop] = other[prop];
2697                     }
2698                 }
2699             }
2700         }
2701         return current;
2702     }
2703     function processElement(func, element, arg, obj) {
2704         var result = null, parent = element.parentElement;
2705         if (!thymol.isFragmentChild(element)) {
2706             if (!element.thObjectVar) {
2707                 parent = element.parentElement;
2708                 while (parent) {
2709                     if (parent.thObjectVar) {
2710                         element.thObjectVar = parent.thObjectVar;
2711                         break;
2712                     }
2713                     parent = parent.parentElement;
2714                 }
2715             }
2716             parent = element.parentElement;
2717             while (parent) {
2718                 if (parent.thLocalVars) {
2719                     element.thLocalVars = mergeVars(element.thLocalVars, parent.thLocalVars);
2720                     break;
2721                 }
2722                 parent = parent.parentElement;
2723             }
2724             result = func(element, arg, obj);
2725         }
2726         return result;
2727     }
2728     function unQuote(param) {
2729         var par = param, pared;
2730         if (par) {
2731             if (typeof par === "string") {
2732                 par = par.trim();
2733                 if (par.charAt(0) == '"') {
2734                     if (par.charAt(par.length - 1) == '"') {
2735                         pared = par.substring(1, par.length - 1);
2736                         if (pairParity(pared, '"', '"') == 0) {
2737                             par = pared;
2738                         }
2739                     }
2740                 } else if (par.charAt(0) == "'") {
2741                     if (par.charAt(par.length - 1) == "'") {
2742                         pared = par.substring(1, par.length - 1);
2743                         if (pairParity(pared, "'", "'") == 0) {
2744                             par = pared;
2745                         }
2746                     }
2747                 }
2748             }
2749         }
2750         return par;
2751     }
2752     function unParenthesise(param) {
2753         var par = param, pared;
2754         if (par) {
2755             if (typeof par === "string") {
2756                 par = par.trim();
2757                 if (par.charAt(0) == "(") {
2758                     if (par.charAt(par.length - 1) == ")") {
2759                         pared = par.substring(1, par.length - 1).trim();
2760                         if (pairParity(pared, "(", ")") == 0) {
2761                             par = pared;
2762                         }
2763                     }
2764                 }
2765             }
2766         }
2767         return par;
2768     }
2769     function pairParity(str, left, right) {
2770         var i, ch, strLength = str.length, parity = 0;
2771         for (i = 0; i < strLength; i++) {
2772             ch = str.charAt(i);
2773             if (ch == left) {
2774                 parity++;
2775             } else if (ch == right) {
2776                 parity--;
2777                 if (parity < 0) {
2778                     break;
2779                 }
2780             }
2781         }
2782         return parity;
2783     }
2784     function unBracket(param) {
2785         var par = param, pared;
2786         if (typeof par === "string") {
2787             par = par.trim();
2788         }
2789         if (par) {
2790             if (par.charAt(0) == "[") {
2791                 if (par.charAt(par.length - 1) == "]") {
2792                     pared = par.substring(1, par.length - 1);
2793                     if (pairParity(pared, "[", "]") == 0) {
2794                         par = pared;
2795                     }
2796                 }
2797             }
2798         }
2799         return par;
2800     }
2801     function getToPrecision(n, p) {
2802         if (typeof p === "undefined") {
2803             return n;
2804         }
2805         var up = thymol.thDefaultPrecision, ndp = 0, s, sl, dp, v;
2806         if (p > up) {
2807             up = p;
2808         } else {
2809             s = n.toString();
2810             sl = s.length;
2811             dp = s.indexOf(".");
2812             if (dp >= 0) {
2813                 ndp = sl - 1 - dp;
2814             }
2815             if (ndp > up) {
2816                 v = n.toPrecision(ndp + 1);
2817                 v = truncateDecimals(v);
2818                 s = v.toString();
2819                 sl = s.length;
2820                 if (dp >= 0) {
2821                     ndp = sl - 1 - dp;
2822                 }
2823             }
2824             if (p > ndp) {
2825                 up = p;
2826             } else if (ndp < up) {
2827                 up = ndp;
2828             }
2829         }
2830         v = parseFloat(n);
2831         v = v.toFixed(up);
2832         if (p === 0) {
2833             v = Number(v);
2834         }
2835         return v;
2836     }
2837     function truncateDecimals(valp) {
2838         var val = valp, iLimit = valp.length - 1, i;
2839         for (i = iLimit; i >= 0; i--) {
2840             if (val.charAt(i) === "0") {
2841                 val = val.substr(0, i);
2842             } else {
2843                 break;
2844             }
2845         }
2846         return val;
2847     }
2848     function getDecimalDigits(val) {
2849         var digits = 0, s, dp;
2850         s = val.toString();
2851         dp = s.indexOf(".") + 1;
2852         if (dp > 0) {
2853             digits = s.length - dp;
2854         }
2855         return digits;
2856     }
2857     function testLiteralFalse(initial) {
2858         var result = false, val;
2859         if (typeof initial === "string") {
2860             val = initial.toLowerCase();
2861             result = val == "false" || val == "off" || val == "no";
2862         } else if (typeof initial === "boolean") {
2863             result = !initial;
2864         }
2865         return result;
2866     }
2867     function renderMessage(msg, values) {
2868         var result = msg, i, iLimit;
2869         if (Object.prototype.toString.call(values) == "[object Array]") {
2870             for (i = 0, iLimit = values.length; i < iLimit; i++) {
2871                 result = renderMessageArg(result, i, values[i]);
2872             }
2873         } else {
2874             result = renderMessageArg(msg, 0, values);
2875         }
2876         return result;
2877     }
2878     function renderMessageArg(msg, index, value) {
2879         var result = msg, splits, i, iLimit, iUpper;
2880         splits = msg.split("{" + index + "}");
2881         if (splits.length > 0) {
2882             result = "";
2883             for (i = 0, iLimit = splits.length, iUpper = iLimit - 1; i < iLimit; i++) {
2884                 result += splits[i];
2885                 if (i < iUpper) {
2886                     result += value;
2887                 }
2888             }
2889         }
2890         return result;
2891     }
2892     function getParameter(name) {
2893         var result, tor;
2894         result = thymol.requestContext[name];
2895         tor = typeof result;
2896         if (tor === "undefined") {
2897             result = thymol.sessionContext[name];
2898             if (typeof result === "undefined") {
2899                 result = thymol.applicationContext[name];
2900             }
2901         } else if (tor === "object") {
2902             if (Object.prototype.toString.call(result) === "[object Array]") {
2903                 if (result.length === 1) {
2904                     result = result[0];
2905                 }
2906             }
2907         }
2908         return result;
2909     }
2910     function charOcurrences(str, chr) {
2911         var count = 0, i = 0, iLimit = str.length;
2912         for (;i < iLimit; i++) {
2913             if (str.charAt(i) === chr) {
2914                 count++;
2915             }
2916         }
2917         return count;
2918     }
2919     function isLiteral(val) {
2920         var first, last;
2921         if (typeof val === "string") {
2922             first = val.charAt(0);
2923             last = val.charAt(val.length - 1);
2924             if (first == "'" && last == "'") {
2925                 return true;
2926             }
2927             if (first == '"' && last == '"') {
2928                 return true;
2929             }
2930         }
2931         return false;
2932     }
2933     function isLiteralSubst(param) {
2934         var result = false, par = param;
2935         if (typeof par === "string") {
2936             par = par.trim();
2937         }
2938         if (par) {
2939             if (par.charAt(0) == "|") {
2940                 if (par.charAt(par.length - 1) == "|") {
2941                     result = true;
2942                 }
2943             }
2944         }
2945         return result;
2946     }
2947     function loadScript(file) {
2948         var script = thymol.Thymol.prototype.getFilePath(file);
2949         var status = "";
2950         var jqxhr = $.ajax({
2951             type: "GET",
2952             url: script,
2953             dataType: "script",
2954             cache: true,
2955             async: false
2956         }).done(function() {
2957             status = "success";
2958         }).fail(function() {
2959             status = "error";
2960         });
2961     }
2962     function unescape(text) {
2963         var result = text, i, iLimit, iUpper, c, cc;
2964         if (text !== null && typeof text !== "undefined") {
2965             result = "";
2966             iLimit = text.length;
2967             iUpper = iLimit - 3;
2968             for (i = 0; i < iLimit; i++) {
2969                 c = text.charAt(i);
2970                 if (i < iUpper) {
2971                     if (c === "&") {
2972                         cc = text.charAt(i + 1).toLowerCase();
2973                         if ((cc === "g" || cc === "l") && text.charAt(i + 2).toLowerCase() === "t" && text.charAt(i + 3) === ";") {
2974                             i += 3;
2975                             if (cc === "g") {
2976                                 c = ">";
2977                             } else {
2978                                 c = "<";
2979                             }
2980                         } else if (i < iUpper - 1 && cc === "a" && text.charAt(i + 2).toLowerCase() === "m" && text.charAt(i + 3).toLowerCase() === "p" && text.charAt(i + 4) === ";") {
2981                             i += 4;
2982                         } else if (i < iUpper - 2) {
2983                             if (cc === "q" && text.charAt(i + 2).toLowerCase() === "u" && text.charAt(i + 3).toLowerCase() === "o" && text.charAt(i + 4).toLowerCase() === "t" && text.charAt(i + 5) === ";") {
2984                                 i += 5;
2985                                 c = '"';
2986                             } else if (cc === "a" && text.charAt(i + 2).toLowerCase() === "p" && text.charAt(i + 3).toLowerCase() === "o" && text.charAt(i + 4).toLowerCase() === "s" && text.charAt(i + 5) === ";") {
2987                                 i += 5;
2988                                 c = "'";
2989                             }
2990                         }
2991                     }
2992                 }
2993                 result += c;
2994             }
2995         }
2996         return result;
2997     }
2998     function unicodeUnescape(initial) {
2999         var result = initial.replace(/\\u([\da-f]{4})/gi, function(match, grp) {
3000             return String.fromCharCode(parseInt(grp, 16));
3001         });
3002         result = unescape(result);
3003         return result;
3004     }
3005     function removeTag(element) {
3006         var parent = element.parentNode, i, iLimit, savedObject = element.thObjectVar, savedLocals = element.thLocalVars;
3007         if (!!parent) {
3008             for (i = 0, iLimit = element.childNodes.length; i < iLimit; i++) {
3009                 var newNode = element.childNodes[i].cloneNode(true);
3010                 if (newNode.nodeType === 1) {
3011                     if (!!savedObject) {
3012                         newNode.thObjectVar = savedObject;
3013                     }
3014                     if (!!savedLocals) {
3015                         newNode.thLocalVars = savedLocals;
3016                     }
3017                 }
3018                 parent.insertBefore(newNode, element);
3019             }
3020             parent.removeChild(element);
3021         }
3022     }
3023     function getRequestEncoded(initial) {
3024         var result = initial;
3025         result = encodeURIComponent(result);
3026         result = result.replace(/%20/g, "+");
3027         result = result.replace(/%26/g, "&");
3028         result = result.replace(/%3A/g, ":");
3029         result = result.replace(/!/g, "%21");
3030         result = result.replace(/'/g, "%27");
3031         result = result.replace(/\(/g, "%28");
3032         result = result.replace(/\)/g, "%29");
3033         result = result.replace(/\*/g, "%2A");
3034         result = result.replace(/~/g, "%7E");
3035         return result;
3036     }
3037     return {
3038         getParameter: getParameter,
3039         processElement: processElement,
3040         unQuote: unQuote,
3041         unParenthesise: unParenthesise,
3042         unBracket: unBracket,
3043         getToPrecision: getToPrecision,
3044         getDecimalDigits: getDecimalDigits,
3045         testLiteralFalse: testLiteralFalse,
3046         renderMessage: renderMessage,
3047         charOcurrences: charOcurrences,
3048         isLiteral: isLiteral,
3049         isLiteralSubst: isLiteralSubst,
3050         loadScript: loadScript,
3051         unescape: unescape,
3052         unicodeUnescape: unicodeUnescape,
3053         removeTag: removeTag,
3054         getRequestEncoded: getRequestEncoded
3055     };
3056 }();
3057
3058 thymol.ThParser = function(scope) {
3059     function object(o) {
3060         function F() {}
3061         F.prototype = o;
3062         return new F();
3063     }
3064     function NullReturn(varName) {
3065         this.varName = varName;
3066     }
3067     var TNUMBER = 0;
3068     var TOP1 = 1;
3069     var TOP2 = 2;
3070     var TVAR = 3;
3071     var TFUNCALL = 4;
3072     var MSGSUBST = 5;
3073     var ARGLIST = 6;
3074     function Token(type_p, index_p, prio_p, number_p, mode_p, meta_p) {
3075         this.type_ = type_p;
3076         this.index_ = index_p || 0;
3077         this.prio_ = prio_p || 0;
3078         this.number_ = number_p !== undefined && number_p !== null ? number_p : 0;
3079         this.mode_ = mode_p !== undefined && mode_p !== null ? mode_p : 0;
3080         this.meta_ = meta_p;
3081         this.toString = function() {
3082             switch (this.type_) {
3083               case TNUMBER:
3084                 return this.number_;
3085
3086               case TOP1:
3087               case TOP2:
3088               case TVAR:
3089                 return this.index_;
3090
3091               case TFUNCALL:
3092               case MSGSUBST:
3093               case ARGLIST:
3094                 return "CALL";
3095
3096               default:
3097                 return "Invalid Token";
3098             }
3099         };
3100     }
3101     function Expression(tokens, ops1, ops2, functions, precision, position) {
3102         this.tokens = tokens;
3103         this.ops1 = ops1;
3104         this.ops2 = ops2;
3105         this.functions = functions;
3106         this.precision = precision;
3107         this.position = position;
3108     }
3109     Expression.prototype = {
3110         simplify: function(valuesParam) {
3111             var values = valuesParam || {};
3112             var nstack = [];
3113             var newexpression = [];
3114             var n1;
3115             var n2;
3116             var f;
3117             var L = this.tokens.length;
3118             var item;
3119             var i = 0;
3120             for (i = 0; i < L; i++) {
3121                 item = this.tokens[i];
3122                 var type_ = item.type_;
3123                 if (type_ === TNUMBER) {
3124                     nstack.push(item);
3125                 } else if (type_ === TVAR && !(item.index_ in new Object()) && item.index_ in values) {
3126                     item = new Token(TNUMBER, 0, 0, values[item.index_]);
3127                     nstack.push(item);
3128                 } else if (type_ === TOP2 && nstack.length > 1) {
3129                     f = this.ops2[item.index_];
3130                     if (!!f) {
3131                         n2 = nstack.pop();
3132                         n1 = nstack.pop();
3133                         item = new Token(TNUMBER, 0, 0, f(n1.number_, n2.number_));
3134                     }
3135                     nstack.push(item);
3136                 } else if (type_ === TOP1 && nstack.length > 0) {
3137                     if ("{" == item.index_) {
3138                         if (item.mode_ == 2) {
3139                             nstack.push(item);
3140                         }
3141                     } else {
3142                         n1 = nstack.pop();
3143                         f = this.ops1[item.index_];
3144                         item = new Token(TNUMBER, 0, 0, f(n1.number_));
3145                         nstack.push(item);
3146                     }
3147                 } else {
3148                     while (nstack.length > 0) {
3149                         newexpression.push(nstack.shift());
3150                     }
3151                     newexpression.push(item);
3152                 }
3153             }
3154             while (nstack.length > 0) {
3155                 newexpression.push(nstack.shift());
3156             }
3157             var res = new Expression(newexpression, object(this.ops1), object(this.ops2), object(this.functions), this.precision);
3158             return res;
3159         },
3160         evaluate: function(element) {
3161             var nstack = [];
3162             var n1;
3163             var n2;
3164             var f;
3165             var res = null;
3166             var L = this.tokens.length;
3167             var item;
3168             var i = 0;
3169             var result;
3170             for (i = 0; i < L; i++) {
3171                 item = this.tokens[i];
3172                 if (i === 0 && thymol.disableMessages && item.mode_ === 4) {
3173                     var nullReturn = new thymol.ThClass();
3174                     nullReturn.abort = true;
3175                     return nullReturn;
3176                 }
3177                 var type_ = item.type_;
3178                 if (type_ === TNUMBER) {
3179                     nstack.push(item.number_);
3180                     if (i == L - 1) {
3181                         break;
3182                     }
3183                 } else if (type_ === TOP2) {
3184                     n2 = nstack.pop();
3185                     if (typeof n2 === "undefined" || n2 instanceof NullReturn) {
3186                         n2 = null;
3187                     }
3188                     n1 = nstack.pop();
3189                     if (typeof n1 === "undefined" || n1 instanceof NullReturn) {
3190                         n1 = null;
3191                     }
3192                     f = this.ops2[item.index_];
3193                     var pathMatch = false;
3194                     try {
3195                         if (item.mode_ === 6) {
3196                             if (f === dot) {
3197                                 res = n1 + "[" + n2 + "]";
3198                             } else if (f === append) {
3199                                 if (!!item.meta_) {
3200                                     if (!!item.meta_.paths) {
3201                                         var values = item.meta_.paths[n1];
3202                                         if (!!values) {
3203                                             values.push(n2);
3204                                             pathMatch = true;
3205                                             res = null;
3206                                         }
3207                                     }
3208                                 }
3209                                 if (!pathMatch) {
3210                                     if (!item.meta_) {
3211                                         item.meta_ = {};
3212                                     }
3213                                     if (!item.meta_.params) {
3214                                         item.meta_.params = [];
3215                                     }
3216                                     var values = item.meta_.params[n1];
3217                                     if (!values) {
3218                                         values = [];
3219                                         item.meta_.params[n1] = values;
3220                                     }
3221                                     values.push(n2);
3222                                     pathMatch = true;
3223                                 }
3224                             } else {
3225                                 res = n2;
3226                                 nstack.push(n1);
3227                             }
3228                         } else {
3229                             if (f === dot && "class" === n2 && !!n1 && !n1["class"]) {
3230                                 var tn2 = typeof n2;
3231                                 if (tn2 === "object" && n2 instanceof thymol.ThParam) {
3232                                     res = f(n1, n2);
3233                                 } else {
3234                                     res = new thymol.ThClass("JavaScript:" + tn2);
3235                                 }
3236                             } else {
3237                                 res = f(n1, n2);
3238                                 if (typeof res === "function") {
3239                                     if (L - 1 > i) {
3240                                         next = this.tokens[i + 1];
3241                                         if (next.type_ === TNUMBER && Object.prototype.toString.call(next.number_) == "[object Array]" && next.number_.length == 0) {
3242                                             i += 1;
3243                                             nstack.push(res);
3244                                             n1.isDirect = true;
3245                                             res = n1;
3246                                         }
3247                                     }
3248                                 }
3249                             }
3250                             if (f !== append) {
3251                                 if (Object.prototype.toString.call(res) == "[object Array]") {
3252                                     res.arrayResult = true;
3253                                 }
3254                             }
3255                         }
3256                     } catch (err) {
3257                         if (!element.isBlockChild) {
3258                             var aValue = n1 == null ? "null" : n1;
3259                             var bValue = n2 == null ? "null" : n2;
3260                             var message = "while evaluating expression: " + this.tokens[i - 2].index_ + ": " + aValue + ", " + this.tokens[i - 1].index_ + ": " + bValue;
3261                             throw new thymol.ThError(message, element, err);
3262                         }
3263                     }
3264                     if (!pathMatch) {
3265                         nstack.push(res);
3266                     }
3267                 } else if (type_ === TVAR) {
3268                     var next = null, pushed = nstack.length;
3269                     if (item.index_ != null) {
3270                         if (L - 1 > i) {
3271                             next = this.tokens[i + 1];
3272                             if (next.type_ === TOP2 && next.index_ === ".") {
3273                                 nstack.push(item.index_);
3274                             }
3275                         }
3276                         if (pushed === nstack.length) {
3277                             var val = thymol.substituteParam(item.index_, item.mode_, element);
3278                             if (Object.prototype.toString.call(val) == "[object Array]") {
3279                                 val.arrayResult = true;
3280                             }
3281                             this.updatePrecision(val);
3282                             if (val === null) {
3283                                 val = new NullReturn(item.index_);
3284                             }
3285                             nstack.push(val);
3286                         }
3287                     } else if (pushed === nstack.length && item.index_ in this.functions) {
3288                         nstack.push(this.functions[item.index_]);
3289                     } else {
3290                         if (!element.isBlockChild) {
3291                             throw new thymol.ThError("Exception undefined variable: " + item.index_, element);
3292                         }
3293                     }
3294                 } else if (type_ === TOP1) {
3295                     n1 = nstack.pop();
3296                     if (typeof n1 === "undefined" || n1 instanceof NullReturn) {
3297                         if (item.mode_ === 2) {
3298                             n1 = "";
3299                         } else {
3300                             n1 = null;
3301                         }
3302                     }
3303                     res = n1;
3304                     if ("{" === item.index_) {
3305                         var prev = this.tokens[i - 1];
3306                         if (prev.mode_ == 7) {
3307                             if (thymol.conversionService) {
3308                                 n1 = thymol.conversionService(n1);
3309                                 res = n1;
3310                             }
3311                         }
3312                         if (typeof n1 === "string") {
3313                             if (item.mode_ === 2) {
3314                                 res = thymol.getStandardURL(n1);
3315                             } else {
3316                                 var subst = thymol.substituteParam(n1, item.mode_, element);
3317                                 if (subst != null) {
3318                                     this.updatePrecision(subst);
3319                                     res = subst;
3320                                 }
3321                             }
3322                         }
3323                     } else {
3324                         f = this.ops1[item.index_];
3325                         try {
3326                             res = f(n1);
3327                         } catch (err) {
3328                             if (!element.isBlockChild) {
3329                                 var aValue = n1 == null ? "null" : n1;
3330                                 var message = "while evaluating expression: " + this.tokens[i - 2].index_ + ": " + aValue;
3331                                 throw new thymol.ThError(message, element, err);
3332                             }
3333                         }
3334                     }
3335                     if (Object.prototype.toString.call(res) == "[object Array]") {
3336                         res.arrayResult = true;
3337                     }
3338                     nstack.push(res);
3339                 } else if (type_ === TFUNCALL || type_ === MSGSUBST || type_ === ARGLIST) {
3340                     n1 = nstack.pop();
3341                     f = nstack.pop();
3342                     if (type_ === MSGSUBST) {
3343                         if (f instanceof NullReturn) {
3344                             res = "??" + f.varName + "_" + thymol.locale.value + "??";
3345                         } else {
3346                             res = thymol.ThUtils.renderMessage(f, n1);
3347                         }
3348                         nstack.push(res);
3349                     } else if (type_ === ARGLIST) {
3350                         var pathMatch = false;
3351                         n2 = nstack.pop();
3352                         if (typeof n2 === "undefined") {
3353                             n2 = f;
3354                             f = n1;
3355                             n1 = "";
3356                         }
3357                         if (!!item.meta_) {
3358                             if (!!item.meta_.paths) {
3359                                 var values = item.meta_.paths[f];
3360                                 if (!!values) {
3361                                     values.push(n1);
3362                                     pathMatch = true;
3363                                 }
3364                                 for (var j in item.meta_.paths) {
3365                                     if (item.meta_.paths.hasOwnProperty(j)) {
3366                                         var values = item.meta_.paths[j];
3367                                         var isReq = n2.indexOf("?") >= 0;
3368                                         if (!!values && values.length > 0) {
3369                                             var pathVar = "{" + j + "}";
3370                                             var repReg = new RegExp(pathVar, "g");
3371                                             var rep = "";
3372                                             values.reverse();
3373                                             for (var k = 0, kLimit = values.length; k < kLimit; k++) {
3374                                                 if (rep.length > 0) {
3375                                                     rep = rep + ",";
3376                                                 }
3377                                                 if (isReq) {
3378                                                     rep = rep + thymol.ThUtils.getRequestEncoded(values[k]);
3379                                                 } else {
3380                                                     rep = rep + encodeURIComponent(values[k]);
3381                                                 }
3382                                             }
3383                                             n2 = n2.replace(repReg, rep);
3384                                         }
3385                                     }
3386                                 }
3387                             }
3388                         }
3389                         if (pathMatch) {
3390                             res = n2;
3391                         } else {
3392                             if (typeof f === "undefined" || f instanceof NullReturn) {
3393                                 f = "";
3394                             } else {
3395                                 f = f.toString();
3396                             }
3397                             f = thymol.ThUtils.getRequestEncoded(f);
3398                             f = "?" + f;
3399                             n1 = n1.toString();
3400                             if (f != "?" && n1 != "") {
3401                                 f = f + "=";
3402                             }
3403                             if (n1 != "") {
3404                                 n1 = thymol.ThUtils.getRequestEncoded(n1);
3405                                 f = f + n1;
3406                             }
3407                             if (typeof n2 === "undefined" || n2 instanceof NullReturn) {
3408                                 n2 = "";
3409                             } else {
3410                                 n2 = n2.toString();
3411                             }
3412                             res = n2 + f;
3413                         }
3414                         if (!!item.meta_) {
3415                             var separator = res.indexOf("?") >= 0 ? "&" : "?";
3416                             for (var j in item.meta_.params) {
3417                                 if (item.meta_.params.hasOwnProperty(j)) {
3418                                     var values = item.meta_.params[j];
3419                                     if (!!values && values.length > 0) {
3420                                         for (var k = 0, kLimit = values.length; k < kLimit; k++) {
3421                                             res = res + separator + thymol.ThUtils.getRequestEncoded(j) + "=" + thymol.ThUtils.getRequestEncoded(values[k]);
3422                                             if (k == 0) {
3423                                                 separator = "&";
3424                                             }
3425                                         }
3426                                     }
3427                                 }
3428                             }
3429                             if (!!item.meta_.urlFragment) {
3430                                 res = res + item.meta_.urlFragment;
3431                             }
3432                         }
3433                         nstack.push(res);
3434                     } else if (f.apply && f.call) {
3435                         if (!!n1 && !!n1.isDirect) {
3436                             res = f.call(n1);
3437                         } else {
3438                             if (n1 instanceof NullReturn) {
3439                                 n1 = null;
3440                             }
3441                             if (n1 != null && (n1.arrayResult || Object.prototype.toString.call(n1) !== "[object Array]")) {
3442                                 res = f.call(element, n1);
3443                             } else {
3444                                 res = f.apply(element, n1);
3445                             }
3446                         }
3447                         if (res instanceof String) {
3448                             if (res.precision) {
3449                                 if (typeof this.precision === "undefined" || res.precision > this.precision) {
3450                                     this.precision = res.precision;
3451                                 }
3452                             }
3453                             res = res.toString();
3454                         } else if (Object.prototype.toString.call(res) == "[object Array]") {
3455                             res.arrayResult = true;
3456                         }
3457                         nstack.push(res);
3458                     } else {
3459                         if (!element.isBlockChild) {
3460                             throw new thymol.ThError(f + " is not a function", element);
3461                         }
3462                     }
3463                 } else {
3464                     if (!element.isBlockChild) {
3465                         throw new thymol.ThError("invalid expression item type: " + type_, element);
3466                     }
3467                 }
3468             }
3469             if (nstack.length > 1) {
3470                 if (!element.isBlockChild) {
3471                     throw new thymol.ThError("invalid Expression (parity)", element);
3472                 }
3473             }
3474             result = nstack[0];
3475             return result;
3476         },
3477         updatePrecision: function(val) {
3478             if (typeof val === "number") {
3479                 var p = thymol.ThUtils.getDecimalDigits(val);
3480                 if (typeof this.precision === "undefined" || p > this.precision) {
3481                     this.precision = p;
3482                 }
3483             }
3484         }
3485     };
3486     function add(a, b) {
3487         return a + b;
3488     }
3489     function assign(a) {
3490         return a;
3491     }
3492     function sub(a, b) {
3493         return a - b;
3494     }
3495     function mul(a, b) {
3496         return a * b;
3497     }
3498     function div(a, b) {
3499         return a / b;
3500     }
3501     function mod(a, b) {
3502         return a % b;
3503     }
3504     function concat(a, b) {
3505         return "" + a + b;
3506     }
3507     function neg(a) {
3508         return -a;
3509     }
3510     function not(a) {
3511         var v = thymol.getBooleanValue(a);
3512         return !v;
3513     }
3514     function random(a) {
3515         return Math.random() * (a || 1);
3516     }
3517     function fac(a) {
3518         var aa = Math.floor(a);
3519         var b = aa;
3520         while (aa > 1) {
3521             b = b * --aa;
3522         }
3523         return b;
3524     }
3525     function append(a, b) {
3526         if (a != null) {
3527             if (a.arrayResult === true || Object.prototype.toString.call(a) != "[object Array]") {
3528                 return [ a, b ];
3529             }
3530         } else {
3531             if (b != null) {
3532                 if (b.arrayResult === true || Object.prototype.toString.call(b) != "[object Array]") {
3533                     return [ a, b ];
3534                 }
3535                 return b;
3536             }
3537             return null;
3538         }
3539         var aa = a.slice();
3540         aa.push(b);
3541         return aa;
3542     }
3543     function equal(a, b) {
3544         return a == b;
3545     }
3546     function notEqual(a, b) {
3547         return a != b;
3548     }
3549     function gt(a, b) {
3550         return a > b;
3551     }
3552     function ge(a, b) {
3553         return a >= b;
3554     }
3555     function lt(a, b) {
3556         return a < b;
3557     }
3558     function le(a, b) {
3559         return a <= b;
3560     }
3561     function and(a, b) {
3562         return a && b;
3563     }
3564     function or(a, b) {
3565         return a || b;
3566     }
3567     function dot(a, b) {
3568         return a[b];
3569     }
3570     function binary(a, b) {
3571         return a ? b : null;
3572     }
3573     function elvis(a, b) {
3574         return a != null ? a : b;
3575     }
3576     function getStr(pos, expression, mode, partial, preprocessed) {
3577         var localMode = mode;
3578         var s = "";
3579         var c = expression.charAt(pos);
3580         var start = pos + 1;
3581         var end = expression.length;
3582         var stopChar = c;
3583         if (localMode === 4 || c === "#") {
3584             stopChar = "}";
3585             localMode = 4;
3586         }
3587         var i = start;
3588         var inCurly = false;
3589         var curlyPos = null;
3590         var urlFragPos = -1;
3591         var meta = null;
3592         if (localMode !== 4 && c !== "'" && c !== '"') {
3593             for (;i <= end; i++) {
3594                 if (c.toUpperCase() === c.toLowerCase()) {
3595                     if (c === "{") {
3596                         inCurly = true;
3597                         curlyPos = i;
3598                         if (meta === null) {
3599                             meta = {};
3600                             meta.paths = [];
3601                         }
3602                     } else if (mode === 2 && c === "#") {
3603                         urlFragPos = i;
3604                     }
3605                     if (i === pos || !inCurly && c === "}" || c !== "_" && c !== "?" && c !== ":" && (c < "0" || c > "9")) {
3606                         if (!(partial && c == "-") && !((mode === 2 || mode === 6) && (c === "/" || c === "." || c === "~" || c === "?" || c === "=" || c === ":" || c === "-" || c === "_" || c === "[" || c === "]" || c === "#" || inCurly && c === "{" || inCurly && c === "}")) || mode === 6 && c === "=") {
3607                             i = i - 1;
3608                             break;
3609                         }
3610                     }
3611                     if (inCurly && c === "}") {
3612                         inCurly = false;
3613                         if (meta === null) {
3614                             var message = 'bad path variable definition in expression: "' + expression + '" near column ' + pos;
3615                             throw new thymol.ThError(message, element);
3616                         }
3617                         var curlyVar = expression.substring(curlyPos, i - 1);
3618                         var values = [];
3619                         meta.paths[curlyVar] = values;
3620                     }
3621                 }
3622                 s += c;
3623                 c = expression.charAt(i);
3624             }
3625             if (urlFragPos >= 0) {
3626                 var urlFrag = expression.substring(urlFragPos - 1, i);
3627                 s = s.substring(0, s.length - urlFrag.length);
3628                 if (meta === null) {
3629                     meta = {};
3630                     meta.urlFragment = urlFrag;
3631                 }
3632             }
3633         } else {
3634             var quoted = false;
3635             var preprocessing = false;
3636             if (c === "'" || c === '"') {
3637                 quoted = true;
3638                 stopChar = c;
3639             }
3640             while (i <= end) {
3641                 if (c === stopChar && i > start && !preprocessing) {
3642                     if (localMode !== 4 || quoted) {
3643                         s += c;
3644                     } else {
3645                         i = i - 1;
3646                     }
3647                     break;
3648                 }
3649                 var nc = expression.charAt(i);
3650                 if (c === "_" && nc === "_" && !preprocessed) {
3651                     preprocessing = !preprocessing;
3652                 }
3653                 if (c === "\\") {
3654                     if (nc === "'" && s.charAt(s.length - 1) !== "\\") {
3655                         c = "&#39;";
3656                         if (i + 1 > end) {
3657                             break;
3658                         }
3659                         i = i + 1;
3660                         nc = expression.charAt(i);
3661                     }
3662                 }
3663                 if (!quoted) {
3664                     if (c === ".") {
3665                         var exp = thymol.thExpressionObjects[s];
3666                         if (typeof exp !== "undefined" && exp !== null) {
3667                             i -= 1;
3668                             break;
3669                         }
3670                     }
3671                     if (c === "(") {
3672                         i -= 1;
3673                         break;
3674                     }
3675                 }
3676                 s += c;
3677                 if (i + 1 > end) {
3678                     break;
3679                 }
3680                 i = i + 1;
3681                 c = nc;
3682             }
3683         }
3684         var str = new Object();
3685         str.str = s;
3686         str.pos = i;
3687         if (meta !== null) {
3688             str.meta = meta;
3689         }
3690         return str;
3691     }
3692     function ThParser() {
3693         this.precision;
3694         this.success = false;
3695         this.errormsg = "";
3696         this.expression = "";
3697         this.pos = 0;
3698         this.tokennumber = 0;
3699         this.tokenprio = 0;
3700         this.tokenindex = 0;
3701         this.tmpprio = 0;
3702         this.ops1 = {
3703             sin: Math.sin,
3704             cos: Math.cos,
3705             tan: Math.tan,
3706             asin: Math.asin,
3707             acos: Math.acos,
3708             atan: Math.atan,
3709             sqrt: Math.sqrt,
3710             log: Math.log,
3711             abs: Math.abs,
3712             ceil: Math.ceil,
3713             floor: Math.floor,
3714             round: Math.round,
3715             "-": neg,
3716             "!": not,
3717             not: not,
3718             exp: Math.exp,
3719             "=": assign
3720         };
3721         this.ops2 = {
3722             "?": binary,
3723             ":": elvis,
3724             "?:": elvis,
3725             "+": add,
3726             "-": sub,
3727             "*": mul,
3728             "/": div,
3729             "%": mod,
3730             "^": Math.pow,
3731             ",": append,
3732             "||": concat,
3733             "==": equal,
3734             eq: equal,
3735             "!=": notEqual,
3736             ne: notEqual,
3737             neq: notEqual,
3738             div: div,
3739             mod: mod,
3740             and: and,
3741             or: or,
3742             ">": gt,
3743             gt: gt,
3744             ">=": ge,
3745             "=>": ge,
3746             ge: ge,
3747             "<": lt,
3748             lt: lt,
3749             "<=": le,
3750             "=<": le,
3751             le: le,
3752             ".": dot,
3753             "[": dot
3754         };
3755         this.functions = {
3756             random: random,
3757             fac: fac,
3758             min: Math.min,
3759             max: Math.max,
3760             pow: Math.pow
3761         };
3762         this.consts = {
3763             E: Math.E,
3764             PI: Math.PI
3765         };
3766     }
3767     ThParser.parse = function(expr, partial, preprocessed) {
3768         return new thymol.ThParser().parse(expr, partial, preprocessed);
3769     };
3770     ThParser.evaluate = function(expr, partial, element) {
3771         return thymol.ThParser.parse(expr, partial, false).evaluate(element);
3772     };
3773     ThParser.Expression = Expression;
3774     ThParser.values = {
3775         sin: Math.sin,
3776         cos: Math.cos,
3777         tan: Math.tan,
3778         asin: Math.asin,
3779         acos: Math.acos,
3780         atan: Math.atan,
3781         sqrt: Math.sqrt,
3782         log: Math.log,
3783         abs: Math.abs,
3784         ceil: Math.ceil,
3785         floor: Math.floor,
3786         round: Math.round,
3787         random: random,
3788         fac: fac,
3789         exp: Math.exp,
3790         min: Math.min,
3791         max: Math.max,
3792         pow: Math.pow,
3793         E: Math.E,
3794         PI: Math.PI
3795     };
3796     var PRIMARY = 1 << 0;
3797     var OPERATOR = 1 << 1;
3798     var FUNCTION = 1 << 2;
3799     var LPAREN = 1 << 3;
3800     var RPAREN = 1 << 4;
3801     var COMMA = 1 << 5;
3802     var SIGN = 1 << 6;
3803     var CALL = 1 << 7;
3804     var NULLARY_CALL = 1 << 8;
3805     var LBRACK = 1 << 9;
3806     var RBRACK = 1 << 10;
3807     var LVARBRK = 1 << 11;
3808     var RVARBRK = 1 << 11;
3809     var OPTION = 1 << 12;
3810     var ASSIGN = 1 << 13;
3811     ThParser.prototype = {
3812         parse: function(expr, partial, preprocessed) {
3813             this.errormsg = "";
3814             this.success = true;
3815             var operstack = [];
3816             var tokenstack = [];
3817             var modestack = [];
3818             this.tmpprio = 0;
3819             var expected = PRIMARY | LPAREN | LVARBRK | FUNCTION | OPERATOR | SIGN | OPTION;
3820             var noperators = 0;
3821             this.expression = expr;
3822             this.pos = 0;
3823             this.mode = 0;
3824             while (this.pos < this.expression.length) {
3825                 if (this.isWhite()) {} else if (this.isOperator()) {
3826                     if (this.isSign() && expected & SIGN) {
3827                         if (this.isNegativeSign()) {
3828                             this.tokenprio = 6;
3829                             this.tokenindex = "-";
3830                             noperators++;
3831                             this.addfunc(tokenstack, operstack, TOP1);
3832                         }
3833                         expected = PRIMARY | LPAREN | LVARBRK | FUNCTION | SIGN | OPTION;
3834                     } else if (this.isAssign() && expected & ASSIGN) {
3835                         noperators++;
3836                         expected = PRIMARY | LPAREN | LVARBRK | FUNCTION | SIGN | OPTION;
3837                     } else if (this.isComment()) {} else {
3838                         if (this.tokenindex == "!") {
3839                             if ((expected & SIGN) === 0) {
3840                                 this.error_parsing(this.pos, "unexpected sign");
3841                             }
3842                             noperators += 1;
3843                             this.addfunc(tokenstack, operstack, TOP1);
3844                         } else {
3845                             if ((expected & OPERATOR) === 0) {
3846                                 this.error_parsing(this.pos, "unexpected operator");
3847                             }
3848                             noperators += 2;
3849                             this.addfunc(tokenstack, operstack, TOP2);
3850                         }
3851                         if (this.expression.charAt(this.pos - 1) === "[") {
3852                             this.tmpprio += 20;
3853                         }
3854                         expected = PRIMARY | OPERATOR | LPAREN | LVARBRK | FUNCTION | SIGN | OPTION;
3855                     }
3856                 } else if (this.isNumber()) {
3857                     if ((expected & PRIMARY) === 0) {
3858                         this.error_parsing(this.pos, "unexpected number");
3859                     }
3860                     var token = new Token(TNUMBER, 0, 0, this.tokennumber);
3861                     tokenstack.push(token);
3862                     expected = OPERATOR | RPAREN | RBRACK | RVARBRK | COMMA;
3863                 } else if (this.isLeftParenth()) {
3864                     if ((expected & LPAREN) === 0) {
3865                         this.error_parsing(this.pos, 'unexpected "("');
3866                     }
3867                     modestack.push(this.mode);
3868                     if (expected & CALL) {
3869                         noperators += 2;
3870                         this.tokenprio = -2;
3871                         this.tokenindex = -1;
3872                         this.tmpprio += 2;
3873                         var ft = TFUNCALL;
3874                         if (this.mode === 4) {
3875                             ft = MSGSUBST;
3876                             this.mode = 5;
3877                         } else if (this.mode === 2) {
3878                             ft = ARGLIST;
3879                             this.mode = 6;
3880                             var url = tokenstack[tokenstack.length - 1];
3881                             if (!url.meta_) {
3882                                 url.meta_ = {};
3883                             }
3884                             this.meta = url.meta_;
3885                         } else {
3886                             this.mode = 5;
3887                         }
3888                         this.addfunc(tokenstack, operstack, ft);
3889                         this.tmpprio -= 2;
3890                     }
3891                     if (this.mode === 5 || this.mode === 6) {
3892                         this.tmpprio += 10;
3893                     }
3894                     expected = PRIMARY | OPERATOR | LPAREN | LVARBRK | FUNCTION | SIGN | OPTION | NULLARY_CALL;
3895                 } else if (this.isRightParenth()) {
3896                     if (expected & NULLARY_CALL) {
3897                         var token = new Token(TNUMBER, 0, 0, []);
3898                         tokenstack.push(token);
3899                     } else if ((expected & RPAREN) === 0) {
3900                         this.error_parsing(this.pos, 'unexpected ")"');
3901                     }
3902                     if (this.mode === 5 || this.mode === 6) {
3903                         this.tmpprio -= 10;
3904                     }
3905                     this.mode = modestack.pop();
3906                     expected = OPERATOR | RPAREN | RBRACK | RVARBRK | COMMA | LPAREN | LVARBRK | CALL | OPTION;
3907                 } else if (this.isRightBracket()) {
3908                     if ((expected & RBRACK) === 0) {
3909                         this.error_parsing(this.pos, 'unexpected "]"');
3910                     }
3911                     expected = OPERATOR | RPAREN | RBRACK | RVARBRK | COMMA | LPAREN | LVARBRK | CALL | OPTION;
3912                 } else if (this.isLeftVarBrk(modestack)) {
3913                     if ((expected & LVARBRK) === 0) {
3914                         this.error_parsing(this.pos, 'unexpected "{"');
3915                     }
3916                     noperators += 1;
3917                     this.addfunc(tokenstack, operstack, TOP1);
3918                     expected = PRIMARY | LPAREN | LVARBRK | FUNCTION | SIGN | OPTION;
3919                 } else if (this.isRightVarBrk()) {
3920                     if ((expected & RVARBRK) === 0) {
3921                         this.error_parsing(this.pos, 'unexpected "}"');
3922                     }
3923                     this.mode = modestack.pop();
3924                     expected = FUNCTION | OPERATOR | RPAREN | RBRACK | RVARBRK | COMMA | LPAREN | LVARBRK | CALL | OPTION;
3925                 } else if (this.isLeftCurly()) {
3926                     if (this.mode == 1 || this.mode == 2 || this.mode == 3 || this.mode == 4) {
3927                         modestack.push(this.mode);
3928                         this.mode = 7;
3929                     } else {
3930                         this.error_parsing(this.pos, 'unexpected "{"');
3931                     }
3932                 } else if (this.isRightCurly()) {
3933                     if (this.mode == 7) {
3934                         this.mode = modestack.pop();
3935                     } else {
3936                         this.error_parsing(this.pos, 'unexpected "}"');
3937                     }
3938                 } else if (this.isComma()) {
3939                     if ((expected & COMMA) === 0) {
3940                         this.error_parsing(this.pos, 'unexpected ","');
3941                     }
3942                     if (!!partial) {
3943                         break;
3944                     }
3945                     if (this.mode === 5 || this.mode === 6) {
3946                         this.tmpprio -= 10;
3947                     }
3948                     this.tmpprio += 2;
3949                     this.addfunc(tokenstack, operstack, TOP2);
3950                     this.tmpprio -= 2;
3951                     if (this.mode === 5 || this.mode === 6) {
3952                         this.tmpprio += 10;
3953                     }
3954                     noperators += 2;
3955                     expected = PRIMARY | LPAREN | LVARBRK | FUNCTION | SIGN | OPTION;
3956                 } else if (this.isConst()) {
3957                     if ((expected & PRIMARY) === 0) {
3958                         this.error_parsing(this.pos, "unexpected constant");
3959                     }
3960                     var consttoken = new Token(TNUMBER, 0, 0, this.tokennumber);
3961                     tokenstack.push(consttoken);
3962                     expected = OPERATOR | RPAREN | RVARBRK | RBRACK | COMMA;
3963                 } else {
3964                     var str = getStr(this.pos, this.expression, this.mode, partial, preprocessed);
3965                     if (this.isOpX(str, this.ops2) && (this.mode !== 2 && "/" !== str)) {
3966                         if ("and" === str.str || "or" === str.str) {
3967                             this.tokenprio = 3;
3968                         }
3969                         if ((expected & OPERATOR) === 0) {
3970                             this.error_parsing(this.pos, "unexpected binary operator");
3971                         }
3972                         this.addfunc(tokenstack, operstack, TOP2);
3973                         noperators += 2;
3974                         expected = PRIMARY | LPAREN | LVARBRK | FUNCTION | OPERATOR | SIGN | OPTION;
3975                     } else if (this.isOpX(str, this.ops1)) {
3976                         if ((expected & OPERATOR) === 0) {
3977                             this.error_parsing(this.pos, "unexpected unary operator");
3978                         }
3979                         this.addfunc(tokenstack, operstack, TOP1);
3980                         noperators++;
3981                         expected = PRIMARY | LPAREN | LVARBRK | FUNCTION;
3982                     } else if (this.isLiteralValue(str)) {
3983                         if ((expected & PRIMARY) === 0) {
3984                             this.error_parsing(this.pos, "unexpected literal value");
3985                         }
3986                         var token = new Token(TNUMBER, 0, 0, this.tokennumber);
3987                         tokenstack.push(token);
3988                         expected = FUNCTION | OPERATOR | RPAREN | RBRACK | RVARBRK | COMMA | LPAREN | RVARBRK | LBRACK | CALL | OPTION;
3989                         if (this.mode === 6) {
3990                             expected = expected | ASSIGN;
3991                         }
3992                     } else if (this.isVar(str)) {
3993                         if ((expected & PRIMARY) === 0) {
3994                             this.error_parsing(this.pos, "unexpected variable");
3995                         }
3996                         var vartoken = new Token(TVAR, this.tokenindex, 0, 0, this.mode, str.meta);
3997                         tokenstack.push(vartoken);
3998                         expected = FUNCTION | OPERATOR | RPAREN | RBRACK | RVARBRK | COMMA | LPAREN | RVARBRK | LBRACK | CALL | OPTION | ASSIGN;
3999                     } else {
4000                         if (this.errormsg === "") {
4001                             this.error_parsing(this.pos, "unknown character");
4002                         } else {
4003                             this.error_parsing(this.pos, this.errormsg);
4004                         }
4005                     }
4006                 }
4007             }
4008             if (this.tmpprio < 0 || this.tmpprio >= 10) {
4009                 this.error_parsing(this.pos, 'unmatched "() or []"');
4010             }
4011             while (operstack.length > 0) {
4012                 var tmp = operstack.pop();
4013                 tokenstack.push(tmp);
4014             }
4015             if (noperators + 1 !== tokenstack.length) {
4016                 this.error_parsing(this.pos, "parity");
4017             }
4018             var res = new Expression(tokenstack, object(this.ops1), object(this.ops2), object(this.functions), this.precision, this.pos);
4019             return res;
4020         },
4021         evaluate: function(expr, element) {
4022             return this.parse(expr).evaluate(element);
4023         },
4024         error_parsing: function(column, msg) {
4025             this.success = false;
4026             this.errormsg = "parse error [column " + column + "]: " + msg;
4027             throw new Error(this.errormsg);
4028         },
4029         addfunc: function(tokenstack, operstack, type_) {
4030             var operator = new Token(type_, this.tokenindex, this.tokenprio + this.tmpprio, 0, this.mode, this.meta);
4031             while (operstack.length > 0) {
4032                 if (operator.prio_ <= operstack[operstack.length - 1].prio_) {
4033                     tokenstack.push(operstack.pop());
4034                 } else {
4035                     break;
4036                 }
4037             }
4038             operstack.push(operator);
4039         },
4040         isNumber: function() {
4041             var r = false;
4042             var str = "";
4043             var prec = -1;
4044             while (this.pos < this.expression.length) {
4045                 var code = this.expression.charCodeAt(this.pos);
4046                 if (code >= 48 && code <= 57 || code === 46) {
4047                     str += this.expression.charAt(this.pos);
4048                     if (prec >= 0 || code === 46) {
4049                         prec++;
4050                     }
4051                     this.pos++;
4052                     r = true;
4053                 } else {
4054                     break;
4055                 }
4056             }
4057             if (r) {
4058                 if (prec >= 0 && (typeof this.precision === "undefined" || prec > this.precision)) {
4059                     this.precision = prec;
4060                 }
4061                 this.tokennumber = parseFloat(str);
4062             }
4063             return r;
4064         },
4065         isConst: function() {
4066             var str;
4067             for (var i in this.consts) {
4068                 if (true) {
4069                     var L = i.length;
4070                     str = this.expression.substr(this.pos, L);
4071                     if (i === str) {
4072                         this.tokennumber = this.consts[i];
4073                         this.pos += L;
4074                         return true;
4075                     }
4076                 }
4077             }
4078             return false;
4079         },
4080         isOperator: function() {
4081             var ch = this.expression.charAt(this.pos);
4082             if (ch === "+") {
4083                 this.tokenprio = 0;
4084                 this.tokenindex = "+";
4085             } else if (ch === "-") {
4086                 this.tokenprio = 0;
4087                 this.tokenindex = "-";
4088             } else if (ch === "|") {
4089                 if (this.expression.charAt(this.pos + 1) === "|") {
4090                     this.pos++;
4091                     this.tokenprio = 0;
4092                     this.tokenindex = "||";
4093                 } else {
4094                     return false;
4095                 }
4096             } else if (ch === "*") {
4097                 if (this.expression.charAt(this.pos + 1) === "{") {
4098                     return false;
4099                 }
4100                 this.tokenprio = 1;
4101                 this.tokenindex = "*";
4102             } else if (ch === "/" && this.mode != 2 && this.pos > 0) {
4103                 this.tokenprio = 2;
4104                 this.tokenindex = "/";
4105             } else if (ch === "%") {
4106                 this.tokenprio = 2;
4107                 this.tokenindex = "%";
4108             } else if (ch === "^") {
4109                 this.tokenprio = 3;
4110                 this.tokenindex = "^";
4111             } else if (ch === "=" || ch === "!") {
4112                 if (this.expression.charAt(this.pos + 1) === "=") {
4113                     if (ch === "=") {
4114                         this.tokenindex = "==";
4115                     } else if (ch === "!") {
4116                         this.tokenindex = "!=";
4117                     } else {
4118                         return false;
4119                     }
4120                     this.pos++;
4121                     this.tokenprio = 6;
4122                 } else if (ch === "!") {
4123                     this.tokenprio = 7;
4124                     this.tokenindex = "!";
4125                 } else if (ch === "=") {
4126                     this.tokenindex = "=";
4127                 } else {
4128                     return false;
4129                 }
4130             } else if (ch === "<") {
4131                 if (this.expression.charAt(this.pos + 1) === "=") {
4132                     this.tokenindex = "<=";
4133                     this.pos++;
4134                 } else {
4135                     this.tokenindex = "<";
4136                 }
4137                 this.tokenprio = 4;
4138             } else if (ch === ">") {
4139                 if (this.expression.charAt(this.pos + 1) === "=") {
4140                     this.tokenindex = ">=";
4141                     this.pos++;
4142                 } else {
4143                     this.tokenindex = ">";
4144                 }
4145                 this.tokenprio = 4;
4146             } else if (ch === "." || ch === "[") {
4147                 this.tokenprio = 10;
4148                 this.tokenindex = ".";
4149             } else {
4150                 return false;
4151             }
4152             this.pos++;
4153             return true;
4154         },
4155         isRightBracket: function() {
4156             var code = this.expression.charCodeAt(this.pos);
4157             if (code === 93) {
4158                 this.pos++;
4159                 this.tmpprio -= 20;
4160                 return true;
4161             }
4162             return false;
4163         },
4164         isSign: function() {
4165             var code = this.expression.charCodeAt(this.pos - 1);
4166             if (code === 45 || code === 43) {
4167                 return true;
4168             }
4169             return false;
4170         },
4171         isAssign: function() {
4172             var code = this.expression.charCodeAt(this.pos - 1);
4173             if (code === 61) {
4174                 var cha = this.expression.charAt(this.pos - 2);
4175                 if (cha === "!" || cha === ">" || cha === "<" || cha === "=") {
4176                     return false;
4177                 }
4178                 cha = this.expression.charAt(this.pos);
4179                 if (cha === ">" || cha === "<" || cha === "=") {
4180                     return false;
4181                 }
4182                 return true;
4183             }
4184             return false;
4185         },
4186         isPositiveSign: function() {
4187             var code = this.expression.charCodeAt(this.pos - 1);
4188             if (code === 43) {
4189                 return true;
4190             }
4191             return false;
4192         },
4193         isNegativeSign: function() {
4194             var code = this.expression.charCodeAt(this.pos - 1);
4195             if (code === 45) {
4196                 return true;
4197             }
4198             return false;
4199         },
4200         isLeftParenth: function() {
4201             var code = this.expression.charCodeAt(this.pos);
4202             if (code === 40) {
4203                 this.pos++;
4204                 this.tmpprio += 10;
4205                 return true;
4206             }
4207             return false;
4208         },
4209         isRightParenth: function() {
4210             var code = this.expression.charCodeAt(this.pos);
4211             if (code === 41) {
4212                 this.pos++;
4213                 this.tmpprio -= 10;
4214                 return true;
4215             }
4216             return false;
4217         },
4218         isLeftCurly: function() {
4219             var code = this.expression.charCodeAt(this.pos);
4220             if (code === 123) {
4221                 this.pos++;
4222                 this.tmpprio += 10;
4223                 return true;
4224             }
4225             return false;
4226         },
4227         isRightCurly: function() {
4228             var code = this.expression.charCodeAt(this.pos);
4229             if (code === 125) {
4230                 this.pos++;
4231                 this.tmpprio -= 10;
4232                 return true;
4233             }
4234             return false;
4235         },
4236         isComma: function() {
4237             var code = this.expression.charCodeAt(this.pos);
4238             if (code === 44) {
4239                 this.pos++;
4240                 this.tokenprio = -1;
4241                 this.tokenindex = ",";
4242                 return true;
4243             }
4244             return false;
4245         },
4246         isWhite: function() {
4247             var code = this.expression.charCodeAt(this.pos);
4248             if (code === 32 || code === 9 || code === 10 || code === 13) {
4249                 this.pos++;
4250                 return true;
4251             }
4252             return false;
4253         },
4254         isLeftVarBrk: function(modestack) {
4255             var pp = this.pos, ch = this.expression.charAt(pp);
4256             if (ch === "$" || ch === "@" || ch === "*" || ch === "#") {
4257                 pp++;
4258                 var ch2 = this.expression.charAt(pp);
4259                 if (ch2 === "{") {
4260                     pp++;
4261                     this.tmpprio += 10;
4262                     this.tokenprio = -4;
4263                     var oldMode = this.mode;
4264                     modestack.push(oldMode);
4265                     if (ch === "$") {
4266                         this.mode = 1;
4267                     } else if (ch === "@") {
4268                         this.mode = 2;
4269                     } else if (ch === "*") {
4270                         this.mode = 3;
4271                     } else if (ch === "#") {
4272                         this.mode = 4;
4273                     }
4274                     this.tokenindex = "{";
4275                     this.pos = pp;
4276                     return true;
4277                 }
4278             }
4279             return false;
4280         },
4281         isRightVarBrk: function() {
4282             var code = this.expression.charCodeAt(this.pos);
4283             if (code === 125) {
4284                 this.pos++;
4285                 this.tmpprio -= 10;
4286                 return true;
4287             }
4288             return false;
4289         },
4290         isOpX: function(str, group) {
4291             if (str.str.length > 0) {
4292                 if (str.str in new Object()) {
4293                     return false;
4294                 }
4295                 if (str.str in group) {
4296                     this.tokenindex = str.str;
4297                     this.tokenprio = 5;
4298                     this.pos = str.pos;
4299                     return true;
4300                 }
4301             }
4302             return false;
4303         },
4304         isLiteralValue: function(str) {
4305             if (typeof str.str === "string") {
4306                 var first = str.str.charAt(0);
4307                 var last = str.str.charAt(str.str.length - 1);
4308                 if (first == "'" && last == "'" || first == '"' && last == '"') {
4309                     this.tokennumber = str.str.substring(1, str.str.length - 1);
4310                     this.pos = str.pos;
4311                     return true;
4312                 }
4313             }
4314             return false;
4315         },
4316         isVar: function(str) {
4317             if (str.str.length > 0) {
4318                 this.tokenindex = str.str;
4319                 this.tokenprio = 4;
4320                 this.pos = str.pos;
4321                 return true;
4322             }
4323             return false;
4324         },
4325         isComment: function() {
4326             var code = this.expression.charCodeAt(this.pos - 1);
4327             if (code === 47 && this.expression.charCodeAt(this.pos) === 42) {
4328                 this.pos = this.expression.indexOf("*/", this.pos) + 2;
4329                 if (this.pos === 1) {
4330                     this.pos = this.expression.length;
4331                 }
4332                 return true;
4333             }
4334             return false;
4335         }
4336     };
4337     return ThParser;
4338 }();
4339
4340 (function() {
4341     var specAttrModList = [ "abbr", "accept", "accept-charset", "accesskey", "action", "align", "alt", "archive", "audio", "autocomplete", "axis", "background", "bgcolor", "border", "cellpadding", "cellspacing", "challenge", "charset", "cite", "class", "classid", "codebase", "codetype", "cols", "colspan", "compact", "content", "contenteditable", "contextmenu", "data", "datetime", "dir", "draggable", "dropzone", "enctype", "for", "form", "formaction", "formenctype", "formmethod", "formtarget", "frame", "frameborder", "headers", "height", "high", "href", "hreflang", "hspace", "http-equiv", "icon", "id", "keytype", "kind", "label", "lang", "list", "longdesc", "low", "manifest", "marginheight", "marginwidth", "max", "maxlength", "media", "method", "min", "name", "optimum", "pattern", "placeholder", "poster", "preload", "radiogroup", "rel", "rev", "rows", "rowspan", "rules", "sandbox", "scheme", "scope", "scrolling", "size", "sizes", "span", "spellcheck", "src", "srclang", "standby", "start", "step", "style", "summary", "tabindex", "target", "title", "type", "usemap", "value", "valuetype", "vspace", "width", "wrap", "xmlbase", "xmllang", "xmlspace" ];
4342     var fixedValBoolAttrList = [ "async", "autofocus", "autoplay", "checked", "controls", "declare", "default", "defer", "disabled", "formnovalidate", "hidden", "ismap", "loop", "multiple", "novalidate", "nowrap", "open", "pubdate", "readonly", "required", "reversed", "scoped", "seamless", "selected" ];
4343     var eventAttrList = [ "onabort", "onafterprint", "onbeforeprint", "onbeforeunload", "onblur", "oncanplay", "oncanplaythrough", "onchange", "onclick", "oncontextmenu", "ondblclick", "ondrag", "ondragend", "ondragenter", "ondragleave", "ondragover", "ondragstart", "ondrop", "ondurationchanged", "onemptied", "onended", "onerror", "onfocus", "onformchange", "onforminput", "onhashchange", "oninput", "oninvalid", "onkeydown", "onkeypress", "onkeyup", "onload", "onloadeddata", "onloadedmetadata", "onloadstart", "onmessage", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmousewheel", "onoffline", "ononline", "onpause", "onplay", "onplaying", "onpopstate", "onprogress", "onratechange", "onreadystatechange", "onredo", "onreset", "onresize", "onscroll", "onseeked", "onseeking", "onselect", "onshow", "onstalled", "onstorage", "onsubmit", "onsuspend", "ontimeupdate", "onundo", "onunload", "onvolumechange", "onwaiting" ];
4344     var literalTokenExpr = /^[a-zA-Z0-9\[\]\.\-_]*$/;
4345     var numericExpr = /^[+\-]?[0-9]*?[.]?[0-9]*?$/;
4346     var nonURLExpr = /[\$\*#]{1}\{(?:!?[^}]*)\}/;
4347     var varExpr = /[\$\*#@]{1}\{(!?[^}]*)\}/;
4348     var textInlineCommentExpr = /\[\[(.*)\]\]/;
4349     var javascriptInlineCommentExpr = /\/\*\[\[(.*)\]\]\*\//;
4350     var javascriptInlineRemainderExpr = /\s*(?:['][^']*['])*(?:["][^"]*["])*(?:[\(][^\(\)]*[\)])*(?:[\{][^\{\}]*[\}])*(?:[\[][^\[\]]*[\]])*((?:[;,\(\)\[\]:\{\}](?=(?:\s*\/\/.*?(?:\n|$)))(?:\s*\/\/.*?(?:\n|$)))|(?:\s*\/\/.*?(?:\n|$))|(?:[;,\(\)\[\]:\{\}](?=(?:\s*(?:\n|$)))(?:\s*(?:\n|$)))|(?:\s*(?:\n|$)))/;
4351     var thCase;
4352     thymol.getThAttribute = function(part, element) {
4353         var result = thymol.ThUtils.unParenthesise(part);
4354         result = thymol.doExpression(result, element);
4355         if (Object.prototype.toString.call(result) === "[object Array]") {
4356             if (result.length === 1) {
4357                 result = result[0];
4358             }
4359         }
4360         if (result instanceof thymol.ThParam) {
4361             result = result.value;
4362         }
4363         return result;
4364     };
4365     thymol.doExpression = function(part, element) {
4366         var result = thymol.ThUtils.unParenthesise(part), expr, unq, token, mapped;
4367         expr = null;
4368         unq = thymol.ThUtils.unQuote(result);
4369         if (unq != result) {
4370             result = thymol.preProcess(unq, element);
4371         } else {
4372             if (literalTokenExpr.test(result)) {
4373                 token = thymol.booleanAndNullTokens[result];
4374                 if (!(typeof token === "undefined")) {
4375                     result = token;
4376                 } else {
4377                     if (result.match(numericExpr)) {
4378                         result = thymol.ThUtils.getToPrecision(result, thymol.ThUtils.getDecimalDigits(result));
4379                     } else {
4380                         expr = thymol.getExpression(result, element);
4381                         if (expr !== undefined && expr !== null && !(expr != expr)) {
4382                             result = expr;
4383                         }
4384                     }
4385                 }
4386             } else {
4387                 expr = thymol.getExpression(result, element);
4388                 if (expr !== null && !(expr != expr)) {
4389                     result = expr;
4390                 } else {
4391                     result = null;
4392                 }
4393             }
4394         }
4395         return result;
4396     };
4397     thymol.processText = function(element, thUrlAttr, thAttr) {
4398         var url = thymol.getThAttribute(thUrlAttr.value, element), updated = false, text, newTextNode, i, iLimit, iUpper;
4399         if (url == null) {
4400             if (!thymol.allowNullText) {
4401                 if (thymol.debug) {
4402                     thymol.thWindow.alert("thymol.processText cannot process: " + thUrlAttr.name + '="' + thUrlAttr.value + '"\n' + element.innerHTML);
4403                 }
4404                 return updated;
4405             }
4406             url = "";
4407         } else {
4408             if (url instanceof thymol.ThParam || url instanceof thymol.ThObject) {
4409                 if (url.value) {
4410                     url = url.value;
4411                 }
4412             } else if (url instanceof thymol.ThClass && url.abort) {
4413                 element.removeAttribute(thUrlAttr.name);
4414                 return true;
4415             }
4416         }
4417         try {
4418             while (element.firstChild != null) {
4419                 element.removeChild(element.firstChild);
4420                 updated = true;
4421                 if (element.firstChild == null) {
4422                     break;
4423                 }
4424             }
4425             if ("text" == thAttr.suffix) {
4426                 if (Object.prototype.toString.call(url) === "[object Array]") {
4427                     text = "[";
4428                     for (i = 0, iLimit = url.length, iUpper = url.length - 1; i < iLimit; i++) {
4429                         text += url[i].toString();
4430                         if (i < iUpper) {
4431                             text += ", ";
4432                         }
4433                     }
4434                     text += "]";
4435                 } else {
4436                     text = url.toString();
4437                 }
4438                 text = thymol.ThUtils.unescape(text);
4439                 newTextNode = element.ownerDocument.createTextNode(text);
4440                 element.appendChild(newTextNode);
4441                 updated = true;
4442             }
4443             if ("utext" == thAttr.suffix) {
4444                 element.innerHTML = url;
4445             }
4446             element.removeAttribute(thUrlAttr.name);
4447         } catch (err) {
4448             if (thymol.debug) {
4449                 thymol.thWindow.alert("text replace error");
4450             }
4451         }
4452         return updated;
4453     };
4454     thymol.processSpecAttrMod = function(element, thUrlAttr, thAttrObj) {
4455         var url = thymol.getThAttribute(thUrlAttr.value, element);
4456         if (!url || !(url instanceof thymol.ThClass) || !url.abort) {
4457             element.setAttribute(thAttrObj.suffix, url);
4458         }
4459         element.removeAttribute(thUrlAttr.name);
4460     };
4461     thymol.processAttr = function(element, thUrlAttr, thAttrObj) {
4462         var argValue = thUrlAttr.value.trim(), argsExpr, expr, identifier, attrName = null, ep, lp, url, tt;
4463         if (argValue) {
4464             do {
4465                 argsExpr = thymol.ThParser.parse(argValue, true, false);
4466                 identifier = argsExpr.tokens.shift();
4467                 if (identifier.type_ === 3) {
4468                     attrName = identifier.index_;
4469                     if (!!attrName) {
4470                         ep = argValue.indexOf("=");
4471                         if (ep >= 0) {
4472                             lp = argsExpr.position - 1;
4473                             if (argsExpr.position === argValue.length) {
4474                                 lp = argValue.position;
4475                             }
4476                             expr = argValue.substring(ep + 1, lp).trim();
4477                             if (fixedValBoolAttrList.indexOf(attrName) >= 0) {
4478                                 thymol.doFixedValBoolAttr(expr, element, attrName);
4479                             } else {
4480                                 url = thymol.getThAttribute(expr, element);
4481                                 tt = typeof url;
4482                                 if (thAttrObj.suffix == "attrappend" || thAttrObj.suffix == "attrprepend") {
4483                                     if (url !== null && (tt === "number" || tt === "string" && url.length > 0)) {
4484                                         existing = element.getAttribute(attrName);
4485                                         if (existing) {
4486                                             if (thAttrObj.suffix == "attrappend") {
4487                                                 url = existing + url;
4488                                             } else if (thAttrObj.suffix == "attrprepend") {
4489                                                 url = url + existing;
4490                                             }
4491                                         }
4492                                     }
4493                                 }
4494                                 if (url !== null && (tt === "number" || tt === "string" && url.length > 0)) {
4495                                     element.setAttribute(attrName, url);
4496                                 }
4497                             }
4498                         }
4499                     }
4500                     argValue = argValue.substring(argsExpr.position);
4501                 } else {
4502                     break;
4503                 }
4504             } while (argValue.length > 0);
4505         }
4506         element.removeAttribute(thUrlAttr.name);
4507     };
4508     thymol.processCSSAttr = function(element, thUrlAttr, thAttrObj) {
4509         var parts = thUrlAttr.value.split(","), i, iLimit, expr, attrName, url, tt, existing;
4510         for (i = 0, iLimit = parts.length; i < iLimit; i++) {
4511             expr = parts[i];
4512             attrName = thAttrObj.suffix == "classappend" ? "class" : "style";
4513             if (!!attrName) {
4514                 if (!!expr) {
4515                     url = thymol.getThAttribute(expr, element);
4516                     tt = typeof url;
4517                     if (url !== null && (tt === "number" || tt === "string" && url.length > 0)) {
4518                         existing = element.getAttribute(attrName);
4519                         if (existing) {
4520                             url = existing + " " + url;
4521                         }
4522                     }
4523                     if (url !== null && (tt === "number" || tt === "string" && url.length > 0)) {
4524                         element.setAttribute(attrName, url);
4525                     }
4526                 }
4527             }
4528         }
4529         element.removeAttribute(thUrlAttr.name);
4530     };
4531     thymol.processFixedValBoolAttr = function(element, thUrlAttr, thAttrObj) {
4532         var val = thymol.doFixedValBoolAttr(thUrlAttr.value, element, thAttrObj.suffix);
4533         if (val != null) {
4534             element.removeAttribute(thUrlAttr.name);
4535         } else {
4536             if (thymol.debug) {
4537                 thymol.thWindow.alert("thymol.processFixedValBoolAttr cannot process: " + thUrlAttr.name + '="' + thUrlAttr.value + '"\n' + element.innerHTML);
4538             }
4539         }
4540     };
4541     thymol.doFixedValBoolAttr = function(valParam, element, attr) {
4542         var val = thymol.getBoolean(valParam, element);
4543         if (val) {
4544             element.setAttribute(attr, attr);
4545         }
4546         return val;
4547     };
4548     thymol.processPairedAttr = function(element, thUrlAttr, thAttrObj) {
4549         var url = thymol.getThAttribute(thUrlAttr.value, element);
4550         if (url != "") {
4551             if (thAttrObj.suffix === "alt-title") {
4552                 element.setAttribute("alt", url);
4553                 element.setAttribute("title", url);
4554             }
4555             if (thAttrObj.suffix === "lang-xmllang") {
4556                 element.setAttribute("lang", url);
4557                 element.setAttribute("xml:lang", url);
4558             }
4559             element.removeAttribute(thUrlAttr.name);
4560         } else {
4561             if (thymol.debug) {
4562                 thymol.thWindow.alert("thymol.processPairedAttr cannot process: " + thUrlAttr.name + '="' + thUrlAttr.value + '"\n' + element.innerHTML);
4563             }
4564         }
4565     };
4566     thymol.processConditional = function(element, attr, thAttrObj) {
4567         var removed = false;
4568         if (attr.value) {
4569             removed = thymol.doIfOrUnless(element, attr.value, thAttrObj.suffix === "if");
4570         }
4571         element.removeAttribute(attr.name);
4572         return removed;
4573     };
4574     thymol.doIfOrUnless = function(element, value, isIf) {
4575         var processed = false, flag;
4576         if (value) {
4577             flag = thymol.getBoolean(value, element);
4578             processed = true;
4579             if (!flag) {
4580                 if (isIf) {
4581                     element.parentNode.removeChild(element);
4582                     return true;
4583                 }
4584             } else {
4585                 if (!isIf) {
4586                     element.parentNode.removeChild(element);
4587                     return true;
4588                 }
4589             }
4590         }
4591         if (!processed && thymol.debug) {
4592             thymol.thWindow.alert("thymol.processConditional cannot process conditional: " + value + "\n" + element.innerHTML);
4593         }
4594         return false;
4595     };
4596     thymol.processEach = function(element, thUrlAttr, junk) {
4597         var elementsUpdated = false, initial = thUrlAttr.value.trim(), colonPos, varName, varNames, statVarName, expr, root, node, i, iLimit, tho, stat, count, newNode, next;
4598         colonPos = initial.indexOf(":");
4599         if (colonPos > 0) {
4600             varName = initial.substring(0, colonPos);
4601             if (varName) {
4602                 varName = varName.trim();
4603                 varNames = varName.split(",");
4604                 varName = varNames[0].trim();
4605                 if (varNames.length > 1) {
4606                     statVarName = varNames[1].trim();
4607                 } else {
4608                     statVarName = varName + "Stat";
4609                 }
4610                 expr = initial.substr(colonPos + 1);
4611                 if (expr) {
4612                     expr = expr.trim();
4613                     expr = thymol.getExpression(expr, element);
4614                     if (expr instanceof thymol.ThSet) {
4615                         expr = expr.toArray();
4616                     }
4617                     root = element.parentNode;
4618                     if (expr && expr instanceof Object && expr.length > 0) {
4619                         node = element;
4620                         iLimit = expr.length;
4621                         element.removeAttribute(thUrlAttr.name);
4622                         for (i = 0; i < iLimit; i++) {
4623                             tho = expr[i];
4624                             stat = new Object();
4625                             stat.current = tho;
4626                             stat.size = expr.length;
4627                             stat.index = i;
4628                             count = i + 1;
4629                             stat.count = count;
4630                             if (i == 0) {
4631                                 stat.first = true;
4632                             } else {
4633                                 stat.first = false;
4634                             }
4635                             if (i == expr.length - 1) {
4636                                 stat.last = true;
4637                             } else {
4638                                 stat.last = false;
4639                             }
4640                             if (i % 2) {
4641                                 stat.odd = true;
4642                                 stat.even = false;
4643                             } else {
4644                                 stat.odd = false;
4645                                 stat.even = true;
4646                             }
4647                             if (!node.thLocalVars) {
4648                                 node.thLocalVars = {};
4649                             }
4650                             node.thLocalVars[varName] = tho;
4651                             node.thLocalVars[statVarName] = stat;
4652                             if (count < expr.length) {
4653                                 newNode = element.cloneNode(true);
4654                                 if (node.nextElementSibling != null) {
4655                                     next = root.insertBefore(newNode, node.nextElementSibling);
4656                                 } else {
4657                                     next = root.appendChild(newNode);
4658                                 }
4659                                 node = next;
4660                                 elementsUpdated = true;
4661                             }
4662                         }
4663                     } else {
4664                         if (root !== null) {
4665                             if (!element.thLocalVars) {
4666                                 element.thLocalVars = {};
4667                             }
4668                             if (!element.thLocalVars[varName]) {
4669                                 element.thLocalVars[varName] = new Object();
4670                             }
4671                             if (!element.thLocalVars[statVarName]) {
4672                                 element.thLocalVars[statVarName] = new Object();
4673                             }
4674                             root.removeChild(element);
4675                             elementsUpdated = true;
4676                         }
4677                     }
4678                 }
4679             }
4680         }
4681         return elementsUpdated;
4682     };
4683     thymol.processObject = function(element, thUrlAttr) {
4684         var argValue = thUrlAttr.value.trim(), val;
4685         if (argValue) {
4686             val = thymol.getExpression(argValue, element);
4687             if (val) {
4688                 element.thObjectVar = val;
4689             }
4690         }
4691         element.removeAttribute(thUrlAttr.name);
4692     };
4693     thymol.processInline = function(element, thUrlAttr, thAttrObj) {
4694         var mode = thymol.getThAttribute(thUrlAttr.value, element);
4695         if (mode == "text") {
4696             thymol.doInlineText(element);
4697         } else if (mode == "javascript" || mode == "dart") {
4698             thymol.doInlineJavascript(element);
4699         } else {
4700             if (thymol.debug) {
4701                 thymol.thWindow.alert('thymol.processInline cannot process scripting mode: "' + mode + '" - it isn\'t supported by version "' + thymol.thVersion + '"\n');
4702             }
4703         }
4704         element.removeAttribute(thUrlAttr.name);
4705     };
4706     thymol.doInlineText = function(element) {
4707         var changed, value, i, iLimit, expr, term, result;
4708         for (i = 0, iLimit = element.childNodes.length; i < iLimit; i++) {
4709             do {
4710                 changed = false;
4711                 if (element.childNodes[i].nodeType == 1) {
4712                     thymol.doInlineText(element.childNodes[i]);
4713                 } else if (element.childNodes[i].nodeType == 3) {
4714                     value = element.childNodes[i].nodeValue;
4715                     if (value) {
4716                         expr = textInlineCommentExpr.exec(value);
4717                         if (expr) {
4718                             term = "";
4719                             if (expr.length > 1) {
4720                                 term = "[[" + expr[1] + "]]";
4721                             }
4722                             if (expr.length > 1) {
4723                                 result = thymol.getThAttribute(expr[1], element);
4724                                 result = value.replace(term, result);
4725                                 element.childNodes[i].nodeValue = result;
4726                                 changed = true;
4727                             }
4728                             expr = null;
4729                         }
4730                     }
4731                 }
4732             } while (changed);
4733         }
4734     };
4735     thymol.doInlineJavascript = function(element) {
4736         var changed, value, second, i, iLimit, expr, scraps, remainder, termIndx, term, secondIndx, result;
4737         for (i = 0, iLimit = element.childNodes.length; i < iLimit; i++) {
4738             do {
4739                 second = null;
4740                 changed = false;
4741                 value = element.childNodes[i].nodeValue;
4742                 if (value) {
4743                     expr = javascriptInlineCommentExpr.exec(value);
4744                     if (expr) {
4745                         termIndx = expr.index;
4746                         term = "";
4747                         if (expr.length > 1) {
4748                             term = "/*[[" + expr[1] + "]]*/";
4749                         }
4750                         termIndx = termIndx + term.length;
4751                         remainder = value.substring(termIndx);
4752                         scraps = javascriptInlineRemainderExpr.exec(remainder);
4753                         if (scraps) {
4754                             if (scraps.length > 1) {
4755                                 secondIndx = remainder.indexOf(scraps[1]);
4756                                 second = remainder.substring(secondIndx);
4757                                 value = value.substring(0, termIndx);
4758                                 value = value + second;
4759                             }
4760                         }
4761                         if (expr.length > 1) {
4762                             result = thymol.getExpression(expr[1], element);
4763                             if (result instanceof thymol.ThObject) {
4764                                 result = result.toNonThObject();
4765                             }
4766                             if (!thymol.ThUtils.isLiteral(result)) {
4767                                 result = thymol.getStringView(result);
4768                             }
4769                             result = value.replace(term, result);
4770                             element.childNodes[i].nodeValue = result;
4771                             changed = true;
4772                         }
4773                         expr = null;
4774                         scraps = null;
4775                     }
4776                 }
4777             } while (changed);
4778         }
4779     };
4780     thymol.getStringView = function(param) {
4781         var view = "", objType;
4782         if (typeof param === "string") {
4783             view = view + "'" + param + "'";
4784         } else if (typeof param === "number" || typeof param === "boolean") {
4785             view = view + param;
4786         } else if (typeof param === "object") {
4787             if (param instanceof Object) {
4788                 objType = Object.prototype.toString.call(param);
4789                 if ("[object Array]" == objType) {
4790                     view = thymol.getStringViewArray(param);
4791                 } else if ("[object Object]" == objType) {
4792                     view = thymol.getStringViewObject(param);
4793                 }
4794             }
4795         }
4796         return view;
4797     };
4798     thymol.getStringViewArray = function(param) {
4799         var view = "[", i, iLimit;
4800         for (i = 0, iLimit = param.length; i < iLimit; i++) {
4801             view = view + thymol.getStringView(param[i]);
4802             if (i < param.length - 1) {
4803                 view = view + ",";
4804             }
4805         }
4806         view = view + "]";
4807         return view;
4808     };
4809     thymol.getStringViewObject = function(param) {
4810         var view = "{", key = null;
4811         for (key in param) {
4812             if (key) {
4813                 if (view != "{") {
4814                     view = view + ",";
4815                 }
4816                 view = view + thymol.getStringView(key) + ":";
4817                 view = view + thymol.getStringView(param[key]);
4818             }
4819         }
4820         view = view + "}";
4821         return view;
4822     };
4823     thymol.processRemove = function(element, thUrlAttr) {
4824         var haveRemoved = false;
4825         var locals = element.thLocalVars, savedLocals = element.thLocalVars, arg, nodes, first;
4826         if (!locals) {
4827             locals = {};
4828         }
4829         if (!locals["tag"]) {
4830             locals["tag"] = "tag";
4831         }
4832         if (!locals["body"]) {
4833             locals["body"] = "body";
4834         }
4835         if (!locals["none"]) {
4836             locals["none"] = "none";
4837         }
4838         if (!locals["all"]) {
4839             locals["all"] = "all";
4840         }
4841         if (!locals["all-but-first"]) {
4842             locals["all-but-first"] = "all-but-first";
4843         }
4844         element.thLocalVars = locals;
4845         arg = thymol.getThAttribute(thUrlAttr.value, element);
4846         element.thLocalVars = savedLocals;
4847         element.removeAttribute(thUrlAttr.name);
4848         if ("all" == arg) {
4849             if (element.parentNode != null) {
4850                 element.parentNode.removeChild(element);
4851                 haveRemoved = true;
4852             }
4853         } else if ("body" == arg) {
4854             element.innerHTML = "";
4855             haveRemoved = true;
4856         } else if ("tag" == arg) {
4857             thymol.ThUtils.removeTag(element);
4858             haveRemoved = true;
4859         } else if ("all-but-first" == arg) {
4860             nodes = element.childNodes;
4861             first = true;
4862             $(nodes).each(function() {
4863                 if (this.nodeType == 1) {
4864                     if (!first) {
4865                         element.removeChild(this);
4866                         haveRemoved = true;
4867                     }
4868                     first = false;
4869                 }
4870             });
4871         } else if ("none" == arg || null == arg) {}
4872         return haveRemoved;
4873     };
4874     thymol.processSwitch = function(element, attr) {
4875         var val = thymol.ThUtils.unParenthesise(attr.value), updated = false, args, matched = false, thCaseSpecs, caseClause, remove, ccAttr;
4876         val = thymol.getThAttribute(val, element);
4877         if (typeof val === "string") {
4878             args = val.match(nonURLExpr);
4879             if (args) {
4880                 val = args[1];
4881             }
4882         }
4883         val = thymol.ThUtils.unQuote(val);
4884         thCaseSpecs = $(thCase.escpName, element);
4885         thCaseSpecs.each(function() {
4886             caseClause = this;
4887             remove = true;
4888             $(caseClause.attributes).each(function() {
4889                 ccAttr = this;
4890                 if (thCase.name == ccAttr.name || thCase.synonym == ccAttr.name) {
4891                     if (!matched) {
4892                         matched = thymol.processCase(element, ccAttr, val);
4893                         if (matched) {
4894                             remove = false;
4895                         }
4896                     }
4897                     caseClause.removeAttribute(ccAttr.name);
4898                 }
4899             });
4900             if (remove) {
4901                 element.removeChild(caseClause);
4902                 updated = true;
4903             }
4904         });
4905         return updated;
4906     };
4907     thymol.processCase = function(element, attr, param) {
4908         var val = thymol.substitute(attr.value, element);
4909         val = thymol.ThUtils.unQuote(val);
4910         if (val == "*" || param && param == val) {
4911             return true;
4912         }
4913         return false;
4914     };
4915     thymol.processWith = function(element, thUrlAttr) {
4916         thymol.getWith(element, thUrlAttr.value);
4917         element.removeAttribute(thUrlAttr.name);
4918     };
4919     thymol.processAssert = function(element, thUrlAttr) {
4920         var argValue = thUrlAttr.value.trim(), result = true, term = "", terms, i, iLimit, expr, val, flag;
4921         if (argValue) {
4922             terms = argValue.split(",");
4923             for (i = 0, iLimit = terms.length; i < iLimit; i++) {
4924                 term = terms[i];
4925                 expr = thymol.ThUtils.unParenthesise(term);
4926                 if (expr != null) {
4927                     val = thymol.getExpression(expr, element);
4928                     if (val) {
4929                         flag = thymol.getBoolean(val, element);
4930                         if (!flag) {
4931                             result = false;
4932                             break;
4933                         }
4934                     } else {
4935                         result = false;
4936                         break;
4937                     }
4938                 } else {
4939                     result = false;
4940                     break;
4941                 }
4942             }
4943         }
4944         if (!result) {
4945             if (argValue != term) {
4946                 argValue = " list is: " + argValue;
4947             } else {
4948                 argValue = "";
4949             }
4950             if (term != "") {
4951                 term = ' false term is: "' + term + '"';
4952             }
4953             if (thymol.debug) {
4954                 thymol.thWindow.alert("thymol.processAssert assertion failure -" + argValue + term + "\n");
4955             }
4956         }
4957         element.removeAttribute(thUrlAttr.name);
4958     };
4959     thymol.processFragment = function(element, thUrlAttr, thAttrObj) {
4960         element.removeAttribute(thUrlAttr.name);
4961     };
4962     thymol.getBoolean = function(param, element) {
4963         if (param == null) {
4964             return false;
4965         }
4966         if (typeof param === "boolean") {
4967             return param;
4968         } else if (typeof param === "number") {
4969             return param != 0;
4970         }
4971         var initial = thymol.ThUtils.unParenthesise(param), negate = false, val, args, flag;
4972         if (initial.charAt(0) == "!") {
4973             negate = true;
4974             initial = initial.substring(1, initial.length);
4975             initial = thymol.ThUtils.unParenthesise(initial);
4976         }
4977         val = thymol.getThAttribute(initial, element);
4978         if (val == null) {
4979             args = initial.match(varExpr);
4980             if (args) {
4981                 if (args[1].charAt(0) == "!") {
4982                     negate = !negate;
4983                 }
4984             }
4985         }
4986         flag = thymol.getBooleanValue(val);
4987         if (negate) {
4988             flag = !flag;
4989         }
4990         return flag;
4991     };
4992     thymol.appendToAttrList = function(func, prec, attrArray) {
4993         var j, jLimit = attrArray.length, tha = null;
4994         for (j = 0; j < jLimit; j++) {
4995             tha = new thymol.ThAttr(attrArray[j], func, prec, thymol.thThymeleafPrefixList, thymol.prefix);
4996         }
4997         j = tha;
4998     };
4999     thymol.setupAttrList = function() {
5000         thCase = new thymol.ThAttr("case", null, 275, thymol.thThymeleafPrefixList, thymol.prefix);
5001         thymol.addDialect({
5002             prefix: thymol.prefix,
5003             attributeProcessors: [ {
5004                 name: "each",
5005                 processor: thymol.processEach,
5006                 precedence: 200
5007             }, {
5008                 name: "switch",
5009                 processor: thymol.processSwitch,
5010                 precedence: 250
5011             }, {
5012                 name: "if",
5013                 processor: thymol.processConditional,
5014                 precedence: 300
5015             }, {
5016                 name: "unless",
5017                 processor: thymol.processConditional,
5018                 precedence: 400
5019             }, {
5020                 name: "object",
5021                 processor: thymol.processObject,
5022                 precedence: 500
5023             }, {
5024                 name: "with",
5025                 processor: thymol.processWith,
5026                 precedence: 600
5027             }, {
5028                 name: "attr",
5029                 processor: thymol.processAttr,
5030                 precedence: 700
5031             }, {
5032                 name: "attrprepend",
5033                 processor: thymol.processAttr,
5034                 precedence: 800
5035             }, {
5036                 name: "attrappend",
5037                 processor: thymol.processAttr,
5038                 precedence: 900
5039             }, {
5040                 name: "alt-title",
5041                 processor: thymol.processPairedAttr,
5042                 precedence: 990
5043             }, {
5044                 name: "lang-xmllang",
5045                 processor: thymol.processPairedAttr,
5046                 precedence: 990
5047             }, {
5048                 name: "inline",
5049                 processor: thymol.processInline,
5050                 precedence: 1e3
5051             }, {
5052                 name: "classappend",
5053                 processor: thymol.processCSSAttr,
5054                 precedence: 1100
5055             }, {
5056                 name: "styleappend",
5057                 processor: thymol.processCSSAttr,
5058                 precedence: 1100
5059             }, {
5060                 name: "text",
5061                 processor: thymol.processText,
5062                 precedence: 1300
5063             }, {
5064                 name: "utext",
5065                 processor: thymol.processText,
5066                 precedence: 1400
5067             }, {
5068                 name: "fragment",
5069                 processor: thymol.processFragment,
5070                 precedence: 1500
5071             }, {
5072                 name: "assert",
5073                 processor: thymol.processAssert,
5074                 precedence: 1550
5075             }, {
5076                 name: "remove",
5077                 processor: thymol.processRemove,
5078                 precedence: 1600
5079             } ]
5080         });
5081         thymol.appendToAttrList(thymol.processSpecAttrMod, 1e3, specAttrModList);
5082         thymol.appendToAttrList(thymol.processSpecAttrMod, 1e3, eventAttrList);
5083         thymol.appendToAttrList(thymol.processFixedValBoolAttr, 1e3, fixedValBoolAttrList);
5084     };
5085 })();
5086
5087 thymol.objects.thHttpSessionObject = function() {
5088     var thExpressionObjectName = "#httpSession";
5089     function getAttribute(name) {
5090         var result = thymol.sessionContext[name];
5091         return result;
5092     }
5093     function getParameter(name) {
5094         var result = thymol.sessionContext[name];
5095         return result;
5096     }
5097     function getServletContext() {
5098         var result = thymol.applicationContext;
5099         return result;
5100     }
5101     function getSessionContext() {
5102         var result = thymol.sessionContext;
5103         return result;
5104     }
5105     function getContextPath() {
5106         var result = "";
5107         return result;
5108     }
5109     function getRequestName() {
5110         var result = "";
5111         return result;
5112     }
5113     function getParameterValues(name) {
5114         var result = thymol.sessionContext[name];
5115         return result;
5116     }
5117     return {
5118         thExpressionObjectName: thExpressionObjectName,
5119         getAttribute: getAttribute,
5120         getParameter: getParameter,
5121         getServletContext: getServletContext,
5122         getSessionContext: getSessionContext,
5123         getContextPath: getContextPath,
5124         getRequestName: getRequestName,
5125         getParameterValues: getParameterValues
5126     };
5127 }();
5128
5129 thymol.objects.thHttpServletRequestObject = function() {
5130     var thExpressionObjectName = "#httpServletRequest";
5131     function getAttribute(name) {
5132         var result = thymol.requestContext[name][0];
5133         if (result instanceof thymol.ThParam) {
5134             result = thymol.ThUtils.unQuote(result.value);
5135         }
5136         return result;
5137     }
5138     function getParameter(name) {
5139         var result = thymol.requestContext[name];
5140         return result;
5141     }
5142     function getContextPath() {
5143         var result = "";
5144         return result;
5145     }
5146     function getRequestName() {
5147         var result = "";
5148         return result;
5149     }
5150     function getParameterValues(name) {
5151         var result = thymol.requestContext[name];
5152         return result;
5153     }
5154     function getSession(create) {
5155         return thymol.objects.thHttpSessionObject;
5156     }
5157     return {
5158         thExpressionObjectName: thExpressionObjectName,
5159         getAttribute: getAttribute,
5160         getParameter: getParameter,
5161         getContextPath: getContextPath,
5162         getRequestName: getRequestName,
5163         getParameterValues: getParameterValues,
5164         getSession: getSession
5165     };
5166 }();
5167
5168 (function() {
5169     var DOMParser_proto = thymol.thDomParser.prototype, real_parseFromString = DOMParser_proto.parseFromString;
5170     try {
5171         if (new thymol.thDomParser().parseFromString("", "text/html")) {
5172             return;
5173         }
5174     } catch (ignore) {}
5175     DOMParser_proto.parseFromString = function(markup, type) {
5176         var res, doc;
5177         if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
5178             doc = thymol.thDocument.implementation.createHTMLDocument("");
5179             if (markup.toLowerCase().indexOf("<!doctype") > -1) {
5180                 doc.documentElement.innerHTML = markup;
5181             } else {
5182                 doc.body.innerHTML = markup;
5183             }
5184             res = doc;
5185         } else {
5186             res = real_parseFromString.apply(this, arguments);
5187         }
5188         return res;
5189     };
5190 })();
5191
5192 if (!Array.indexOf) {
5193     Array.prototype.indexOf = function(obj, start) {
5194         for (var i = start || 0; i < this.length; i++) {
5195             if (this[i] === obj) {
5196                 return i;
5197             }
5198         }
5199         return -1;
5200     };
5201 }
5202
5203 $(function() {
5204     thymol.jqSetup($);
5205     thymol.execute(document);
5206 });
5207
5208 $(window).unload(function() {
5209     if (thymol.sessionContext && thymol.sessionContext.persist) {
5210         thymol.sessionContext.persist();
5211     }
5212 });