CLIENT GUI Framework
[vnfsdk/refrepo.git] / portal-common / src / main / webapp / framework / browser / thirdparty / js / mustache.js
1 /*!\r
2  * mustache.js - Logic-less {{mustache}} templates with JavaScript\r
3  * http://github.com/janl/mustache.js\r
4  */\r
5 \r
6 /*global define: false Mustache: true*/\r
7 \r
8 (function defineMustache (global, factory) {\r
9     if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') {\r
10         factory(exports); // CommonJS\r
11     } else if (typeof define === 'function' && define.amd) {\r
12         define(['exports'], factory); // AMD\r
13     } else {\r
14         global.Mustache = {};\r
15         factory(global.Mustache); // script, wsh, asp\r
16     }\r
17 }(this, function mustacheFactory (mustache) {\r
18 \r
19     var objectToString = Object.prototype.toString;\r
20     var isArray = Array.isArray || function isArrayPolyfill (object) {\r
21             return objectToString.call(object) === '[object Array]';\r
22         };\r
23 \r
24     function isFunction (object) {\r
25         return typeof object === 'function';\r
26     }\r
27 \r
28     /**\r
29      * More correct typeof string handling array\r
30      * which normally returns typeof 'object'\r
31      */\r
32     function typeStr (obj) {\r
33         return isArray(obj) ? 'array' : typeof obj;\r
34     }\r
35 \r
36     function escapeRegExp (string) {\r
37         return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');\r
38     }\r
39 \r
40     /**\r
41      * Null safe way of checking whether or not an object,\r
42      * including its prototype, has a given property\r
43      */\r
44     function hasProperty (obj, propName) {\r
45         return obj != null && typeof obj === 'object' && (propName in obj);\r
46     }\r
47 \r
48     // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577\r
49     // See https://github.com/janl/mustache.js/issues/189\r
50     var regExpTest = RegExp.prototype.test;\r
51     function testRegExp (re, string) {\r
52         return regExpTest.call(re, string);\r
53     }\r
54 \r
55     var nonSpaceRe = /\S/;\r
56     function isWhitespace (string) {\r
57         return !testRegExp(nonSpaceRe, string);\r
58     }\r
59 \r
60     var entityMap = {\r
61         '&': '&',\r
62         '<': '&lt;',\r
63         '>': '&gt;',\r
64         '"': '&quot;',\r
65         "'": '&#39;',\r
66         '/': '&#x2F;',\r
67         '`': '&#x60;',\r
68         '=': '&#x3D;'\r
69     };\r
70 \r
71     function escapeHtml (string) {\r
72         return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {\r
73             return entityMap[s];\r
74         });\r
75     }\r
76 \r
77     var whiteRe = /\s*/;\r
78     var spaceRe = /\s+/;\r
79     var equalsRe = /\s*=/;\r
80     var curlyRe = /\s*\}/;\r
81     var tagRe = /#|\^|\/|>|\{|&|=|!/;\r
82 \r
83     /**\r
84      * Breaks up the given `template` string into a tree of tokens. If the `tags`\r
85      * argument is given here it must be an array with two string values: the\r
86      * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of\r
87      * course, the default is to use mustaches (i.e. mustache.tags).\r
88      *\r
89      * A token is an array with at least 4 elements. The first element is the\r
90      * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag\r
91      * did not contain a symbol (i.e. {{myValue}}) this element is "name". For\r
92      * all text that appears outside a symbol this element is "text".\r
93      *\r
94      * The second element of a token is its "value". For mustache tags this is\r
95      * whatever else was inside the tag besides the opening symbol. For text tokens\r
96      * this is the text itself.\r
97      *\r
98      * The third and fourth elements of the token are the start and end indices,\r
99      * respectively, of the token in the original template.\r
100      *\r
101      * Tokens that are the root node of a subtree contain two more elements: 1) an\r
102      * array of tokens in the subtree and 2) the index in the original template at\r
103      * which the closing tag for that section begins.\r
104      */\r
105     function parseTemplate (template, tags) {\r
106         if (!template)\r
107             return [];\r
108 \r
109         var sections = [];     // Stack to hold section tokens\r
110         var tokens = [];       // Buffer to hold the tokens\r
111         var spaces = [];       // Indices of whitespace tokens on the current line\r
112         var hasTag = false;    // Is there a {{tag}} on the current line?\r
113         var nonSpace = false;  // Is there a non-space char on the current line?\r
114 \r
115         // Strips all whitespace tokens array for the current line\r
116         // if there was a {{#tag}} on it and otherwise only space.\r
117         function stripSpace () {\r
118             if (hasTag && !nonSpace) {\r
119                 while (spaces.length)\r
120                     delete tokens[spaces.pop()];\r
121             } else {\r
122                 spaces = [];\r
123             }\r
124 \r
125             hasTag = false;\r
126             nonSpace = false;\r
127         }\r
128 \r
129         var openingTagRe, closingTagRe, closingCurlyRe;\r
130         function compileTags (tagsToCompile) {\r
131             if (typeof tagsToCompile === 'string')\r
132                 tagsToCompile = tagsToCompile.split(spaceRe, 2);\r
133 \r
134             if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)\r
135                 throw new Error('Invalid tags: ' + tagsToCompile);\r
136 \r
137             openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');\r
138             closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));\r
139             closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));\r
140         }\r
141 \r
142         compileTags(tags || mustache.tags);\r
143 \r
144         var scanner = new Scanner(template);\r
145 \r
146         var start, type, value, chr, token, openSection;\r
147         while (!scanner.eos()) {\r
148             start = scanner.pos;\r
149 \r
150             // Match any text between tags.\r
151             value = scanner.scanUntil(openingTagRe);\r
152 \r
153             if (value) {\r
154                 for (var i = 0, valueLength = value.length; i < valueLength; ++i) {\r
155                     chr = value.charAt(i);\r
156 \r
157                     if (isWhitespace(chr)) {\r
158                         spaces.push(tokens.length);\r
159                     } else {\r
160                         nonSpace = true;\r
161                     }\r
162 \r
163                     tokens.push([ 'text', chr, start, start + 1 ]);\r
164                     start += 1;\r
165 \r
166                     // Check for whitespace on the current line.\r
167                     if (chr === '\n')\r
168                         stripSpace();\r
169                 }\r
170             }\r
171 \r
172             // Match the opening tag.\r
173             if (!scanner.scan(openingTagRe))\r
174                 break;\r
175 \r
176             hasTag = true;\r
177 \r
178             // Get the tag type.\r
179             type = scanner.scan(tagRe) || 'name';\r
180             scanner.scan(whiteRe);\r
181 \r
182             // Get the tag value.\r
183             if (type === '=') {\r
184                 value = scanner.scanUntil(equalsRe);\r
185                 scanner.scan(equalsRe);\r
186                 scanner.scanUntil(closingTagRe);\r
187             } else if (type === '{') {\r
188                 value = scanner.scanUntil(closingCurlyRe);\r
189                 scanner.scan(curlyRe);\r
190                 scanner.scanUntil(closingTagRe);\r
191                 type = '&';\r
192             } else {\r
193                 value = scanner.scanUntil(closingTagRe);\r
194             }\r
195 \r
196             // Match the closing tag.\r
197             if (!scanner.scan(closingTagRe))\r
198                 throw new Error('Unclosed tag at ' + scanner.pos);\r
199 \r
200             token = [ type, value, start, scanner.pos ];\r
201             tokens.push(token);\r
202 \r
203             if (type === '#' || type === '^') {\r
204                 sections.push(token);\r
205             } else if (type === '/') {\r
206                 // Check section nesting.\r
207                 openSection = sections.pop();\r
208 \r
209                 if (!openSection)\r
210                     throw new Error('Unopened section "' + value + '" at ' + start);\r
211 \r
212                 if (openSection[1] !== value)\r
213                     throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);\r
214             } else if (type === 'name' || type === '{' || type === '&') {\r
215                 nonSpace = true;\r
216             } else if (type === '=') {\r
217                 // Set the tags for the next time around.\r
218                 compileTags(value);\r
219             }\r
220         }\r
221 \r
222         // Make sure there are no open sections when we're done.\r
223         openSection = sections.pop();\r
224 \r
225         if (openSection)\r
226             throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);\r
227 \r
228         return nestTokens(squashTokens(tokens));\r
229     }\r
230 \r
231     /**\r
232      * Combines the values of consecutive text tokens in the given `tokens` array\r
233      * to a single token.\r
234      */\r
235     function squashTokens (tokens) {\r
236         var squashedTokens = [];\r
237 \r
238         var token, lastToken;\r
239         for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {\r
240             token = tokens[i];\r
241 \r
242             if (token) {\r
243                 if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {\r
244                     lastToken[1] += token[1];\r
245                     lastToken[3] = token[3];\r
246                 } else {\r
247                     squashedTokens.push(token);\r
248                     lastToken = token;\r
249                 }\r
250             }\r
251         }\r
252 \r
253         return squashedTokens;\r
254     }\r
255 \r
256     /**\r
257      * Forms the given array of `tokens` into a nested tree structure where\r
258      * tokens that represent a section have two additional items: 1) an array of\r
259      * all tokens that appear in that section and 2) the index in the original\r
260      * template that represents the end of that section.\r
261      */\r
262     function nestTokens (tokens) {\r
263         var nestedTokens = [];\r
264         var collector = nestedTokens;\r
265         var sections = [];\r
266 \r
267         var token, section;\r
268         for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {\r
269             token = tokens[i];\r
270 \r
271             switch (token[0]) {\r
272                 case '#':\r
273                 case '^':\r
274                     collector.push(token);\r
275                     sections.push(token);\r
276                     collector = token[4] = [];\r
277                     break;\r
278                 case '/':\r
279                     section = sections.pop();\r
280                     section[5] = token[2];\r
281                     collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;\r
282                     break;\r
283                 default:\r
284                     collector.push(token);\r
285             }\r
286         }\r
287 \r
288         return nestedTokens;\r
289     }\r
290 \r
291     /**\r
292      * A simple string scanner that is used by the template parser to find\r
293      * tokens in template strings.\r
294      */\r
295     function Scanner (string) {\r
296         this.string = string;\r
297         this.tail = string;\r
298         this.pos = 0;\r
299     }\r
300 \r
301     /**\r
302      * Returns `true` if the tail is empty (end of string).\r
303      */\r
304     Scanner.prototype.eos = function eos () {\r
305         return this.tail === '';\r
306     };\r
307 \r
308     /**\r
309      * Tries to match the given regular expression at the current position.\r
310      * Returns the matched text if it can match, the empty string otherwise.\r
311      */\r
312     Scanner.prototype.scan = function scan (re) {\r
313         var match = this.tail.match(re);\r
314 \r
315         if (!match || match.index !== 0)\r
316             return '';\r
317 \r
318         var string = match[0];\r
319 \r
320         this.tail = this.tail.substring(string.length);\r
321         this.pos += string.length;\r
322 \r
323         return string;\r
324     };\r
325 \r
326     /**\r
327      * Skips all text until the given regular expression can be matched. Returns\r
328      * the skipped string, which is the entire tail if no match can be made.\r
329      */\r
330     Scanner.prototype.scanUntil = function scanUntil (re) {\r
331         var index = this.tail.search(re), match;\r
332 \r
333         switch (index) {\r
334             case -1:\r
335                 match = this.tail;\r
336                 this.tail = '';\r
337                 break;\r
338             case 0:\r
339                 match = '';\r
340                 break;\r
341             default:\r
342                 match = this.tail.substring(0, index);\r
343                 this.tail = this.tail.substring(index);\r
344         }\r
345 \r
346         this.pos += match.length;\r
347 \r
348         return match;\r
349     };\r
350 \r
351     /**\r
352      * Represents a rendering context by wrapping a view object and\r
353      * maintaining a reference to the parent context.\r
354      */\r
355     function Context (view, parentContext) {\r
356         this.view = view;\r
357         this.cache = { '.': this.view };\r
358         this.parent = parentContext;\r
359     }\r
360 \r
361     /**\r
362      * Creates a new context using the given view with this context\r
363      * as the parent.\r
364      */\r
365     Context.prototype.push = function push (view) {\r
366         return new Context(view, this);\r
367     };\r
368 \r
369     /**\r
370      * Returns the value of the given name in this context, traversing\r
371      * up the context hierarchy if the value is absent in this context's view.\r
372      */\r
373     Context.prototype.lookup = function lookup (name) {\r
374         var cache = this.cache;\r
375 \r
376         var value;\r
377         if (cache.hasOwnProperty(name)) {\r
378             value = cache[name];\r
379         } else {\r
380             var context = this, names, index, lookupHit = false;\r
381 \r
382             while (context) {\r
383                 if (name.indexOf('.') > 0) {\r
384                     value = context.view;\r
385                     names = name.split('.');\r
386                     index = 0;\r
387 \r
388                     /**\r
389                      * Using the dot notion path in `name`, we descend through the\r
390                      * nested objects.\r
391                      *\r
392                      * To be certain that the lookup has been successful, we have to\r
393                      * check if the last object in the path actually has the property\r
394                      * we are looking for. We store the result in `lookupHit`.\r
395                      *\r
396                      * This is specially necessary for when the value has been set to\r
397                      * `undefined` and we want to avoid looking up parent contexts.\r
398                      **/\r
399                     while (value != null && index < names.length) {\r
400                         if (index === names.length - 1)\r
401                             lookupHit = hasProperty(value, names[index]);\r
402 \r
403                         value = value[names[index++]];\r
404                     }\r
405                 } else {\r
406                     value = context.view[name];\r
407                     lookupHit = hasProperty(context.view, name);\r
408                 }\r
409 \r
410                 if (lookupHit)\r
411                     break;\r
412 \r
413                 context = context.parent;\r
414             }\r
415 \r
416             cache[name] = value;\r
417         }\r
418 \r
419         if (isFunction(value))\r
420             value = value.call(this.view);\r
421 \r
422         return value;\r
423     };\r
424 \r
425     /**\r
426      * A Writer knows how to take a stream of tokens and render them to a\r
427      * string, given a context. It also maintains a cache of templates to\r
428      * avoid the need to parse the same template twice.\r
429      */\r
430     function Writer () {\r
431         this.cache = {};\r
432     }\r
433 \r
434     /**\r
435      * Clears all cached templates in this writer.\r
436      */\r
437     Writer.prototype.clearCache = function clearCache () {\r
438         this.cache = {};\r
439     };\r
440 \r
441     /**\r
442      * Parses and caches the given `template` and returns the array of tokens\r
443      * that is generated from the parse.\r
444      */\r
445     Writer.prototype.parse = function parse (template, tags) {\r
446         var cache = this.cache;\r
447         var tokens = cache[template];\r
448 \r
449         if (tokens == null)\r
450             tokens = cache[template] = parseTemplate(template, tags);\r
451 \r
452         return tokens;\r
453     };\r
454 \r
455     /**\r
456      * High-level method that is used to render the given `template` with\r
457      * the given `view`.\r
458      *\r
459      * The optional `partials` argument may be an object that contains the\r
460      * names and templates of partials that are used in the template. It may\r
461      * also be a function that is used to load partial templates on the fly\r
462      * that takes a single argument: the name of the partial.\r
463      */\r
464     Writer.prototype.render = function render (template, view, partials) {\r
465         var tokens = this.parse(template);\r
466         var context = (view instanceof Context) ? view : new Context(view);\r
467         return this.renderTokens(tokens, context, partials, template);\r
468     };\r
469 \r
470     /**\r
471      * Low-level method that renders the given array of `tokens` using\r
472      * the given `context` and `partials`.\r
473      *\r
474      * Note: The `originalTemplate` is only ever used to extract the portion\r
475      * of the original template that was contained in a higher-order section.\r
476      * If the template doesn't use higher-order sections, this argument may\r
477      * be omitted.\r
478      */\r
479     Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate) {\r
480         var buffer = '';\r
481 \r
482         var token, symbol, value;\r
483         for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {\r
484             value = undefined;\r
485             token = tokens[i];\r
486             symbol = token[0];\r
487 \r
488             if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);\r
489             else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);\r
490             else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate);\r
491             else if (symbol === '&') value = this.unescapedValue(token, context);\r
492             else if (symbol === 'name') value = this.escapedValue(token, context);\r
493             else if (symbol === 'text') value = this.rawValue(token);\r
494 \r
495             if (value !== undefined)\r
496                 buffer += value;\r
497         }\r
498 \r
499         return buffer;\r
500     };\r
501 \r
502     Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) {\r
503         var self = this;\r
504         var buffer = '';\r
505         var value = context.lookup(token[1]);\r
506 \r
507         // This function is used to render an arbitrary template\r
508         // in the current context by higher-order sections.\r
509         function subRender (template) {\r
510             return self.render(template, context, partials);\r
511         }\r
512 \r
513         if (!value) return;\r
514 \r
515         if (isArray(value)) {\r
516             for (var j = 0, valueLength = value.length; j < valueLength; ++j) {\r
517                 buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);\r
518             }\r
519         } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {\r
520             buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);\r
521         } else if (isFunction(value)) {\r
522             if (typeof originalTemplate !== 'string')\r
523                 throw new Error('Cannot use higher-order sections without the original template');\r
524 \r
525             // Extract the portion of the original template that the section contains.\r
526             value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);\r
527 \r
528             if (value != null)\r
529                 buffer += value;\r
530         } else {\r
531             buffer += this.renderTokens(token[4], context, partials, originalTemplate);\r
532         }\r
533         return buffer;\r
534     };\r
535 \r
536     Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) {\r
537         var value = context.lookup(token[1]);\r
538 \r
539         // Use JavaScript's definition of falsy. Include empty arrays.\r
540         // See https://github.com/janl/mustache.js/issues/186\r
541         if (!value || (isArray(value) && value.length === 0))\r
542             return this.renderTokens(token[4], context, partials, originalTemplate);\r
543     };\r
544 \r
545     Writer.prototype.renderPartial = function renderPartial (token, context, partials) {\r
546         if (!partials) return;\r
547 \r
548         var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];\r
549         if (value != null)\r
550             return this.renderTokens(this.parse(value), context, partials, value);\r
551     };\r
552 \r
553     Writer.prototype.unescapedValue = function unescapedValue (token, context) {\r
554         var value = context.lookup(token[1]);\r
555         if (value != null)\r
556             return value;\r
557     };\r
558 \r
559     Writer.prototype.escapedValue = function escapedValue (token, context) {\r
560         var value = context.lookup(token[1]);\r
561         if (value != null)\r
562             return mustache.escape(value);\r
563     };\r
564 \r
565     Writer.prototype.rawValue = function rawValue (token) {\r
566         return token[1];\r
567     };\r
568 \r
569     mustache.name = 'mustache.js';\r
570     mustache.version = '2.3.0';\r
571     mustache.tags = [ '{{', '}}' ];\r
572 \r
573     // All high-level mustache.* functions use this writer.\r
574     var defaultWriter = new Writer();\r
575 \r
576     /**\r
577      * Clears all cached templates in the default writer.\r
578      */\r
579     mustache.clearCache = function clearCache () {\r
580         return defaultWriter.clearCache();\r
581     };\r
582 \r
583     /**\r
584      * Parses and caches the given template in the default writer and returns the\r
585      * array of tokens it contains. Doing this ahead of time avoids the need to\r
586      * parse templates on the fly as they are rendered.\r
587      */\r
588     mustache.parse = function parse (template, tags) {\r
589         return defaultWriter.parse(template, tags);\r
590     };\r
591 \r
592     /**\r
593      * Renders the `template` with the given `view` and `partials` using the\r
594      * default writer.\r
595      */\r
596     mustache.render = function render (template, view, partials) {\r
597         if (typeof template !== 'string') {\r
598             throw new TypeError('Invalid template! Template should be a "string" ' +\r
599                 'but "' + typeStr(template) + '" was given as the first ' +\r
600                 'argument for mustache#render(template, view, partials)');\r
601         }\r
602 \r
603         return defaultWriter.render(template, view, partials);\r
604     };\r
605 \r
606     // This is here for backwards compatibility with 0.4.x.,\r
607     /*eslint-disable */ // eslint wants camel cased function name\r
608     mustache.to_html = function to_html (template, view, partials, send) {\r
609         /*eslint-enable*/\r
610 \r
611         var result = mustache.render(template, view, partials);\r
612 \r
613         if (isFunction(send)) {\r
614             send(result);\r
615         } else {\r
616             return result;\r
617         }\r
618     };\r
619 \r
620     // Export the escaping function so that the user may override it.\r
621     // See https://github.com/janl/mustache.js/issues/244\r
622     mustache.escape = escapeHtml;\r
623 \r
624     // Export these mainly for testing, but also for advanced usage.\r
625     mustache.Scanner = Scanner;\r
626     mustache.Context = Context;\r
627     mustache.Writer = Writer;\r
628 \r
629     return mustache;\r
630 }));