Use handelbars to make templates for the manifest files. Add tests for media_se…
…quence, playlist_type, and target_duration.
Showing
6 changed files
with
2905 additions
and
0 deletions
libs/handlebars/handlebars-v1.1.2.js
0 → 100644
1 | /*! | ||
2 | |||
3 | handlebars v1.1.2 | ||
4 | |||
5 | Copyright (C) 2011 by Yehuda Katz | ||
6 | |||
7 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
8 | of this software and associated documentation files (the "Software"), to deal | ||
9 | in the Software without restriction, including without limitation the rights | ||
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
11 | copies of the Software, and to permit persons to whom the Software is | ||
12 | furnished to do so, subject to the following conditions: | ||
13 | |||
14 | The above copyright notice and this permission notice shall be included in | ||
15 | all copies or substantial portions of the Software. | ||
16 | |||
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
23 | THE SOFTWARE. | ||
24 | |||
25 | @license | ||
26 | */ | ||
27 | var Handlebars = (function() { | ||
28 | // handlebars/safe-string.js | ||
29 | var __module4__ = (function() { | ||
30 | "use strict"; | ||
31 | var __exports__; | ||
32 | // Build out our basic SafeString type | ||
33 | function SafeString(string) { | ||
34 | this.string = string; | ||
35 | } | ||
36 | |||
37 | SafeString.prototype.toString = function() { | ||
38 | return "" + this.string; | ||
39 | }; | ||
40 | |||
41 | __exports__ = SafeString; | ||
42 | return __exports__; | ||
43 | })(); | ||
44 | |||
45 | // handlebars/utils.js | ||
46 | var __module3__ = (function(__dependency1__) { | ||
47 | "use strict"; | ||
48 | var __exports__ = {}; | ||
49 | var SafeString = __dependency1__; | ||
50 | |||
51 | var escape = { | ||
52 | "&": "&", | ||
53 | "<": "<", | ||
54 | ">": ">", | ||
55 | '"': """, | ||
56 | "'": "'", | ||
57 | "`": "`" | ||
58 | }; | ||
59 | |||
60 | var badChars = /[&<>"'`]/g; | ||
61 | var possible = /[&<>"'`]/; | ||
62 | |||
63 | function escapeChar(chr) { | ||
64 | return escape[chr] || "&"; | ||
65 | } | ||
66 | |||
67 | function extend(obj, value) { | ||
68 | for(var key in value) { | ||
69 | if(value.hasOwnProperty(key)) { | ||
70 | obj[key] = value[key]; | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | __exports__.extend = extend;var toString = Object.prototype.toString; | ||
76 | __exports__.toString = toString; | ||
77 | // Sourced from lodash | ||
78 | // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt | ||
79 | var isFunction = function(value) { | ||
80 | return typeof value === 'function'; | ||
81 | }; | ||
82 | // fallback for older versions of Chrome and Safari | ||
83 | if (isFunction(/x/)) { | ||
84 | isFunction = function(value) { | ||
85 | return typeof value === 'function' && toString.call(value) === '[object Function]'; | ||
86 | }; | ||
87 | } | ||
88 | var isFunction; | ||
89 | __exports__.isFunction = isFunction; | ||
90 | var isArray = Array.isArray || function(value) { | ||
91 | return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; | ||
92 | }; | ||
93 | __exports__.isArray = isArray; | ||
94 | |||
95 | function escapeExpression(string) { | ||
96 | // don't escape SafeStrings, since they're already safe | ||
97 | if (string instanceof SafeString) { | ||
98 | return string.toString(); | ||
99 | } else if (!string && string !== 0) { | ||
100 | return ""; | ||
101 | } | ||
102 | |||
103 | // Force a string conversion as this will be done by the append regardless and | ||
104 | // the regex test will do this transparently behind the scenes, causing issues if | ||
105 | // an object's to string has escaped characters in it. | ||
106 | string = "" + string; | ||
107 | |||
108 | if(!possible.test(string)) { return string; } | ||
109 | return string.replace(badChars, escapeChar); | ||
110 | } | ||
111 | |||
112 | __exports__.escapeExpression = escapeExpression;function isEmpty(value) { | ||
113 | if (!value && value !== 0) { | ||
114 | return true; | ||
115 | } else if (isArray(value) && value.length === 0) { | ||
116 | return true; | ||
117 | } else { | ||
118 | return false; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | __exports__.isEmpty = isEmpty; | ||
123 | return __exports__; | ||
124 | })(__module4__); | ||
125 | |||
126 | // handlebars/exception.js | ||
127 | var __module5__ = (function() { | ||
128 | "use strict"; | ||
129 | var __exports__; | ||
130 | |||
131 | var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; | ||
132 | |||
133 | function Exception(/* message */) { | ||
134 | var tmp = Error.prototype.constructor.apply(this, arguments); | ||
135 | |||
136 | // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. | ||
137 | for (var idx = 0; idx < errorProps.length; idx++) { | ||
138 | this[errorProps[idx]] = tmp[errorProps[idx]]; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | Exception.prototype = new Error(); | ||
143 | |||
144 | __exports__ = Exception; | ||
145 | return __exports__; | ||
146 | })(); | ||
147 | |||
148 | // handlebars/base.js | ||
149 | var __module2__ = (function(__dependency1__, __dependency2__) { | ||
150 | "use strict"; | ||
151 | var __exports__ = {}; | ||
152 | /*globals Exception, Utils */ | ||
153 | var Utils = __dependency1__; | ||
154 | var Exception = __dependency2__; | ||
155 | |||
156 | var VERSION = "1.1.2"; | ||
157 | __exports__.VERSION = VERSION;var COMPILER_REVISION = 4; | ||
158 | __exports__.COMPILER_REVISION = COMPILER_REVISION; | ||
159 | var REVISION_CHANGES = { | ||
160 | 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it | ||
161 | 2: '== 1.0.0-rc.3', | ||
162 | 3: '== 1.0.0-rc.4', | ||
163 | 4: '>= 1.0.0' | ||
164 | }; | ||
165 | __exports__.REVISION_CHANGES = REVISION_CHANGES; | ||
166 | var isArray = Utils.isArray, | ||
167 | isFunction = Utils.isFunction, | ||
168 | toString = Utils.toString, | ||
169 | objectType = '[object Object]'; | ||
170 | |||
171 | function HandlebarsEnvironment(helpers, partials) { | ||
172 | this.helpers = helpers || {}; | ||
173 | this.partials = partials || {}; | ||
174 | |||
175 | registerDefaultHelpers(this); | ||
176 | } | ||
177 | |||
178 | __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = { | ||
179 | constructor: HandlebarsEnvironment, | ||
180 | |||
181 | logger: logger, | ||
182 | log: log, | ||
183 | |||
184 | registerHelper: function(name, fn, inverse) { | ||
185 | if (toString.call(name) === objectType) { | ||
186 | if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); } | ||
187 | Utils.extend(this.helpers, name); | ||
188 | } else { | ||
189 | if (inverse) { fn.not = inverse; } | ||
190 | this.helpers[name] = fn; | ||
191 | } | ||
192 | }, | ||
193 | |||
194 | registerPartial: function(name, str) { | ||
195 | if (toString.call(name) === objectType) { | ||
196 | Utils.extend(this.partials, name); | ||
197 | } else { | ||
198 | this.partials[name] = str; | ||
199 | } | ||
200 | } | ||
201 | }; | ||
202 | |||
203 | function registerDefaultHelpers(instance) { | ||
204 | instance.registerHelper('helperMissing', function(arg) { | ||
205 | if(arguments.length === 2) { | ||
206 | return undefined; | ||
207 | } else { | ||
208 | throw new Error("Missing helper: '" + arg + "'"); | ||
209 | } | ||
210 | }); | ||
211 | |||
212 | instance.registerHelper('blockHelperMissing', function(context, options) { | ||
213 | var inverse = options.inverse || function() {}, fn = options.fn; | ||
214 | |||
215 | if (isFunction(context)) { context = context.call(this); } | ||
216 | |||
217 | if(context === true) { | ||
218 | return fn(this); | ||
219 | } else if(context === false || context == null) { | ||
220 | return inverse(this); | ||
221 | } else if (isArray(context)) { | ||
222 | if(context.length > 0) { | ||
223 | return instance.helpers.each(context, options); | ||
224 | } else { | ||
225 | return inverse(this); | ||
226 | } | ||
227 | } else { | ||
228 | return fn(context); | ||
229 | } | ||
230 | }); | ||
231 | |||
232 | instance.registerHelper('each', function(context, options) { | ||
233 | var fn = options.fn, inverse = options.inverse; | ||
234 | var i = 0, ret = "", data; | ||
235 | |||
236 | if (isFunction(context)) { context = context.call(this); } | ||
237 | |||
238 | if (options.data) { | ||
239 | data = createFrame(options.data); | ||
240 | } | ||
241 | |||
242 | if(context && typeof context === 'object') { | ||
243 | if (isArray(context)) { | ||
244 | for(var j = context.length; i<j; i++) { | ||
245 | if (data) { | ||
246 | data.index = i; | ||
247 | data.first = (i === 0) | ||
248 | data.last = (i === (context.length-1)); | ||
249 | } | ||
250 | ret = ret + fn(context[i], { data: data }); | ||
251 | } | ||
252 | } else { | ||
253 | for(var key in context) { | ||
254 | if(context.hasOwnProperty(key)) { | ||
255 | if(data) { data.key = key; } | ||
256 | ret = ret + fn(context[key], {data: data}); | ||
257 | i++; | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | |||
263 | if(i === 0){ | ||
264 | ret = inverse(this); | ||
265 | } | ||
266 | |||
267 | return ret; | ||
268 | }); | ||
269 | |||
270 | instance.registerHelper('if', function(conditional, options) { | ||
271 | if (isFunction(conditional)) { conditional = conditional.call(this); } | ||
272 | |||
273 | // Default behavior is to render the positive path if the value is truthy and not empty. | ||
274 | // The `includeZero` option may be set to treat the condtional as purely not empty based on the | ||
275 | // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. | ||
276 | if ((!options.hash.includeZero && !conditional) || Utils.isEmpty(conditional)) { | ||
277 | return options.inverse(this); | ||
278 | } else { | ||
279 | return options.fn(this); | ||
280 | } | ||
281 | }); | ||
282 | |||
283 | instance.registerHelper('unless', function(conditional, options) { | ||
284 | return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash}); | ||
285 | }); | ||
286 | |||
287 | instance.registerHelper('with', function(context, options) { | ||
288 | if (isFunction(context)) { context = context.call(this); } | ||
289 | |||
290 | if (!Utils.isEmpty(context)) return options.fn(context); | ||
291 | }); | ||
292 | |||
293 | instance.registerHelper('log', function(context, options) { | ||
294 | var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1; | ||
295 | instance.log(level, context); | ||
296 | }); | ||
297 | } | ||
298 | |||
299 | var logger = { | ||
300 | methodMap: { 0: 'debug', 1: 'info', 2: 'warn', 3: 'error' }, | ||
301 | |||
302 | // State enum | ||
303 | DEBUG: 0, | ||
304 | INFO: 1, | ||
305 | WARN: 2, | ||
306 | ERROR: 3, | ||
307 | level: 3, | ||
308 | |||
309 | // can be overridden in the host environment | ||
310 | log: function(level, obj) { | ||
311 | if (logger.level <= level) { | ||
312 | var method = logger.methodMap[level]; | ||
313 | if (typeof console !== 'undefined' && console[method]) { | ||
314 | console[method].call(console, obj); | ||
315 | } | ||
316 | } | ||
317 | } | ||
318 | }; | ||
319 | __exports__.logger = logger; | ||
320 | function log(level, obj) { logger.log(level, obj); } | ||
321 | |||
322 | __exports__.log = log;var createFrame = function(object) { | ||
323 | var obj = {}; | ||
324 | Utils.extend(obj, object); | ||
325 | return obj; | ||
326 | }; | ||
327 | __exports__.createFrame = createFrame; | ||
328 | return __exports__; | ||
329 | })(__module3__, __module5__); | ||
330 | |||
331 | // handlebars/runtime.js | ||
332 | var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) { | ||
333 | "use strict"; | ||
334 | var __exports__ = {}; | ||
335 | /*global Utils */ | ||
336 | var Utils = __dependency1__; | ||
337 | var Exception = __dependency2__; | ||
338 | var COMPILER_REVISION = __dependency3__.COMPILER_REVISION; | ||
339 | var REVISION_CHANGES = __dependency3__.REVISION_CHANGES; | ||
340 | |||
341 | function checkRevision(compilerInfo) { | ||
342 | var compilerRevision = compilerInfo && compilerInfo[0] || 1, | ||
343 | currentRevision = COMPILER_REVISION; | ||
344 | |||
345 | if (compilerRevision !== currentRevision) { | ||
346 | if (compilerRevision < currentRevision) { | ||
347 | var runtimeVersions = REVISION_CHANGES[currentRevision], | ||
348 | compilerVersions = REVISION_CHANGES[compilerRevision]; | ||
349 | throw new Error("Template was precompiled with an older version of Handlebars than the current runtime. "+ | ||
350 | "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+")."); | ||
351 | } else { | ||
352 | // Use the embedded version info since the runtime doesn't know about this revision yet | ||
353 | throw new Error("Template was precompiled with a newer version of Handlebars than the current runtime. "+ | ||
354 | "Please update your runtime to a newer version ("+compilerInfo[1]+")."); | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | |||
359 | // TODO: Remove this line and break up compilePartial | ||
360 | |||
361 | function template(templateSpec, env) { | ||
362 | if (!env) { | ||
363 | throw new Error("No environment passed to template"); | ||
364 | } | ||
365 | |||
366 | var invokePartialWrapper; | ||
367 | if (env.compile) { | ||
368 | invokePartialWrapper = function(partial, name, context, helpers, partials, data) { | ||
369 | // TODO : Check this for all inputs and the options handling (partial flag, etc). This feels | ||
370 | // like there should be a common exec path | ||
371 | var result = invokePartial.apply(this, arguments); | ||
372 | if (result) { return result; } | ||
373 | |||
374 | var options = { helpers: helpers, partials: partials, data: data }; | ||
375 | partials[name] = env.compile(partial, { data: data !== undefined }, env); | ||
376 | return partials[name](context, options); | ||
377 | }; | ||
378 | } else { | ||
379 | invokePartialWrapper = function(partial, name /* , context, helpers, partials, data */) { | ||
380 | var result = invokePartial.apply(this, arguments); | ||
381 | if (result) { return result; } | ||
382 | throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); | ||
383 | }; | ||
384 | } | ||
385 | |||
386 | // Just add water | ||
387 | var container = { | ||
388 | escapeExpression: Utils.escapeExpression, | ||
389 | invokePartial: invokePartialWrapper, | ||
390 | programs: [], | ||
391 | program: function(i, fn, data) { | ||
392 | var programWrapper = this.programs[i]; | ||
393 | if(data) { | ||
394 | programWrapper = program(i, fn, data); | ||
395 | } else if (!programWrapper) { | ||
396 | programWrapper = this.programs[i] = program(i, fn); | ||
397 | } | ||
398 | return programWrapper; | ||
399 | }, | ||
400 | merge: function(param, common) { | ||
401 | var ret = param || common; | ||
402 | |||
403 | if (param && common && (param !== common)) { | ||
404 | ret = {}; | ||
405 | Utils.extend(ret, common); | ||
406 | Utils.extend(ret, param); | ||
407 | } | ||
408 | return ret; | ||
409 | }, | ||
410 | programWithDepth: programWithDepth, | ||
411 | noop: noop, | ||
412 | compilerInfo: null | ||
413 | }; | ||
414 | |||
415 | return function(context, options) { | ||
416 | options = options || {}; | ||
417 | var namespace = options.partial ? options : env, | ||
418 | helpers, | ||
419 | partials; | ||
420 | |||
421 | if (!options.partial) { | ||
422 | helpers = options.helpers; | ||
423 | partials = options.partials; | ||
424 | } | ||
425 | var result = templateSpec.call( | ||
426 | container, | ||
427 | namespace, context, | ||
428 | helpers, | ||
429 | partials, | ||
430 | options.data); | ||
431 | |||
432 | if (!options.partial) { | ||
433 | checkRevision(container.compilerInfo); | ||
434 | } | ||
435 | |||
436 | return result; | ||
437 | }; | ||
438 | } | ||
439 | |||
440 | __exports__.template = template;function programWithDepth(i, fn, data /*, $depth */) { | ||
441 | var args = Array.prototype.slice.call(arguments, 3); | ||
442 | |||
443 | var prog = function(context, options) { | ||
444 | options = options || {}; | ||
445 | |||
446 | return fn.apply(this, [context, options.data || data].concat(args)); | ||
447 | }; | ||
448 | prog.program = i; | ||
449 | prog.depth = args.length; | ||
450 | return prog; | ||
451 | } | ||
452 | |||
453 | __exports__.programWithDepth = programWithDepth;function program(i, fn, data) { | ||
454 | var prog = function(context, options) { | ||
455 | options = options || {}; | ||
456 | |||
457 | return fn(context, options.data || data); | ||
458 | }; | ||
459 | prog.program = i; | ||
460 | prog.depth = 0; | ||
461 | return prog; | ||
462 | } | ||
463 | |||
464 | __exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data) { | ||
465 | var options = { partial: true, helpers: helpers, partials: partials, data: data }; | ||
466 | |||
467 | if(partial === undefined) { | ||
468 | throw new Exception("The partial " + name + " could not be found"); | ||
469 | } else if(partial instanceof Function) { | ||
470 | return partial(context, options); | ||
471 | } | ||
472 | } | ||
473 | |||
474 | __exports__.invokePartial = invokePartial;function noop() { return ""; } | ||
475 | |||
476 | __exports__.noop = noop; | ||
477 | return __exports__; | ||
478 | })(__module3__, __module5__, __module2__); | ||
479 | |||
480 | // handlebars.runtime.js | ||
481 | var __module1__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { | ||
482 | "use strict"; | ||
483 | var __exports__; | ||
484 | var base = __dependency1__; | ||
485 | |||
486 | // Each of these augment the Handlebars object. No need to setup here. | ||
487 | // (This is done to easily share code between commonjs and browse envs) | ||
488 | var SafeString = __dependency2__; | ||
489 | var Exception = __dependency3__; | ||
490 | var Utils = __dependency4__; | ||
491 | var runtime = __dependency5__; | ||
492 | |||
493 | // For compatibility and usage outside of module systems, make the Handlebars object a namespace | ||
494 | var create = function() { | ||
495 | var hb = new base.HandlebarsEnvironment(); | ||
496 | |||
497 | Utils.extend(hb, base); | ||
498 | hb.SafeString = SafeString; | ||
499 | hb.Exception = Exception; | ||
500 | hb.Utils = Utils; | ||
501 | |||
502 | hb.VM = runtime; | ||
503 | hb.template = function(spec) { | ||
504 | return runtime.template(spec, hb); | ||
505 | }; | ||
506 | |||
507 | return hb; | ||
508 | }; | ||
509 | |||
510 | var Handlebars = create(); | ||
511 | Handlebars.create = create; | ||
512 | |||
513 | __exports__ = Handlebars; | ||
514 | return __exports__; | ||
515 | })(__module2__, __module4__, __module5__, __module3__, __module6__); | ||
516 | |||
517 | // handlebars/compiler/ast.js | ||
518 | var __module7__ = (function(__dependency1__) { | ||
519 | "use strict"; | ||
520 | var __exports__ = {}; | ||
521 | var Exception = __dependency1__; | ||
522 | |||
523 | function ProgramNode(statements, inverseStrip, inverse) { | ||
524 | this.type = "program"; | ||
525 | this.statements = statements; | ||
526 | this.strip = {}; | ||
527 | |||
528 | if(inverse) { | ||
529 | this.inverse = new ProgramNode(inverse, inverseStrip); | ||
530 | this.strip.right = inverseStrip.left; | ||
531 | } else if (inverseStrip) { | ||
532 | this.strip.left = inverseStrip.right; | ||
533 | } | ||
534 | } | ||
535 | |||
536 | __exports__.ProgramNode = ProgramNode;function MustacheNode(rawParams, hash, open, strip) { | ||
537 | this.type = "mustache"; | ||
538 | this.hash = hash; | ||
539 | this.strip = strip; | ||
540 | |||
541 | var escapeFlag = open[3] || open[2]; | ||
542 | this.escaped = escapeFlag !== '{' && escapeFlag !== '&'; | ||
543 | |||
544 | var id = this.id = rawParams[0]; | ||
545 | var params = this.params = rawParams.slice(1); | ||
546 | |||
547 | // a mustache is an eligible helper if: | ||
548 | // * its id is simple (a single part, not `this` or `..`) | ||
549 | var eligibleHelper = this.eligibleHelper = id.isSimple; | ||
550 | |||
551 | // a mustache is definitely a helper if: | ||
552 | // * it is an eligible helper, and | ||
553 | // * it has at least one parameter or hash segment | ||
554 | this.isHelper = eligibleHelper && (params.length || hash); | ||
555 | |||
556 | // if a mustache is an eligible helper but not a definite | ||
557 | // helper, it is ambiguous, and will be resolved in a later | ||
558 | // pass or at runtime. | ||
559 | } | ||
560 | |||
561 | __exports__.MustacheNode = MustacheNode;function PartialNode(partialName, context, strip) { | ||
562 | this.type = "partial"; | ||
563 | this.partialName = partialName; | ||
564 | this.context = context; | ||
565 | this.strip = strip; | ||
566 | } | ||
567 | |||
568 | __exports__.PartialNode = PartialNode;function BlockNode(mustache, program, inverse, close) { | ||
569 | if(mustache.id.original !== close.path.original) { | ||
570 | throw new Exception(mustache.id.original + " doesn't match " + close.path.original); | ||
571 | } | ||
572 | |||
573 | this.type = "block"; | ||
574 | this.mustache = mustache; | ||
575 | this.program = program; | ||
576 | this.inverse = inverse; | ||
577 | |||
578 | this.strip = { | ||
579 | left: mustache.strip.left, | ||
580 | right: close.strip.right | ||
581 | }; | ||
582 | |||
583 | (program || inverse).strip.left = mustache.strip.right; | ||
584 | (inverse || program).strip.right = close.strip.left; | ||
585 | |||
586 | if (inverse && !program) { | ||
587 | this.isInverse = true; | ||
588 | } | ||
589 | } | ||
590 | |||
591 | __exports__.BlockNode = BlockNode;function ContentNode(string) { | ||
592 | this.type = "content"; | ||
593 | this.string = string; | ||
594 | } | ||
595 | |||
596 | __exports__.ContentNode = ContentNode;function HashNode(pairs) { | ||
597 | this.type = "hash"; | ||
598 | this.pairs = pairs; | ||
599 | } | ||
600 | |||
601 | __exports__.HashNode = HashNode;function IdNode(parts) { | ||
602 | this.type = "ID"; | ||
603 | |||
604 | var original = "", | ||
605 | dig = [], | ||
606 | depth = 0; | ||
607 | |||
608 | for(var i=0,l=parts.length; i<l; i++) { | ||
609 | var part = parts[i].part; | ||
610 | original += (parts[i].separator || '') + part; | ||
611 | |||
612 | if (part === ".." || part === "." || part === "this") { | ||
613 | if (dig.length > 0) { throw new Exception("Invalid path: " + original); } | ||
614 | else if (part === "..") { depth++; } | ||
615 | else { this.isScoped = true; } | ||
616 | } | ||
617 | else { dig.push(part); } | ||
618 | } | ||
619 | |||
620 | this.original = original; | ||
621 | this.parts = dig; | ||
622 | this.string = dig.join('.'); | ||
623 | this.depth = depth; | ||
624 | |||
625 | // an ID is simple if it only has one part, and that part is not | ||
626 | // `..` or `this`. | ||
627 | this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; | ||
628 | |||
629 | this.stringModeValue = this.string; | ||
630 | } | ||
631 | |||
632 | __exports__.IdNode = IdNode;function PartialNameNode(name) { | ||
633 | this.type = "PARTIAL_NAME"; | ||
634 | this.name = name.original; | ||
635 | } | ||
636 | |||
637 | __exports__.PartialNameNode = PartialNameNode;function DataNode(id) { | ||
638 | this.type = "DATA"; | ||
639 | this.id = id; | ||
640 | } | ||
641 | |||
642 | __exports__.DataNode = DataNode;function StringNode(string) { | ||
643 | this.type = "STRING"; | ||
644 | this.original = | ||
645 | this.string = | ||
646 | this.stringModeValue = string; | ||
647 | } | ||
648 | |||
649 | __exports__.StringNode = StringNode;function IntegerNode(integer) { | ||
650 | this.type = "INTEGER"; | ||
651 | this.original = | ||
652 | this.integer = integer; | ||
653 | this.stringModeValue = Number(integer); | ||
654 | } | ||
655 | |||
656 | __exports__.IntegerNode = IntegerNode;function BooleanNode(bool) { | ||
657 | this.type = "BOOLEAN"; | ||
658 | this.bool = bool; | ||
659 | this.stringModeValue = bool === "true"; | ||
660 | } | ||
661 | |||
662 | __exports__.BooleanNode = BooleanNode;function CommentNode(comment) { | ||
663 | this.type = "comment"; | ||
664 | this.comment = comment; | ||
665 | } | ||
666 | |||
667 | __exports__.CommentNode = CommentNode; | ||
668 | return __exports__; | ||
669 | })(__module5__); | ||
670 | |||
671 | // handlebars/compiler/parser.js | ||
672 | var __module9__ = (function() { | ||
673 | "use strict"; | ||
674 | var __exports__; | ||
675 | /* Jison generated parser */ | ||
676 | var handlebars = (function(){ | ||
677 | var parser = {trace: function trace() { }, | ||
678 | yy: {}, | ||
679 | symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"inMustache_repetition0":28,"inMustache_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"hash":35,"hash_repetition_plus0":36,"hashSegment":37,"ID":38,"EQUALS":39,"DATA":40,"pathSegments":41,"SEP":42,"$accept":0,"$end":1}, | ||
680 | terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",38:"ID",39:"EQUALS",40:"DATA",42:"SEP"}, | ||
681 | productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[35,1],[37,3],[26,1],[26,1],[26,1],[30,2],[21,1],[41,3],[41,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[36,1],[36,2]], | ||
682 | performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { | ||
683 | |||
684 | var $0 = $$.length - 1; | ||
685 | switch (yystate) { | ||
686 | case 1: return new yy.ProgramNode($$[$0-1]); | ||
687 | break; | ||
688 | case 2: return new yy.ProgramNode([]); | ||
689 | break; | ||
690 | case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0]); | ||
691 | break; | ||
692 | case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0]); | ||
693 | break; | ||
694 | case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], []); | ||
695 | break; | ||
696 | case 6:this.$ = new yy.ProgramNode($$[$0]); | ||
697 | break; | ||
698 | case 7:this.$ = new yy.ProgramNode([]); | ||
699 | break; | ||
700 | case 8:this.$ = new yy.ProgramNode([]); | ||
701 | break; | ||
702 | case 9:this.$ = [$$[$0]]; | ||
703 | break; | ||
704 | case 10: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; | ||
705 | break; | ||
706 | case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]); | ||
707 | break; | ||
708 | case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]); | ||
709 | break; | ||
710 | case 13:this.$ = $$[$0]; | ||
711 | break; | ||
712 | case 14:this.$ = $$[$0]; | ||
713 | break; | ||
714 | case 15:this.$ = new yy.ContentNode($$[$0]); | ||
715 | break; | ||
716 | case 16:this.$ = new yy.CommentNode($$[$0]); | ||
717 | break; | ||
718 | case 17:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); | ||
719 | break; | ||
720 | case 18:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); | ||
721 | break; | ||
722 | case 19:this.$ = {path: $$[$0-1], strip: stripFlags($$[$0-2], $$[$0])}; | ||
723 | break; | ||
724 | case 20:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); | ||
725 | break; | ||
726 | case 21:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); | ||
727 | break; | ||
728 | case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0])); | ||
729 | break; | ||
730 | case 23:this.$ = stripFlags($$[$0-1], $$[$0]); | ||
731 | break; | ||
732 | case 24:this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]]; | ||
733 | break; | ||
734 | case 25:this.$ = [[$$[$0]], null]; | ||
735 | break; | ||
736 | case 26:this.$ = $$[$0]; | ||
737 | break; | ||
738 | case 27:this.$ = new yy.StringNode($$[$0]); | ||
739 | break; | ||
740 | case 28:this.$ = new yy.IntegerNode($$[$0]); | ||
741 | break; | ||
742 | case 29:this.$ = new yy.BooleanNode($$[$0]); | ||
743 | break; | ||
744 | case 30:this.$ = $$[$0]; | ||
745 | break; | ||
746 | case 31:this.$ = new yy.HashNode($$[$0]); | ||
747 | break; | ||
748 | case 32:this.$ = [$$[$0-2], $$[$0]]; | ||
749 | break; | ||
750 | case 33:this.$ = new yy.PartialNameNode($$[$0]); | ||
751 | break; | ||
752 | case 34:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0])); | ||
753 | break; | ||
754 | case 35:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0])); | ||
755 | break; | ||
756 | case 36:this.$ = new yy.DataNode($$[$0]); | ||
757 | break; | ||
758 | case 37:this.$ = new yy.IdNode($$[$0]); | ||
759 | break; | ||
760 | case 38: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2]; | ||
761 | break; | ||
762 | case 39:this.$ = [{part: $$[$0]}]; | ||
763 | break; | ||
764 | case 42:this.$ = []; | ||
765 | break; | ||
766 | case 43:$$[$0-1].push($$[$0]); | ||
767 | break; | ||
768 | case 46:this.$ = [$$[$0]]; | ||
769 | break; | ||
770 | case 47:$$[$0-1].push($$[$0]); | ||
771 | break; | ||
772 | } | ||
773 | }, | ||
774 | table: [{3:1,4:2,5:[1,3],8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[3]},{5:[1,16],8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:29,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:30,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:31,21:24,30:25,38:[1,28],40:[1,27],41:26},{21:33,26:32,32:[1,34],33:[1,35],38:[1,28],41:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,38:[1,28],40:[1,27],41:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,42],24:[2,42],28:43,32:[2,42],33:[2,42],34:[2,42],38:[2,42],40:[2,42]},{18:[2,25],24:[2,25]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],38:[2,37],40:[2,37],42:[1,44]},{21:45,38:[1,28],41:26},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],40:[2,39],42:[2,39]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,40],21:50,27:49,38:[1,28],41:26},{18:[2,33],38:[2,33]},{18:[2,34],38:[2,34]},{18:[2,35],38:[2,35]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,38:[1,28],41:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,44],21:56,24:[2,44],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:55,36:61,37:62,38:[1,63],40:[1,27],41:26},{38:[1,64]},{18:[2,36],24:[2,36],32:[2,36],33:[2,36],34:[2,36],38:[2,36],40:[2,36]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,65]},{18:[2,41]},{18:[1,66]},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],25:[1,15]},{18:[2,24],24:[2,24]},{18:[2,43],24:[2,43],32:[2,43],33:[2,43],34:[2,43],38:[2,43],40:[2,43]},{18:[2,45],24:[2,45]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],38:[2,26],40:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],38:[2,27],40:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],38:[2,28],40:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],38:[2,29],40:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],38:[2,30],40:[2,30]},{18:[2,31],24:[2,31],37:67,38:[1,68]},{18:[2,46],24:[2,46],38:[2,46]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],39:[1,69],40:[2,39],42:[2,39]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],38:[2,38],40:[2,38],42:[2,38]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{18:[2,47],24:[2,47],38:[2,47]},{39:[1,69]},{21:56,30:60,31:70,32:[1,57],33:[1,58],34:[1,59],38:[1,28],40:[1,27],41:26},{18:[2,32],24:[2,32],38:[2,32]}], | ||
775 | defaultActions: {3:[2,2],16:[2,1],50:[2,41]}, | ||
776 | parseError: function parseError(str, hash) { | ||
777 | throw new Error(str); | ||
778 | }, | ||
779 | parse: function parse(input) { | ||
780 | var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; | ||
781 | this.lexer.setInput(input); | ||
782 | this.lexer.yy = this.yy; | ||
783 | this.yy.lexer = this.lexer; | ||
784 | this.yy.parser = this; | ||
785 | if (typeof this.lexer.yylloc == "undefined") | ||
786 | this.lexer.yylloc = {}; | ||
787 | var yyloc = this.lexer.yylloc; | ||
788 | lstack.push(yyloc); | ||
789 | var ranges = this.lexer.options && this.lexer.options.ranges; | ||
790 | if (typeof this.yy.parseError === "function") | ||
791 | this.parseError = this.yy.parseError; | ||
792 | function popStack(n) { | ||
793 | stack.length = stack.length - 2 * n; | ||
794 | vstack.length = vstack.length - n; | ||
795 | lstack.length = lstack.length - n; | ||
796 | } | ||
797 | function lex() { | ||
798 | var token; | ||
799 | token = self.lexer.lex() || 1; | ||
800 | if (typeof token !== "number") { | ||
801 | token = self.symbols_[token] || token; | ||
802 | } | ||
803 | return token; | ||
804 | } | ||
805 | var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; | ||
806 | while (true) { | ||
807 | state = stack[stack.length - 1]; | ||
808 | if (this.defaultActions[state]) { | ||
809 | action = this.defaultActions[state]; | ||
810 | } else { | ||
811 | if (symbol === null || typeof symbol == "undefined") { | ||
812 | symbol = lex(); | ||
813 | } | ||
814 | action = table[state] && table[state][symbol]; | ||
815 | } | ||
816 | if (typeof action === "undefined" || !action.length || !action[0]) { | ||
817 | var errStr = ""; | ||
818 | if (!recovering) { | ||
819 | expected = []; | ||
820 | for (p in table[state]) | ||
821 | if (this.terminals_[p] && p > 2) { | ||
822 | expected.push("'" + this.terminals_[p] + "'"); | ||
823 | } | ||
824 | if (this.lexer.showPosition) { | ||
825 | errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; | ||
826 | } else { | ||
827 | errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); | ||
828 | } | ||
829 | this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); | ||
830 | } | ||
831 | } | ||
832 | if (action[0] instanceof Array && action.length > 1) { | ||
833 | throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); | ||
834 | } | ||
835 | switch (action[0]) { | ||
836 | case 1: | ||
837 | stack.push(symbol); | ||
838 | vstack.push(this.lexer.yytext); | ||
839 | lstack.push(this.lexer.yylloc); | ||
840 | stack.push(action[1]); | ||
841 | symbol = null; | ||
842 | if (!preErrorSymbol) { | ||
843 | yyleng = this.lexer.yyleng; | ||
844 | yytext = this.lexer.yytext; | ||
845 | yylineno = this.lexer.yylineno; | ||
846 | yyloc = this.lexer.yylloc; | ||
847 | if (recovering > 0) | ||
848 | recovering--; | ||
849 | } else { | ||
850 | symbol = preErrorSymbol; | ||
851 | preErrorSymbol = null; | ||
852 | } | ||
853 | break; | ||
854 | case 2: | ||
855 | len = this.productions_[action[1]][1]; | ||
856 | yyval.$ = vstack[vstack.length - len]; | ||
857 | yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; | ||
858 | if (ranges) { | ||
859 | yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; | ||
860 | } | ||
861 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); | ||
862 | if (typeof r !== "undefined") { | ||
863 | return r; | ||
864 | } | ||
865 | if (len) { | ||
866 | stack = stack.slice(0, -1 * len * 2); | ||
867 | vstack = vstack.slice(0, -1 * len); | ||
868 | lstack = lstack.slice(0, -1 * len); | ||
869 | } | ||
870 | stack.push(this.productions_[action[1]][0]); | ||
871 | vstack.push(yyval.$); | ||
872 | lstack.push(yyval._$); | ||
873 | newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; | ||
874 | stack.push(newState); | ||
875 | break; | ||
876 | case 3: | ||
877 | return true; | ||
878 | } | ||
879 | } | ||
880 | return true; | ||
881 | } | ||
882 | }; | ||
883 | |||
884 | |||
885 | function stripFlags(open, close) { | ||
886 | return { | ||
887 | left: open[2] === '~', | ||
888 | right: close[0] === '~' || close[1] === '~' | ||
889 | }; | ||
890 | } | ||
891 | |||
892 | /* Jison generated lexer */ | ||
893 | var lexer = (function(){ | ||
894 | var lexer = ({EOF:1, | ||
895 | parseError:function parseError(str, hash) { | ||
896 | if (this.yy.parser) { | ||
897 | this.yy.parser.parseError(str, hash); | ||
898 | } else { | ||
899 | throw new Error(str); | ||
900 | } | ||
901 | }, | ||
902 | setInput:function (input) { | ||
903 | this._input = input; | ||
904 | this._more = this._less = this.done = false; | ||
905 | this.yylineno = this.yyleng = 0; | ||
906 | this.yytext = this.matched = this.match = ''; | ||
907 | this.conditionStack = ['INITIAL']; | ||
908 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; | ||
909 | if (this.options.ranges) this.yylloc.range = [0,0]; | ||
910 | this.offset = 0; | ||
911 | return this; | ||
912 | }, | ||
913 | input:function () { | ||
914 | var ch = this._input[0]; | ||
915 | this.yytext += ch; | ||
916 | this.yyleng++; | ||
917 | this.offset++; | ||
918 | this.match += ch; | ||
919 | this.matched += ch; | ||
920 | var lines = ch.match(/(?:\r\n?|\n).*/g); | ||
921 | if (lines) { | ||
922 | this.yylineno++; | ||
923 | this.yylloc.last_line++; | ||
924 | } else { | ||
925 | this.yylloc.last_column++; | ||
926 | } | ||
927 | if (this.options.ranges) this.yylloc.range[1]++; | ||
928 | |||
929 | this._input = this._input.slice(1); | ||
930 | return ch; | ||
931 | }, | ||
932 | unput:function (ch) { | ||
933 | var len = ch.length; | ||
934 | var lines = ch.split(/(?:\r\n?|\n)/g); | ||
935 | |||
936 | this._input = ch + this._input; | ||
937 | this.yytext = this.yytext.substr(0, this.yytext.length-len-1); | ||
938 | //this.yyleng -= len; | ||
939 | this.offset -= len; | ||
940 | var oldLines = this.match.split(/(?:\r\n?|\n)/g); | ||
941 | this.match = this.match.substr(0, this.match.length-1); | ||
942 | this.matched = this.matched.substr(0, this.matched.length-1); | ||
943 | |||
944 | if (lines.length-1) this.yylineno -= lines.length-1; | ||
945 | var r = this.yylloc.range; | ||
946 | |||
947 | this.yylloc = {first_line: this.yylloc.first_line, | ||
948 | last_line: this.yylineno+1, | ||
949 | first_column: this.yylloc.first_column, | ||
950 | last_column: lines ? | ||
951 | (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: | ||
952 | this.yylloc.first_column - len | ||
953 | }; | ||
954 | |||
955 | if (this.options.ranges) { | ||
956 | this.yylloc.range = [r[0], r[0] + this.yyleng - len]; | ||
957 | } | ||
958 | return this; | ||
959 | }, | ||
960 | more:function () { | ||
961 | this._more = true; | ||
962 | return this; | ||
963 | }, | ||
964 | less:function (n) { | ||
965 | this.unput(this.match.slice(n)); | ||
966 | }, | ||
967 | pastInput:function () { | ||
968 | var past = this.matched.substr(0, this.matched.length - this.match.length); | ||
969 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); | ||
970 | }, | ||
971 | upcomingInput:function () { | ||
972 | var next = this.match; | ||
973 | if (next.length < 20) { | ||
974 | next += this._input.substr(0, 20-next.length); | ||
975 | } | ||
976 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); | ||
977 | }, | ||
978 | showPosition:function () { | ||
979 | var pre = this.pastInput(); | ||
980 | var c = new Array(pre.length + 1).join("-"); | ||
981 | return pre + this.upcomingInput() + "\n" + c+"^"; | ||
982 | }, | ||
983 | next:function () { | ||
984 | if (this.done) { | ||
985 | return this.EOF; | ||
986 | } | ||
987 | if (!this._input) this.done = true; | ||
988 | |||
989 | var token, | ||
990 | match, | ||
991 | tempMatch, | ||
992 | index, | ||
993 | col, | ||
994 | lines; | ||
995 | if (!this._more) { | ||
996 | this.yytext = ''; | ||
997 | this.match = ''; | ||
998 | } | ||
999 | var rules = this._currentRules(); | ||
1000 | for (var i=0;i < rules.length; i++) { | ||
1001 | tempMatch = this._input.match(this.rules[rules[i]]); | ||
1002 | if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { | ||
1003 | match = tempMatch; | ||
1004 | index = i; | ||
1005 | if (!this.options.flex) break; | ||
1006 | } | ||
1007 | } | ||
1008 | if (match) { | ||
1009 | lines = match[0].match(/(?:\r\n?|\n).*/g); | ||
1010 | if (lines) this.yylineno += lines.length; | ||
1011 | this.yylloc = {first_line: this.yylloc.last_line, | ||
1012 | last_line: this.yylineno+1, | ||
1013 | first_column: this.yylloc.last_column, | ||
1014 | last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; | ||
1015 | this.yytext += match[0]; | ||
1016 | this.match += match[0]; | ||
1017 | this.matches = match; | ||
1018 | this.yyleng = this.yytext.length; | ||
1019 | if (this.options.ranges) { | ||
1020 | this.yylloc.range = [this.offset, this.offset += this.yyleng]; | ||
1021 | } | ||
1022 | this._more = false; | ||
1023 | this._input = this._input.slice(match[0].length); | ||
1024 | this.matched += match[0]; | ||
1025 | token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); | ||
1026 | if (this.done && this._input) this.done = false; | ||
1027 | if (token) return token; | ||
1028 | else return; | ||
1029 | } | ||
1030 | if (this._input === "") { | ||
1031 | return this.EOF; | ||
1032 | } else { | ||
1033 | return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), | ||
1034 | {text: "", token: null, line: this.yylineno}); | ||
1035 | } | ||
1036 | }, | ||
1037 | lex:function lex() { | ||
1038 | var r = this.next(); | ||
1039 | if (typeof r !== 'undefined') { | ||
1040 | return r; | ||
1041 | } else { | ||
1042 | return this.lex(); | ||
1043 | } | ||
1044 | }, | ||
1045 | begin:function begin(condition) { | ||
1046 | this.conditionStack.push(condition); | ||
1047 | }, | ||
1048 | popState:function popState() { | ||
1049 | return this.conditionStack.pop(); | ||
1050 | }, | ||
1051 | _currentRules:function _currentRules() { | ||
1052 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; | ||
1053 | }, | ||
1054 | topState:function () { | ||
1055 | return this.conditionStack[this.conditionStack.length-2]; | ||
1056 | }, | ||
1057 | pushState:function begin(condition) { | ||
1058 | this.begin(condition); | ||
1059 | }}); | ||
1060 | lexer.options = {}; | ||
1061 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { | ||
1062 | |||
1063 | |||
1064 | function strip(start, end) { | ||
1065 | return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end); | ||
1066 | } | ||
1067 | |||
1068 | |||
1069 | var YYSTATE=YY_START | ||
1070 | switch($avoiding_name_collisions) { | ||
1071 | case 0: | ||
1072 | if(yy_.yytext.slice(-2) === "\\\\") { | ||
1073 | strip(0,1); | ||
1074 | this.begin("mu"); | ||
1075 | } else if(yy_.yytext.slice(-1) === "\\") { | ||
1076 | strip(0,1); | ||
1077 | this.begin("emu"); | ||
1078 | } else { | ||
1079 | this.begin("mu"); | ||
1080 | } | ||
1081 | if(yy_.yytext) return 14; | ||
1082 | |||
1083 | break; | ||
1084 | case 1:return 14; | ||
1085 | break; | ||
1086 | case 2: | ||
1087 | if(yy_.yytext.slice(-1) !== "\\") this.popState(); | ||
1088 | if(yy_.yytext.slice(-1) === "\\") strip(0,1); | ||
1089 | return 14; | ||
1090 | |||
1091 | break; | ||
1092 | case 3:strip(0,4); this.popState(); return 15; | ||
1093 | break; | ||
1094 | case 4:return 25; | ||
1095 | break; | ||
1096 | case 5:return 16; | ||
1097 | break; | ||
1098 | case 6:return 20; | ||
1099 | break; | ||
1100 | case 7:return 19; | ||
1101 | break; | ||
1102 | case 8:return 19; | ||
1103 | break; | ||
1104 | case 9:return 23; | ||
1105 | break; | ||
1106 | case 10:return 22; | ||
1107 | break; | ||
1108 | case 11:this.popState(); this.begin('com'); | ||
1109 | break; | ||
1110 | case 12:strip(3,5); this.popState(); return 15; | ||
1111 | break; | ||
1112 | case 13:return 22; | ||
1113 | break; | ||
1114 | case 14:return 39; | ||
1115 | break; | ||
1116 | case 15:return 38; | ||
1117 | break; | ||
1118 | case 16:return 38; | ||
1119 | break; | ||
1120 | case 17:return 42; | ||
1121 | break; | ||
1122 | case 18:/*ignore whitespace*/ | ||
1123 | break; | ||
1124 | case 19:this.popState(); return 24; | ||
1125 | break; | ||
1126 | case 20:this.popState(); return 18; | ||
1127 | break; | ||
1128 | case 21:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32; | ||
1129 | break; | ||
1130 | case 22:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32; | ||
1131 | break; | ||
1132 | case 23:return 40; | ||
1133 | break; | ||
1134 | case 24:return 34; | ||
1135 | break; | ||
1136 | case 25:return 34; | ||
1137 | break; | ||
1138 | case 26:return 33; | ||
1139 | break; | ||
1140 | case 27:return 38; | ||
1141 | break; | ||
1142 | case 28:yy_.yytext = strip(1,2); return 38; | ||
1143 | break; | ||
1144 | case 29:return 'INVALID'; | ||
1145 | break; | ||
1146 | case 30:return 5; | ||
1147 | break; | ||
1148 | } | ||
1149 | }; | ||
1150 | lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s])))/,/^(?:false(?=([~}\s])))/,/^(?:-?[0-9]+(?=([~}\s])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; | ||
1151 | lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,30],"inclusive":true}}; | ||
1152 | return lexer;})() | ||
1153 | parser.lexer = lexer; | ||
1154 | function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; | ||
1155 | return new Parser; | ||
1156 | })();__exports__ = handlebars; | ||
1157 | return __exports__; | ||
1158 | })(); | ||
1159 | |||
1160 | // handlebars/compiler/base.js | ||
1161 | var __module8__ = (function(__dependency1__, __dependency2__) { | ||
1162 | "use strict"; | ||
1163 | var __exports__ = {}; | ||
1164 | var parser = __dependency1__; | ||
1165 | var AST = __dependency2__; | ||
1166 | |||
1167 | __exports__.parser = parser; | ||
1168 | |||
1169 | function parse(input) { | ||
1170 | // Just return if an already-compile AST was passed in. | ||
1171 | if(input.constructor === AST.ProgramNode) { return input; } | ||
1172 | |||
1173 | parser.yy = AST; | ||
1174 | return parser.parse(input); | ||
1175 | } | ||
1176 | |||
1177 | __exports__.parse = parse; | ||
1178 | return __exports__; | ||
1179 | })(__module9__, __module7__); | ||
1180 | |||
1181 | // handlebars/compiler/javascript-compiler.js | ||
1182 | var __module11__ = (function(__dependency1__) { | ||
1183 | "use strict"; | ||
1184 | var __exports__; | ||
1185 | var COMPILER_REVISION = __dependency1__.COMPILER_REVISION; | ||
1186 | var REVISION_CHANGES = __dependency1__.REVISION_CHANGES; | ||
1187 | var log = __dependency1__.log; | ||
1188 | |||
1189 | function Literal(value) { | ||
1190 | this.value = value; | ||
1191 | } | ||
1192 | |||
1193 | function JavaScriptCompiler() {} | ||
1194 | |||
1195 | JavaScriptCompiler.prototype = { | ||
1196 | // PUBLIC API: You can override these methods in a subclass to provide | ||
1197 | // alternative compiled forms for name lookup and buffering semantics | ||
1198 | nameLookup: function(parent, name /* , type*/) { | ||
1199 | var wrap, | ||
1200 | ret; | ||
1201 | if (parent.indexOf('depth') === 0) { | ||
1202 | wrap = true; | ||
1203 | } | ||
1204 | |||
1205 | if (/^[0-9]+$/.test(name)) { | ||
1206 | ret = parent + "[" + name + "]"; | ||
1207 | } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { | ||
1208 | ret = parent + "." + name; | ||
1209 | } | ||
1210 | else { | ||
1211 | ret = parent + "['" + name + "']"; | ||
1212 | } | ||
1213 | |||
1214 | if (wrap) { | ||
1215 | return '(' + parent + ' && ' + ret + ')'; | ||
1216 | } else { | ||
1217 | return ret; | ||
1218 | } | ||
1219 | }, | ||
1220 | |||
1221 | appendToBuffer: function(string) { | ||
1222 | if (this.environment.isSimple) { | ||
1223 | return "return " + string + ";"; | ||
1224 | } else { | ||
1225 | return { | ||
1226 | appendToBuffer: true, | ||
1227 | content: string, | ||
1228 | toString: function() { return "buffer += " + string + ";"; } | ||
1229 | }; | ||
1230 | } | ||
1231 | }, | ||
1232 | |||
1233 | initializeBuffer: function() { | ||
1234 | return this.quotedString(""); | ||
1235 | }, | ||
1236 | |||
1237 | namespace: "Handlebars", | ||
1238 | // END PUBLIC API | ||
1239 | |||
1240 | compile: function(environment, options, context, asObject) { | ||
1241 | this.environment = environment; | ||
1242 | this.options = options || {}; | ||
1243 | |||
1244 | log('debug', this.environment.disassemble() + "\n\n"); | ||
1245 | |||
1246 | this.name = this.environment.name; | ||
1247 | this.isChild = !!context; | ||
1248 | this.context = context || { | ||
1249 | programs: [], | ||
1250 | environments: [], | ||
1251 | aliases: { } | ||
1252 | }; | ||
1253 | |||
1254 | this.preamble(); | ||
1255 | |||
1256 | this.stackSlot = 0; | ||
1257 | this.stackVars = []; | ||
1258 | this.registers = { list: [] }; | ||
1259 | this.compileStack = []; | ||
1260 | this.inlineStack = []; | ||
1261 | |||
1262 | this.compileChildren(environment, options); | ||
1263 | |||
1264 | var opcodes = environment.opcodes, opcode; | ||
1265 | |||
1266 | this.i = 0; | ||
1267 | |||
1268 | for(var l=opcodes.length; this.i<l; this.i++) { | ||
1269 | opcode = opcodes[this.i]; | ||
1270 | |||
1271 | if(opcode.opcode === 'DECLARE') { | ||
1272 | this[opcode.name] = opcode.value; | ||
1273 | } else { | ||
1274 | this[opcode.opcode].apply(this, opcode.args); | ||
1275 | } | ||
1276 | |||
1277 | // Reset the stripNext flag if it was not set by this operation. | ||
1278 | if (opcode.opcode !== this.stripNext) { | ||
1279 | this.stripNext = false; | ||
1280 | } | ||
1281 | } | ||
1282 | |||
1283 | // Flush any trailing content that might be pending. | ||
1284 | this.pushSource(''); | ||
1285 | |||
1286 | return this.createFunctionContext(asObject); | ||
1287 | }, | ||
1288 | |||
1289 | preamble: function() { | ||
1290 | var out = []; | ||
1291 | |||
1292 | if (!this.isChild) { | ||
1293 | var namespace = this.namespace; | ||
1294 | |||
1295 | var copies = "helpers = this.merge(helpers, " + namespace + ".helpers);"; | ||
1296 | if (this.environment.usePartial) { copies = copies + " partials = this.merge(partials, " + namespace + ".partials);"; } | ||
1297 | if (this.options.data) { copies = copies + " data = data || {};"; } | ||
1298 | out.push(copies); | ||
1299 | } else { | ||
1300 | out.push(''); | ||
1301 | } | ||
1302 | |||
1303 | if (!this.environment.isSimple) { | ||
1304 | out.push(", buffer = " + this.initializeBuffer()); | ||
1305 | } else { | ||
1306 | out.push(""); | ||
1307 | } | ||
1308 | |||
1309 | // track the last context pushed into place to allow skipping the | ||
1310 | // getContext opcode when it would be a noop | ||
1311 | this.lastContext = 0; | ||
1312 | this.source = out; | ||
1313 | }, | ||
1314 | |||
1315 | createFunctionContext: function(asObject) { | ||
1316 | var locals = this.stackVars.concat(this.registers.list); | ||
1317 | |||
1318 | if(locals.length > 0) { | ||
1319 | this.source[1] = this.source[1] + ", " + locals.join(", "); | ||
1320 | } | ||
1321 | |||
1322 | // Generate minimizer alias mappings | ||
1323 | if (!this.isChild) { | ||
1324 | for (var alias in this.context.aliases) { | ||
1325 | if (this.context.aliases.hasOwnProperty(alias)) { | ||
1326 | this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; | ||
1327 | } | ||
1328 | } | ||
1329 | } | ||
1330 | |||
1331 | if (this.source[1]) { | ||
1332 | this.source[1] = "var " + this.source[1].substring(2) + ";"; | ||
1333 | } | ||
1334 | |||
1335 | // Merge children | ||
1336 | if (!this.isChild) { | ||
1337 | this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; | ||
1338 | } | ||
1339 | |||
1340 | if (!this.environment.isSimple) { | ||
1341 | this.pushSource("return buffer;"); | ||
1342 | } | ||
1343 | |||
1344 | var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; | ||
1345 | |||
1346 | for(var i=0, l=this.environment.depths.list.length; i<l; i++) { | ||
1347 | params.push("depth" + this.environment.depths.list[i]); | ||
1348 | } | ||
1349 | |||
1350 | // Perform a second pass over the output to merge content when possible | ||
1351 | var source = this.mergeSource(); | ||
1352 | |||
1353 | if (!this.isChild) { | ||
1354 | var revision = COMPILER_REVISION, | ||
1355 | versions = REVISION_CHANGES[revision]; | ||
1356 | source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source; | ||
1357 | } | ||
1358 | |||
1359 | if (asObject) { | ||
1360 | params.push(source); | ||
1361 | |||
1362 | return Function.apply(this, params); | ||
1363 | } else { | ||
1364 | var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}'; | ||
1365 | log('debug', functionSource + "\n\n"); | ||
1366 | return functionSource; | ||
1367 | } | ||
1368 | }, | ||
1369 | mergeSource: function() { | ||
1370 | // WARN: We are not handling the case where buffer is still populated as the source should | ||
1371 | // not have buffer append operations as their final action. | ||
1372 | var source = '', | ||
1373 | buffer; | ||
1374 | for (var i = 0, len = this.source.length; i < len; i++) { | ||
1375 | var line = this.source[i]; | ||
1376 | if (line.appendToBuffer) { | ||
1377 | if (buffer) { | ||
1378 | buffer = buffer + '\n + ' + line.content; | ||
1379 | } else { | ||
1380 | buffer = line.content; | ||
1381 | } | ||
1382 | } else { | ||
1383 | if (buffer) { | ||
1384 | source += 'buffer += ' + buffer + ';\n '; | ||
1385 | buffer = undefined; | ||
1386 | } | ||
1387 | source += line + '\n '; | ||
1388 | } | ||
1389 | } | ||
1390 | return source; | ||
1391 | }, | ||
1392 | |||
1393 | // [blockValue] | ||
1394 | // | ||
1395 | // On stack, before: hash, inverse, program, value | ||
1396 | // On stack, after: return value of blockHelperMissing | ||
1397 | // | ||
1398 | // The purpose of this opcode is to take a block of the form | ||
1399 | // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and | ||
1400 | // replace it on the stack with the result of properly | ||
1401 | // invoking blockHelperMissing. | ||
1402 | blockValue: function() { | ||
1403 | this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; | ||
1404 | |||
1405 | var params = ["depth0"]; | ||
1406 | this.setupParams(0, params); | ||
1407 | |||
1408 | this.replaceStack(function(current) { | ||
1409 | params.splice(1, 0, current); | ||
1410 | return "blockHelperMissing.call(" + params.join(", ") + ")"; | ||
1411 | }); | ||
1412 | }, | ||
1413 | |||
1414 | // [ambiguousBlockValue] | ||
1415 | // | ||
1416 | // On stack, before: hash, inverse, program, value | ||
1417 | // Compiler value, before: lastHelper=value of last found helper, if any | ||
1418 | // On stack, after, if no lastHelper: same as [blockValue] | ||
1419 | // On stack, after, if lastHelper: value | ||
1420 | ambiguousBlockValue: function() { | ||
1421 | this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; | ||
1422 | |||
1423 | var params = ["depth0"]; | ||
1424 | this.setupParams(0, params); | ||
1425 | |||
1426 | var current = this.topStack(); | ||
1427 | params.splice(1, 0, current); | ||
1428 | |||
1429 | // Use the options value generated from the invocation | ||
1430 | params[params.length-1] = 'options'; | ||
1431 | |||
1432 | this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }"); | ||
1433 | }, | ||
1434 | |||
1435 | // [appendContent] | ||
1436 | // | ||
1437 | // On stack, before: ... | ||
1438 | // On stack, after: ... | ||
1439 | // | ||
1440 | // Appends the string value of `content` to the current buffer | ||
1441 | appendContent: function(content) { | ||
1442 | if (this.pendingContent) { | ||
1443 | content = this.pendingContent + content; | ||
1444 | } | ||
1445 | if (this.stripNext) { | ||
1446 | content = content.replace(/^\s+/, ''); | ||
1447 | } | ||
1448 | |||
1449 | this.pendingContent = content; | ||
1450 | }, | ||
1451 | |||
1452 | // [strip] | ||
1453 | // | ||
1454 | // On stack, before: ... | ||
1455 | // On stack, after: ... | ||
1456 | // | ||
1457 | // Removes any trailing whitespace from the prior content node and flags | ||
1458 | // the next operation for stripping if it is a content node. | ||
1459 | strip: function() { | ||
1460 | if (this.pendingContent) { | ||
1461 | this.pendingContent = this.pendingContent.replace(/\s+$/, ''); | ||
1462 | } | ||
1463 | this.stripNext = 'strip'; | ||
1464 | }, | ||
1465 | |||
1466 | // [append] | ||
1467 | // | ||
1468 | // On stack, before: value, ... | ||
1469 | // On stack, after: ... | ||
1470 | // | ||
1471 | // Coerces `value` to a String and appends it to the current buffer. | ||
1472 | // | ||
1473 | // If `value` is truthy, or 0, it is coerced into a string and appended | ||
1474 | // Otherwise, the empty string is appended | ||
1475 | append: function() { | ||
1476 | // Force anything that is inlined onto the stack so we don't have duplication | ||
1477 | // when we examine local | ||
1478 | this.flushInline(); | ||
1479 | var local = this.popStack(); | ||
1480 | this.pushSource("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }"); | ||
1481 | if (this.environment.isSimple) { | ||
1482 | this.pushSource("else { " + this.appendToBuffer("''") + " }"); | ||
1483 | } | ||
1484 | }, | ||
1485 | |||
1486 | // [appendEscaped] | ||
1487 | // | ||
1488 | // On stack, before: value, ... | ||
1489 | // On stack, after: ... | ||
1490 | // | ||
1491 | // Escape `value` and append it to the buffer | ||
1492 | appendEscaped: function() { | ||
1493 | this.context.aliases.escapeExpression = 'this.escapeExpression'; | ||
1494 | |||
1495 | this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")")); | ||
1496 | }, | ||
1497 | |||
1498 | // [getContext] | ||
1499 | // | ||
1500 | // On stack, before: ... | ||
1501 | // On stack, after: ... | ||
1502 | // Compiler value, after: lastContext=depth | ||
1503 | // | ||
1504 | // Set the value of the `lastContext` compiler value to the depth | ||
1505 | getContext: function(depth) { | ||
1506 | if(this.lastContext !== depth) { | ||
1507 | this.lastContext = depth; | ||
1508 | } | ||
1509 | }, | ||
1510 | |||
1511 | // [lookupOnContext] | ||
1512 | // | ||
1513 | // On stack, before: ... | ||
1514 | // On stack, after: currentContext[name], ... | ||
1515 | // | ||
1516 | // Looks up the value of `name` on the current context and pushes | ||
1517 | // it onto the stack. | ||
1518 | lookupOnContext: function(name) { | ||
1519 | this.push(this.nameLookup('depth' + this.lastContext, name, 'context')); | ||
1520 | }, | ||
1521 | |||
1522 | // [pushContext] | ||
1523 | // | ||
1524 | // On stack, before: ... | ||
1525 | // On stack, after: currentContext, ... | ||
1526 | // | ||
1527 | // Pushes the value of the current context onto the stack. | ||
1528 | pushContext: function() { | ||
1529 | this.pushStackLiteral('depth' + this.lastContext); | ||
1530 | }, | ||
1531 | |||
1532 | // [resolvePossibleLambda] | ||
1533 | // | ||
1534 | // On stack, before: value, ... | ||
1535 | // On stack, after: resolved value, ... | ||
1536 | // | ||
1537 | // If the `value` is a lambda, replace it on the stack by | ||
1538 | // the return value of the lambda | ||
1539 | resolvePossibleLambda: function() { | ||
1540 | this.context.aliases.functionType = '"function"'; | ||
1541 | |||
1542 | this.replaceStack(function(current) { | ||
1543 | return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current; | ||
1544 | }); | ||
1545 | }, | ||
1546 | |||
1547 | // [lookup] | ||
1548 | // | ||
1549 | // On stack, before: value, ... | ||
1550 | // On stack, after: value[name], ... | ||
1551 | // | ||
1552 | // Replace the value on the stack with the result of looking | ||
1553 | // up `name` on `value` | ||
1554 | lookup: function(name) { | ||
1555 | this.replaceStack(function(current) { | ||
1556 | return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context'); | ||
1557 | }); | ||
1558 | }, | ||
1559 | |||
1560 | // [lookupData] | ||
1561 | // | ||
1562 | // On stack, before: ... | ||
1563 | // On stack, after: data, ... | ||
1564 | // | ||
1565 | // Push the data lookup operator | ||
1566 | lookupData: function() { | ||
1567 | this.push('data'); | ||
1568 | }, | ||
1569 | |||
1570 | // [pushStringParam] | ||
1571 | // | ||
1572 | // On stack, before: ... | ||
1573 | // On stack, after: string, currentContext, ... | ||
1574 | // | ||
1575 | // This opcode is designed for use in string mode, which | ||
1576 | // provides the string value of a parameter along with its | ||
1577 | // depth rather than resolving it immediately. | ||
1578 | pushStringParam: function(string, type) { | ||
1579 | this.pushStackLiteral('depth' + this.lastContext); | ||
1580 | |||
1581 | this.pushString(type); | ||
1582 | |||
1583 | if (typeof string === 'string') { | ||
1584 | this.pushString(string); | ||
1585 | } else { | ||
1586 | this.pushStackLiteral(string); | ||
1587 | } | ||
1588 | }, | ||
1589 | |||
1590 | emptyHash: function() { | ||
1591 | this.pushStackLiteral('{}'); | ||
1592 | |||
1593 | if (this.options.stringParams) { | ||
1594 | this.register('hashTypes', '{}'); | ||
1595 | this.register('hashContexts', '{}'); | ||
1596 | } | ||
1597 | }, | ||
1598 | pushHash: function() { | ||
1599 | this.hash = {values: [], types: [], contexts: []}; | ||
1600 | }, | ||
1601 | popHash: function() { | ||
1602 | var hash = this.hash; | ||
1603 | this.hash = undefined; | ||
1604 | |||
1605 | if (this.options.stringParams) { | ||
1606 | this.register('hashContexts', '{' + hash.contexts.join(',') + '}'); | ||
1607 | this.register('hashTypes', '{' + hash.types.join(',') + '}'); | ||
1608 | } | ||
1609 | this.push('{\n ' + hash.values.join(',\n ') + '\n }'); | ||
1610 | }, | ||
1611 | |||
1612 | // [pushString] | ||
1613 | // | ||
1614 | // On stack, before: ... | ||
1615 | // On stack, after: quotedString(string), ... | ||
1616 | // | ||
1617 | // Push a quoted version of `string` onto the stack | ||
1618 | pushString: function(string) { | ||
1619 | this.pushStackLiteral(this.quotedString(string)); | ||
1620 | }, | ||
1621 | |||
1622 | // [push] | ||
1623 | // | ||
1624 | // On stack, before: ... | ||
1625 | // On stack, after: expr, ... | ||
1626 | // | ||
1627 | // Push an expression onto the stack | ||
1628 | push: function(expr) { | ||
1629 | this.inlineStack.push(expr); | ||
1630 | return expr; | ||
1631 | }, | ||
1632 | |||
1633 | // [pushLiteral] | ||
1634 | // | ||
1635 | // On stack, before: ... | ||
1636 | // On stack, after: value, ... | ||
1637 | // | ||
1638 | // Pushes a value onto the stack. This operation prevents | ||
1639 | // the compiler from creating a temporary variable to hold | ||
1640 | // it. | ||
1641 | pushLiteral: function(value) { | ||
1642 | this.pushStackLiteral(value); | ||
1643 | }, | ||
1644 | |||
1645 | // [pushProgram] | ||
1646 | // | ||
1647 | // On stack, before: ... | ||
1648 | // On stack, after: program(guid), ... | ||
1649 | // | ||
1650 | // Push a program expression onto the stack. This takes | ||
1651 | // a compile-time guid and converts it into a runtime-accessible | ||
1652 | // expression. | ||
1653 | pushProgram: function(guid) { | ||
1654 | if (guid != null) { | ||
1655 | this.pushStackLiteral(this.programExpression(guid)); | ||
1656 | } else { | ||
1657 | this.pushStackLiteral(null); | ||
1658 | } | ||
1659 | }, | ||
1660 | |||
1661 | // [invokeHelper] | ||
1662 | // | ||
1663 | // On stack, before: hash, inverse, program, params..., ... | ||
1664 | // On stack, after: result of helper invocation | ||
1665 | // | ||
1666 | // Pops off the helper's parameters, invokes the helper, | ||
1667 | // and pushes the helper's return value onto the stack. | ||
1668 | // | ||
1669 | // If the helper is not found, `helperMissing` is called. | ||
1670 | invokeHelper: function(paramSize, name) { | ||
1671 | this.context.aliases.helperMissing = 'helpers.helperMissing'; | ||
1672 | |||
1673 | var helper = this.lastHelper = this.setupHelper(paramSize, name, true); | ||
1674 | var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context'); | ||
1675 | |||
1676 | this.push(helper.name + ' || ' + nonHelper); | ||
1677 | this.replaceStack(function(name) { | ||
1678 | return name + ' ? ' + name + '.call(' + | ||
1679 | helper.callParams + ") " + ": helperMissing.call(" + | ||
1680 | helper.helperMissingParams + ")"; | ||
1681 | }); | ||
1682 | }, | ||
1683 | |||
1684 | // [invokeKnownHelper] | ||
1685 | // | ||
1686 | // On stack, before: hash, inverse, program, params..., ... | ||
1687 | // On stack, after: result of helper invocation | ||
1688 | // | ||
1689 | // This operation is used when the helper is known to exist, | ||
1690 | // so a `helperMissing` fallback is not required. | ||
1691 | invokeKnownHelper: function(paramSize, name) { | ||
1692 | var helper = this.setupHelper(paramSize, name); | ||
1693 | this.push(helper.name + ".call(" + helper.callParams + ")"); | ||
1694 | }, | ||
1695 | |||
1696 | // [invokeAmbiguous] | ||
1697 | // | ||
1698 | // On stack, before: hash, inverse, program, params..., ... | ||
1699 | // On stack, after: result of disambiguation | ||
1700 | // | ||
1701 | // This operation is used when an expression like `{{foo}}` | ||
1702 | // is provided, but we don't know at compile-time whether it | ||
1703 | // is a helper or a path. | ||
1704 | // | ||
1705 | // This operation emits more code than the other options, | ||
1706 | // and can be avoided by passing the `knownHelpers` and | ||
1707 | // `knownHelpersOnly` flags at compile-time. | ||
1708 | invokeAmbiguous: function(name, helperCall) { | ||
1709 | this.context.aliases.functionType = '"function"'; | ||
1710 | |||
1711 | this.pushStackLiteral('{}'); // Hash value | ||
1712 | var helper = this.setupHelper(0, name, helperCall); | ||
1713 | |||
1714 | var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); | ||
1715 | |||
1716 | var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context'); | ||
1717 | var nextStack = this.nextStack(); | ||
1718 | |||
1719 | this.pushSource('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }'); | ||
1720 | this.pushSource('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.call(' + helper.callParams + ') : ' + nextStack + '; }'); | ||
1721 | }, | ||
1722 | |||
1723 | // [invokePartial] | ||
1724 | // | ||
1725 | // On stack, before: context, ... | ||
1726 | // On stack after: result of partial invocation | ||
1727 | // | ||
1728 | // This operation pops off a context, invokes a partial with that context, | ||
1729 | // and pushes the result of the invocation back. | ||
1730 | invokePartial: function(name) { | ||
1731 | var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"]; | ||
1732 | |||
1733 | if (this.options.data) { | ||
1734 | params.push("data"); | ||
1735 | } | ||
1736 | |||
1737 | this.context.aliases.self = "this"; | ||
1738 | this.push("self.invokePartial(" + params.join(", ") + ")"); | ||
1739 | }, | ||
1740 | |||
1741 | // [assignToHash] | ||
1742 | // | ||
1743 | // On stack, before: value, hash, ... | ||
1744 | // On stack, after: hash, ... | ||
1745 | // | ||
1746 | // Pops a value and hash off the stack, assigns `hash[key] = value` | ||
1747 | // and pushes the hash back onto the stack. | ||
1748 | assignToHash: function(key) { | ||
1749 | var value = this.popStack(), | ||
1750 | context, | ||
1751 | type; | ||
1752 | |||
1753 | if (this.options.stringParams) { | ||
1754 | type = this.popStack(); | ||
1755 | context = this.popStack(); | ||
1756 | } | ||
1757 | |||
1758 | var hash = this.hash; | ||
1759 | if (context) { | ||
1760 | hash.contexts.push("'" + key + "': " + context); | ||
1761 | } | ||
1762 | if (type) { | ||
1763 | hash.types.push("'" + key + "': " + type); | ||
1764 | } | ||
1765 | hash.values.push("'" + key + "': (" + value + ")"); | ||
1766 | }, | ||
1767 | |||
1768 | // HELPERS | ||
1769 | |||
1770 | compiler: JavaScriptCompiler, | ||
1771 | |||
1772 | compileChildren: function(environment, options) { | ||
1773 | var children = environment.children, child, compiler; | ||
1774 | |||
1775 | for(var i=0, l=children.length; i<l; i++) { | ||
1776 | child = children[i]; | ||
1777 | compiler = new this.compiler(); | ||
1778 | |||
1779 | var index = this.matchExistingProgram(child); | ||
1780 | |||
1781 | if (index == null) { | ||
1782 | this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children | ||
1783 | index = this.context.programs.length; | ||
1784 | child.index = index; | ||
1785 | child.name = 'program' + index; | ||
1786 | this.context.programs[index] = compiler.compile(child, options, this.context); | ||
1787 | this.context.environments[index] = child; | ||
1788 | } else { | ||
1789 | child.index = index; | ||
1790 | child.name = 'program' + index; | ||
1791 | } | ||
1792 | } | ||
1793 | }, | ||
1794 | matchExistingProgram: function(child) { | ||
1795 | for (var i = 0, len = this.context.environments.length; i < len; i++) { | ||
1796 | var environment = this.context.environments[i]; | ||
1797 | if (environment && environment.equals(child)) { | ||
1798 | return i; | ||
1799 | } | ||
1800 | } | ||
1801 | }, | ||
1802 | |||
1803 | programExpression: function(guid) { | ||
1804 | this.context.aliases.self = "this"; | ||
1805 | |||
1806 | if(guid == null) { | ||
1807 | return "self.noop"; | ||
1808 | } | ||
1809 | |||
1810 | var child = this.environment.children[guid], | ||
1811 | depths = child.depths.list, depth; | ||
1812 | |||
1813 | var programParams = [child.index, child.name, "data"]; | ||
1814 | |||
1815 | for(var i=0, l = depths.length; i<l; i++) { | ||
1816 | depth = depths[i]; | ||
1817 | |||
1818 | if(depth === 1) { programParams.push("depth0"); } | ||
1819 | else { programParams.push("depth" + (depth - 1)); } | ||
1820 | } | ||
1821 | |||
1822 | return (depths.length === 0 ? "self.program(" : "self.programWithDepth(") + programParams.join(", ") + ")"; | ||
1823 | }, | ||
1824 | |||
1825 | register: function(name, val) { | ||
1826 | this.useRegister(name); | ||
1827 | this.pushSource(name + " = " + val + ";"); | ||
1828 | }, | ||
1829 | |||
1830 | useRegister: function(name) { | ||
1831 | if(!this.registers[name]) { | ||
1832 | this.registers[name] = true; | ||
1833 | this.registers.list.push(name); | ||
1834 | } | ||
1835 | }, | ||
1836 | |||
1837 | pushStackLiteral: function(item) { | ||
1838 | return this.push(new Literal(item)); | ||
1839 | }, | ||
1840 | |||
1841 | pushSource: function(source) { | ||
1842 | if (this.pendingContent) { | ||
1843 | this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent))); | ||
1844 | this.pendingContent = undefined; | ||
1845 | } | ||
1846 | |||
1847 | if (source) { | ||
1848 | this.source.push(source); | ||
1849 | } | ||
1850 | }, | ||
1851 | |||
1852 | pushStack: function(item) { | ||
1853 | this.flushInline(); | ||
1854 | |||
1855 | var stack = this.incrStack(); | ||
1856 | if (item) { | ||
1857 | this.pushSource(stack + " = " + item + ";"); | ||
1858 | } | ||
1859 | this.compileStack.push(stack); | ||
1860 | return stack; | ||
1861 | }, | ||
1862 | |||
1863 | replaceStack: function(callback) { | ||
1864 | var prefix = '', | ||
1865 | inline = this.isInline(), | ||
1866 | stack; | ||
1867 | |||
1868 | // If we are currently inline then we want to merge the inline statement into the | ||
1869 | // replacement statement via ',' | ||
1870 | if (inline) { | ||
1871 | var top = this.popStack(true); | ||
1872 | |||
1873 | if (top instanceof Literal) { | ||
1874 | // Literals do not need to be inlined | ||
1875 | stack = top.value; | ||
1876 | } else { | ||
1877 | // Get or create the current stack name for use by the inline | ||
1878 | var name = this.stackSlot ? this.topStackName() : this.incrStack(); | ||
1879 | |||
1880 | prefix = '(' + this.push(name) + ' = ' + top + '),'; | ||
1881 | stack = this.topStack(); | ||
1882 | } | ||
1883 | } else { | ||
1884 | stack = this.topStack(); | ||
1885 | } | ||
1886 | |||
1887 | var item = callback.call(this, stack); | ||
1888 | |||
1889 | if (inline) { | ||
1890 | if (this.inlineStack.length || this.compileStack.length) { | ||
1891 | this.popStack(); | ||
1892 | } | ||
1893 | this.push('(' + prefix + item + ')'); | ||
1894 | } else { | ||
1895 | // Prevent modification of the context depth variable. Through replaceStack | ||
1896 | if (!/^stack/.test(stack)) { | ||
1897 | stack = this.nextStack(); | ||
1898 | } | ||
1899 | |||
1900 | this.pushSource(stack + " = (" + prefix + item + ");"); | ||
1901 | } | ||
1902 | return stack; | ||
1903 | }, | ||
1904 | |||
1905 | nextStack: function() { | ||
1906 | return this.pushStack(); | ||
1907 | }, | ||
1908 | |||
1909 | incrStack: function() { | ||
1910 | this.stackSlot++; | ||
1911 | if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } | ||
1912 | return this.topStackName(); | ||
1913 | }, | ||
1914 | topStackName: function() { | ||
1915 | return "stack" + this.stackSlot; | ||
1916 | }, | ||
1917 | flushInline: function() { | ||
1918 | var inlineStack = this.inlineStack; | ||
1919 | if (inlineStack.length) { | ||
1920 | this.inlineStack = []; | ||
1921 | for (var i = 0, len = inlineStack.length; i < len; i++) { | ||
1922 | var entry = inlineStack[i]; | ||
1923 | if (entry instanceof Literal) { | ||
1924 | this.compileStack.push(entry); | ||
1925 | } else { | ||
1926 | this.pushStack(entry); | ||
1927 | } | ||
1928 | } | ||
1929 | } | ||
1930 | }, | ||
1931 | isInline: function() { | ||
1932 | return this.inlineStack.length; | ||
1933 | }, | ||
1934 | |||
1935 | popStack: function(wrapped) { | ||
1936 | var inline = this.isInline(), | ||
1937 | item = (inline ? this.inlineStack : this.compileStack).pop(); | ||
1938 | |||
1939 | if (!wrapped && (item instanceof Literal)) { | ||
1940 | return item.value; | ||
1941 | } else { | ||
1942 | if (!inline) { | ||
1943 | this.stackSlot--; | ||
1944 | } | ||
1945 | return item; | ||
1946 | } | ||
1947 | }, | ||
1948 | |||
1949 | topStack: function(wrapped) { | ||
1950 | var stack = (this.isInline() ? this.inlineStack : this.compileStack), | ||
1951 | item = stack[stack.length - 1]; | ||
1952 | |||
1953 | if (!wrapped && (item instanceof Literal)) { | ||
1954 | return item.value; | ||
1955 | } else { | ||
1956 | return item; | ||
1957 | } | ||
1958 | }, | ||
1959 | |||
1960 | quotedString: function(str) { | ||
1961 | return '"' + str | ||
1962 | .replace(/\\/g, '\\\\') | ||
1963 | .replace(/"/g, '\\"') | ||
1964 | .replace(/\n/g, '\\n') | ||
1965 | .replace(/\r/g, '\\r') | ||
1966 | .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 | ||
1967 | .replace(/\u2029/g, '\\u2029') + '"'; | ||
1968 | }, | ||
1969 | |||
1970 | setupHelper: function(paramSize, name, missingParams) { | ||
1971 | var params = []; | ||
1972 | this.setupParams(paramSize, params, missingParams); | ||
1973 | var foundHelper = this.nameLookup('helpers', name, 'helper'); | ||
1974 | |||
1975 | return { | ||
1976 | params: params, | ||
1977 | name: foundHelper, | ||
1978 | callParams: ["depth0"].concat(params).join(", "), | ||
1979 | helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") | ||
1980 | }; | ||
1981 | }, | ||
1982 | |||
1983 | // the params and contexts arguments are passed in arrays | ||
1984 | // to fill in | ||
1985 | setupParams: function(paramSize, params, useRegister) { | ||
1986 | var options = [], contexts = [], types = [], param, inverse, program; | ||
1987 | |||
1988 | options.push("hash:" + this.popStack()); | ||
1989 | |||
1990 | inverse = this.popStack(); | ||
1991 | program = this.popStack(); | ||
1992 | |||
1993 | // Avoid setting fn and inverse if neither are set. This allows | ||
1994 | // helpers to do a check for `if (options.fn)` | ||
1995 | if (program || inverse) { | ||
1996 | if (!program) { | ||
1997 | this.context.aliases.self = "this"; | ||
1998 | program = "self.noop"; | ||
1999 | } | ||
2000 | |||
2001 | if (!inverse) { | ||
2002 | this.context.aliases.self = "this"; | ||
2003 | inverse = "self.noop"; | ||
2004 | } | ||
2005 | |||
2006 | options.push("inverse:" + inverse); | ||
2007 | options.push("fn:" + program); | ||
2008 | } | ||
2009 | |||
2010 | for(var i=0; i<paramSize; i++) { | ||
2011 | param = this.popStack(); | ||
2012 | params.push(param); | ||
2013 | |||
2014 | if(this.options.stringParams) { | ||
2015 | types.push(this.popStack()); | ||
2016 | contexts.push(this.popStack()); | ||
2017 | } | ||
2018 | } | ||
2019 | |||
2020 | if (this.options.stringParams) { | ||
2021 | options.push("contexts:[" + contexts.join(",") + "]"); | ||
2022 | options.push("types:[" + types.join(",") + "]"); | ||
2023 | options.push("hashContexts:hashContexts"); | ||
2024 | options.push("hashTypes:hashTypes"); | ||
2025 | } | ||
2026 | |||
2027 | if(this.options.data) { | ||
2028 | options.push("data:data"); | ||
2029 | } | ||
2030 | |||
2031 | options = "{" + options.join(",") + "}"; | ||
2032 | if (useRegister) { | ||
2033 | this.register('options', options); | ||
2034 | params.push('options'); | ||
2035 | } else { | ||
2036 | params.push(options); | ||
2037 | } | ||
2038 | return params.join(", "); | ||
2039 | } | ||
2040 | }; | ||
2041 | |||
2042 | var reservedWords = ( | ||
2043 | "break else new var" + | ||
2044 | " case finally return void" + | ||
2045 | " catch for switch while" + | ||
2046 | " continue function this with" + | ||
2047 | " default if throw" + | ||
2048 | " delete in try" + | ||
2049 | " do instanceof typeof" + | ||
2050 | " abstract enum int short" + | ||
2051 | " boolean export interface static" + | ||
2052 | " byte extends long super" + | ||
2053 | " char final native synchronized" + | ||
2054 | " class float package throws" + | ||
2055 | " const goto private transient" + | ||
2056 | " debugger implements protected volatile" + | ||
2057 | " double import public let yield" | ||
2058 | ).split(" "); | ||
2059 | |||
2060 | var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; | ||
2061 | |||
2062 | for(var i=0, l=reservedWords.length; i<l; i++) { | ||
2063 | compilerWords[reservedWords[i]] = true; | ||
2064 | } | ||
2065 | |||
2066 | JavaScriptCompiler.isValidJavaScriptVariableName = function(name) { | ||
2067 | if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) { | ||
2068 | return true; | ||
2069 | } | ||
2070 | return false; | ||
2071 | }; | ||
2072 | |||
2073 | __exports__ = JavaScriptCompiler; | ||
2074 | return __exports__; | ||
2075 | })(__module2__); | ||
2076 | |||
2077 | // handlebars/compiler/compiler.js | ||
2078 | var __module10__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { | ||
2079 | "use strict"; | ||
2080 | var __exports__ = {}; | ||
2081 | var Exception = __dependency1__; | ||
2082 | var parse = __dependency2__.parse; | ||
2083 | var JavaScriptCompiler = __dependency3__; | ||
2084 | var AST = __dependency4__; | ||
2085 | |||
2086 | function Compiler() {} | ||
2087 | |||
2088 | __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a | ||
2089 | // function in a context. This is necessary for mustache compatibility, which | ||
2090 | // requires that context functions in blocks are evaluated by blockHelperMissing, | ||
2091 | // and then proceed as if the resulting value was provided to blockHelperMissing. | ||
2092 | |||
2093 | Compiler.prototype = { | ||
2094 | compiler: Compiler, | ||
2095 | |||
2096 | disassemble: function() { | ||
2097 | var opcodes = this.opcodes, opcode, out = [], params, param; | ||
2098 | |||
2099 | for (var i=0, l=opcodes.length; i<l; i++) { | ||
2100 | opcode = opcodes[i]; | ||
2101 | |||
2102 | if (opcode.opcode === 'DECLARE') { | ||
2103 | out.push("DECLARE " + opcode.name + "=" + opcode.value); | ||
2104 | } else { | ||
2105 | params = []; | ||
2106 | for (var j=0; j<opcode.args.length; j++) { | ||
2107 | param = opcode.args[j]; | ||
2108 | if (typeof param === "string") { | ||
2109 | param = "\"" + param.replace("\n", "\\n") + "\""; | ||
2110 | } | ||
2111 | params.push(param); | ||
2112 | } | ||
2113 | out.push(opcode.opcode + " " + params.join(" ")); | ||
2114 | } | ||
2115 | } | ||
2116 | |||
2117 | return out.join("\n"); | ||
2118 | }, | ||
2119 | |||
2120 | equals: function(other) { | ||
2121 | var len = this.opcodes.length; | ||
2122 | if (other.opcodes.length !== len) { | ||
2123 | return false; | ||
2124 | } | ||
2125 | |||
2126 | for (var i = 0; i < len; i++) { | ||
2127 | var opcode = this.opcodes[i], | ||
2128 | otherOpcode = other.opcodes[i]; | ||
2129 | if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) { | ||
2130 | return false; | ||
2131 | } | ||
2132 | for (var j = 0; j < opcode.args.length; j++) { | ||
2133 | if (opcode.args[j] !== otherOpcode.args[j]) { | ||
2134 | return false; | ||
2135 | } | ||
2136 | } | ||
2137 | } | ||
2138 | |||
2139 | len = this.children.length; | ||
2140 | if (other.children.length !== len) { | ||
2141 | return false; | ||
2142 | } | ||
2143 | for (i = 0; i < len; i++) { | ||
2144 | if (!this.children[i].equals(other.children[i])) { | ||
2145 | return false; | ||
2146 | } | ||
2147 | } | ||
2148 | |||
2149 | return true; | ||
2150 | }, | ||
2151 | |||
2152 | guid: 0, | ||
2153 | |||
2154 | compile: function(program, options) { | ||
2155 | this.opcodes = []; | ||
2156 | this.children = []; | ||
2157 | this.depths = {list: []}; | ||
2158 | this.options = options; | ||
2159 | |||
2160 | // These changes will propagate to the other compiler components | ||
2161 | var knownHelpers = this.options.knownHelpers; | ||
2162 | this.options.knownHelpers = { | ||
2163 | 'helperMissing': true, | ||
2164 | 'blockHelperMissing': true, | ||
2165 | 'each': true, | ||
2166 | 'if': true, | ||
2167 | 'unless': true, | ||
2168 | 'with': true, | ||
2169 | 'log': true | ||
2170 | }; | ||
2171 | if (knownHelpers) { | ||
2172 | for (var name in knownHelpers) { | ||
2173 | this.options.knownHelpers[name] = knownHelpers[name]; | ||
2174 | } | ||
2175 | } | ||
2176 | |||
2177 | return this.accept(program); | ||
2178 | }, | ||
2179 | |||
2180 | accept: function(node) { | ||
2181 | var strip = node.strip || {}, | ||
2182 | ret; | ||
2183 | if (strip.left) { | ||
2184 | this.opcode('strip'); | ||
2185 | } | ||
2186 | |||
2187 | ret = this[node.type](node); | ||
2188 | |||
2189 | if (strip.right) { | ||
2190 | this.opcode('strip'); | ||
2191 | } | ||
2192 | |||
2193 | return ret; | ||
2194 | }, | ||
2195 | |||
2196 | program: function(program) { | ||
2197 | var statements = program.statements; | ||
2198 | |||
2199 | for(var i=0, l=statements.length; i<l; i++) { | ||
2200 | this.accept(statements[i]); | ||
2201 | } | ||
2202 | this.isSimple = l === 1; | ||
2203 | |||
2204 | this.depths.list = this.depths.list.sort(function(a, b) { | ||
2205 | return a - b; | ||
2206 | }); | ||
2207 | |||
2208 | return this; | ||
2209 | }, | ||
2210 | |||
2211 | compileProgram: function(program) { | ||
2212 | var result = new this.compiler().compile(program, this.options); | ||
2213 | var guid = this.guid++, depth; | ||
2214 | |||
2215 | this.usePartial = this.usePartial || result.usePartial; | ||
2216 | |||
2217 | this.children[guid] = result; | ||
2218 | |||
2219 | for(var i=0, l=result.depths.list.length; i<l; i++) { | ||
2220 | depth = result.depths.list[i]; | ||
2221 | |||
2222 | if(depth < 2) { continue; } | ||
2223 | else { this.addDepth(depth - 1); } | ||
2224 | } | ||
2225 | |||
2226 | return guid; | ||
2227 | }, | ||
2228 | |||
2229 | block: function(block) { | ||
2230 | var mustache = block.mustache, | ||
2231 | program = block.program, | ||
2232 | inverse = block.inverse; | ||
2233 | |||
2234 | if (program) { | ||
2235 | program = this.compileProgram(program); | ||
2236 | } | ||
2237 | |||
2238 | if (inverse) { | ||
2239 | inverse = this.compileProgram(inverse); | ||
2240 | } | ||
2241 | |||
2242 | var type = this.classifyMustache(mustache); | ||
2243 | |||
2244 | if (type === "helper") { | ||
2245 | this.helperMustache(mustache, program, inverse); | ||
2246 | } else if (type === "simple") { | ||
2247 | this.simpleMustache(mustache); | ||
2248 | |||
2249 | // now that the simple mustache is resolved, we need to | ||
2250 | // evaluate it by executing `blockHelperMissing` | ||
2251 | this.opcode('pushProgram', program); | ||
2252 | this.opcode('pushProgram', inverse); | ||
2253 | this.opcode('emptyHash'); | ||
2254 | this.opcode('blockValue'); | ||
2255 | } else { | ||
2256 | this.ambiguousMustache(mustache, program, inverse); | ||
2257 | |||
2258 | // now that the simple mustache is resolved, we need to | ||
2259 | // evaluate it by executing `blockHelperMissing` | ||
2260 | this.opcode('pushProgram', program); | ||
2261 | this.opcode('pushProgram', inverse); | ||
2262 | this.opcode('emptyHash'); | ||
2263 | this.opcode('ambiguousBlockValue'); | ||
2264 | } | ||
2265 | |||
2266 | this.opcode('append'); | ||
2267 | }, | ||
2268 | |||
2269 | hash: function(hash) { | ||
2270 | var pairs = hash.pairs, pair, val; | ||
2271 | |||
2272 | this.opcode('pushHash'); | ||
2273 | |||
2274 | for(var i=0, l=pairs.length; i<l; i++) { | ||
2275 | pair = pairs[i]; | ||
2276 | val = pair[1]; | ||
2277 | |||
2278 | if (this.options.stringParams) { | ||
2279 | if(val.depth) { | ||
2280 | this.addDepth(val.depth); | ||
2281 | } | ||
2282 | this.opcode('getContext', val.depth || 0); | ||
2283 | this.opcode('pushStringParam', val.stringModeValue, val.type); | ||
2284 | } else { | ||
2285 | this.accept(val); | ||
2286 | } | ||
2287 | |||
2288 | this.opcode('assignToHash', pair[0]); | ||
2289 | } | ||
2290 | this.opcode('popHash'); | ||
2291 | }, | ||
2292 | |||
2293 | partial: function(partial) { | ||
2294 | var partialName = partial.partialName; | ||
2295 | this.usePartial = true; | ||
2296 | |||
2297 | if(partial.context) { | ||
2298 | this.ID(partial.context); | ||
2299 | } else { | ||
2300 | this.opcode('push', 'depth0'); | ||
2301 | } | ||
2302 | |||
2303 | this.opcode('invokePartial', partialName.name); | ||
2304 | this.opcode('append'); | ||
2305 | }, | ||
2306 | |||
2307 | content: function(content) { | ||
2308 | this.opcode('appendContent', content.string); | ||
2309 | }, | ||
2310 | |||
2311 | mustache: function(mustache) { | ||
2312 | var options = this.options; | ||
2313 | var type = this.classifyMustache(mustache); | ||
2314 | |||
2315 | if (type === "simple") { | ||
2316 | this.simpleMustache(mustache); | ||
2317 | } else if (type === "helper") { | ||
2318 | this.helperMustache(mustache); | ||
2319 | } else { | ||
2320 | this.ambiguousMustache(mustache); | ||
2321 | } | ||
2322 | |||
2323 | if(mustache.escaped && !options.noEscape) { | ||
2324 | this.opcode('appendEscaped'); | ||
2325 | } else { | ||
2326 | this.opcode('append'); | ||
2327 | } | ||
2328 | }, | ||
2329 | |||
2330 | ambiguousMustache: function(mustache, program, inverse) { | ||
2331 | var id = mustache.id, | ||
2332 | name = id.parts[0], | ||
2333 | isBlock = program != null || inverse != null; | ||
2334 | |||
2335 | this.opcode('getContext', id.depth); | ||
2336 | |||
2337 | this.opcode('pushProgram', program); | ||
2338 | this.opcode('pushProgram', inverse); | ||
2339 | |||
2340 | this.opcode('invokeAmbiguous', name, isBlock); | ||
2341 | }, | ||
2342 | |||
2343 | simpleMustache: function(mustache) { | ||
2344 | var id = mustache.id; | ||
2345 | |||
2346 | if (id.type === 'DATA') { | ||
2347 | this.DATA(id); | ||
2348 | } else if (id.parts.length) { | ||
2349 | this.ID(id); | ||
2350 | } else { | ||
2351 | // Simplified ID for `this` | ||
2352 | this.addDepth(id.depth); | ||
2353 | this.opcode('getContext', id.depth); | ||
2354 | this.opcode('pushContext'); | ||
2355 | } | ||
2356 | |||
2357 | this.opcode('resolvePossibleLambda'); | ||
2358 | }, | ||
2359 | |||
2360 | helperMustache: function(mustache, program, inverse) { | ||
2361 | var params = this.setupFullMustacheParams(mustache, program, inverse), | ||
2362 | name = mustache.id.parts[0]; | ||
2363 | |||
2364 | if (this.options.knownHelpers[name]) { | ||
2365 | this.opcode('invokeKnownHelper', params.length, name); | ||
2366 | } else if (this.options.knownHelpersOnly) { | ||
2367 | throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name); | ||
2368 | } else { | ||
2369 | this.opcode('invokeHelper', params.length, name); | ||
2370 | } | ||
2371 | }, | ||
2372 | |||
2373 | ID: function(id) { | ||
2374 | this.addDepth(id.depth); | ||
2375 | this.opcode('getContext', id.depth); | ||
2376 | |||
2377 | var name = id.parts[0]; | ||
2378 | if (!name) { | ||
2379 | this.opcode('pushContext'); | ||
2380 | } else { | ||
2381 | this.opcode('lookupOnContext', id.parts[0]); | ||
2382 | } | ||
2383 | |||
2384 | for(var i=1, l=id.parts.length; i<l; i++) { | ||
2385 | this.opcode('lookup', id.parts[i]); | ||
2386 | } | ||
2387 | }, | ||
2388 | |||
2389 | DATA: function(data) { | ||
2390 | this.options.data = true; | ||
2391 | if (data.id.isScoped || data.id.depth) { | ||
2392 | throw new Exception('Scoped data references are not supported: ' + data.original); | ||
2393 | } | ||
2394 | |||
2395 | this.opcode('lookupData'); | ||
2396 | var parts = data.id.parts; | ||
2397 | for(var i=0, l=parts.length; i<l; i++) { | ||
2398 | this.opcode('lookup', parts[i]); | ||
2399 | } | ||
2400 | }, | ||
2401 | |||
2402 | STRING: function(string) { | ||
2403 | this.opcode('pushString', string.string); | ||
2404 | }, | ||
2405 | |||
2406 | INTEGER: function(integer) { | ||
2407 | this.opcode('pushLiteral', integer.integer); | ||
2408 | }, | ||
2409 | |||
2410 | BOOLEAN: function(bool) { | ||
2411 | this.opcode('pushLiteral', bool.bool); | ||
2412 | }, | ||
2413 | |||
2414 | comment: function() {}, | ||
2415 | |||
2416 | // HELPERS | ||
2417 | opcode: function(name) { | ||
2418 | this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) }); | ||
2419 | }, | ||
2420 | |||
2421 | declare: function(name, value) { | ||
2422 | this.opcodes.push({ opcode: 'DECLARE', name: name, value: value }); | ||
2423 | }, | ||
2424 | |||
2425 | addDepth: function(depth) { | ||
2426 | if(isNaN(depth)) { throw new Error("EWOT"); } | ||
2427 | if(depth === 0) { return; } | ||
2428 | |||
2429 | if(!this.depths[depth]) { | ||
2430 | this.depths[depth] = true; | ||
2431 | this.depths.list.push(depth); | ||
2432 | } | ||
2433 | }, | ||
2434 | |||
2435 | classifyMustache: function(mustache) { | ||
2436 | var isHelper = mustache.isHelper; | ||
2437 | var isEligible = mustache.eligibleHelper; | ||
2438 | var options = this.options; | ||
2439 | |||
2440 | // if ambiguous, we can possibly resolve the ambiguity now | ||
2441 | if (isEligible && !isHelper) { | ||
2442 | var name = mustache.id.parts[0]; | ||
2443 | |||
2444 | if (options.knownHelpers[name]) { | ||
2445 | isHelper = true; | ||
2446 | } else if (options.knownHelpersOnly) { | ||
2447 | isEligible = false; | ||
2448 | } | ||
2449 | } | ||
2450 | |||
2451 | if (isHelper) { return "helper"; } | ||
2452 | else if (isEligible) { return "ambiguous"; } | ||
2453 | else { return "simple"; } | ||
2454 | }, | ||
2455 | |||
2456 | pushParams: function(params) { | ||
2457 | var i = params.length, param; | ||
2458 | |||
2459 | while(i--) { | ||
2460 | param = params[i]; | ||
2461 | |||
2462 | if(this.options.stringParams) { | ||
2463 | if(param.depth) { | ||
2464 | this.addDepth(param.depth); | ||
2465 | } | ||
2466 | |||
2467 | this.opcode('getContext', param.depth || 0); | ||
2468 | this.opcode('pushStringParam', param.stringModeValue, param.type); | ||
2469 | } else { | ||
2470 | this[param.type](param); | ||
2471 | } | ||
2472 | } | ||
2473 | }, | ||
2474 | |||
2475 | setupMustacheParams: function(mustache) { | ||
2476 | var params = mustache.params; | ||
2477 | this.pushParams(params); | ||
2478 | |||
2479 | if(mustache.hash) { | ||
2480 | this.hash(mustache.hash); | ||
2481 | } else { | ||
2482 | this.opcode('emptyHash'); | ||
2483 | } | ||
2484 | |||
2485 | return params; | ||
2486 | }, | ||
2487 | |||
2488 | // this will replace setupMustacheParams when we're done | ||
2489 | setupFullMustacheParams: function(mustache, program, inverse) { | ||
2490 | var params = mustache.params; | ||
2491 | this.pushParams(params); | ||
2492 | |||
2493 | this.opcode('pushProgram', program); | ||
2494 | this.opcode('pushProgram', inverse); | ||
2495 | |||
2496 | if(mustache.hash) { | ||
2497 | this.hash(mustache.hash); | ||
2498 | } else { | ||
2499 | this.opcode('emptyHash'); | ||
2500 | } | ||
2501 | |||
2502 | return params; | ||
2503 | } | ||
2504 | }; | ||
2505 | |||
2506 | function precompile(input, options) { | ||
2507 | if (input == null || (typeof input !== 'string' && input.constructor !== AST.ProgramNode)) { | ||
2508 | throw new Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input); | ||
2509 | } | ||
2510 | |||
2511 | options = options || {}; | ||
2512 | if (!('data' in options)) { | ||
2513 | options.data = true; | ||
2514 | } | ||
2515 | |||
2516 | var ast = parse(input); | ||
2517 | var environment = new Compiler().compile(ast, options); | ||
2518 | return new JavaScriptCompiler().compile(environment, options); | ||
2519 | } | ||
2520 | |||
2521 | __exports__.precompile = precompile;function compile(input, options, env) { | ||
2522 | if (input == null || (typeof input !== 'string' && input.constructor !== AST.ProgramNode)) { | ||
2523 | throw new Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input); | ||
2524 | } | ||
2525 | |||
2526 | options = options || {}; | ||
2527 | |||
2528 | if (!('data' in options)) { | ||
2529 | options.data = true; | ||
2530 | } | ||
2531 | |||
2532 | var compiled; | ||
2533 | |||
2534 | function compileInput() { | ||
2535 | var ast = parse(input); | ||
2536 | var environment = new Compiler().compile(ast, options); | ||
2537 | var templateSpec = new JavaScriptCompiler().compile(environment, options, undefined, true); | ||
2538 | return env.template(templateSpec); | ||
2539 | } | ||
2540 | |||
2541 | // Template is only compiled on first use and cached after that point. | ||
2542 | return function(context, options) { | ||
2543 | if (!compiled) { | ||
2544 | compiled = compileInput(); | ||
2545 | } | ||
2546 | return compiled.call(this, context, options); | ||
2547 | }; | ||
2548 | } | ||
2549 | |||
2550 | __exports__.compile = compile; | ||
2551 | return __exports__; | ||
2552 | })(__module5__, __module8__, __module11__, __module7__); | ||
2553 | |||
2554 | // handlebars.js | ||
2555 | var __module0__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { | ||
2556 | "use strict"; | ||
2557 | var __exports__; | ||
2558 | var Handlebars = __dependency1__; | ||
2559 | |||
2560 | // Compiler imports | ||
2561 | var AST = __dependency2__; | ||
2562 | var Parser = __dependency3__.parser; | ||
2563 | var parse = __dependency3__.parse; | ||
2564 | var Compiler = __dependency4__.Compiler; | ||
2565 | var compile = __dependency4__.compile; | ||
2566 | var precompile = __dependency4__.precompile; | ||
2567 | var JavaScriptCompiler = __dependency5__; | ||
2568 | |||
2569 | var _create = Handlebars.create; | ||
2570 | var create = function() { | ||
2571 | var hb = _create(); | ||
2572 | |||
2573 | hb.compile = function(input, options) { | ||
2574 | return compile(input, options, hb); | ||
2575 | }; | ||
2576 | hb.precompile = precompile; | ||
2577 | |||
2578 | hb.AST = AST; | ||
2579 | hb.Compiler = Compiler; | ||
2580 | hb.JavaScriptCompiler = JavaScriptCompiler; | ||
2581 | hb.Parser = Parser; | ||
2582 | hb.parse = parse; | ||
2583 | |||
2584 | return hb; | ||
2585 | }; | ||
2586 | |||
2587 | Handlebars = create(); | ||
2588 | Handlebars.create = create; | ||
2589 | |||
2590 | __exports__ = Handlebars; | ||
2591 | return __exports__; | ||
2592 | })(__module1__, __module7__, __module8__, __module10__, __module11__); | ||
2593 | |||
2594 | return __module0__; | ||
2595 | })(); |
... | @@ -87,6 +87,264 @@ | ... | @@ -87,6 +87,264 @@ |
87 | equal(data.hasEndTag, true, 'should have ENDLIST tag'); | 87 | equal(data.hasEndTag, true, 'should have ENDLIST tag'); |
88 | }); | 88 | }); |
89 | 89 | ||
90 | /*3.4.7. EXT-X-PLAYLIST-TYPE | ||
91 | |||
92 | The EXT-X-PLAYLIST-TYPE tag provides mutability information about the | ||
93 | Playlist file. It applies to the entire Playlist file. It is | ||
94 | OPTIONAL. Its format is: | ||
95 | |||
96 | #EXT-X-PLAYLIST-TYPE:<EVENT|VOD> | ||
97 | |||
98 | Section 6.2.1 defines the implications of the EXT-X-PLAYLIST-TYPE | ||
99 | tag. | ||
100 | |||
101 | The EXT-X-PLAYLIST-TYPE tag MUST NOT appear in a Master Playlist. | ||
102 | */ | ||
103 | test('should have parsed VOD playlist type', function() { | ||
104 | var | ||
105 | playlistTemplate = Handlebars.compile(window.playlist_type_template), | ||
106 | testData = {playlistType: 'VOD'}, | ||
107 | playlistData = playlistTemplate(testData), | ||
108 | data = m3u8parser.parse(playlistData); | ||
109 | |||
110 | notEqual(data, null, 'data is not NULL'); | ||
111 | equal(data.invalidReasons.length, 0, 'data has 0 invalid reasons'); | ||
112 | equal(data.playlistType, "VOD", 'acceptable PLAYLIST TYPE'); | ||
113 | }); | ||
114 | |||
115 | test('should have parsed EVENT playlist type', function() { | ||
116 | var | ||
117 | playlistTemplate = Handlebars.compile(window.playlist_type_template), | ||
118 | testData = {playlistType: 'EVENT'}, | ||
119 | playlistData = playlistTemplate(testData), | ||
120 | data = m3u8parser.parse(playlistData); | ||
121 | notEqual(data, null, 'data is not NULL'); | ||
122 | equal(data.invalidReasons.length, 0, 'data has 0 invalid reasons'); | ||
123 | equal(data.playlistType, "EVENT", 'acceptable PLAYLIST TYPE'); | ||
124 | }); | ||
125 | |||
126 | test('should have assumed VOD playlist type if not defined', function() { | ||
127 | var | ||
128 | playlistTemplate = Handlebars.compile(window.playlist_type_template), | ||
129 | testData = {}, | ||
130 | playlistData = playlistTemplate(testData), | ||
131 | data = m3u8parser.parse(playlistData); | ||
132 | |||
133 | notEqual(data, null, 'data is not NULL'); | ||
134 | equal(data.invalidReasons.length, 0, 'data has 0 invalid reasons'); | ||
135 | equal(data.playlistType, "VOD", 'acceptable PLAYLIST TYPE'); | ||
136 | }); | ||
137 | |||
138 | test('should have an invalid reason due to invalid playlist type', function() { | ||
139 | var | ||
140 | playlistTemplate = Handlebars.compile(window.playlist_type_template), | ||
141 | testData = {playlistType: 'baklsdhfajsdf'}, | ||
142 | playlistData = playlistTemplate(testData), | ||
143 | data = m3u8parser.parse(playlistData); | ||
144 | notEqual(data, null, 'data is not NULL'); | ||
145 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
146 | equal(data.invalidReasons[0], 'Invalid Playlist Type Value: baklsdhfajsdf'); | ||
147 | }); | ||
148 | |||
149 | test('should have an invalid reason due to invalid playlist type', function() { | ||
150 | var | ||
151 | playlistTemplate = Handlebars.compile(window.playlist_type_template), | ||
152 | testData = {playlistType: ''}, | ||
153 | playlistData = playlistTemplate(testData), | ||
154 | data = m3u8parser.parse(playlistData); | ||
155 | notEqual(data, null, 'data is not NULL'); | ||
156 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
157 | equal(data.invalidReasons[0], 'Invalid Playlist Type Value: \'\''); | ||
158 | }); | ||
159 | |||
160 | /*3.4.2. EXT-X-TARGETDURATION | ||
161 | |||
162 | The EXT-X-TARGETDURATION tag specifies the maximum media segment | ||
163 | duration. The EXTINF duration of each media segment in the Playlist | ||
164 | file, when rounded to the nearest integer, MUST be less than or equal | ||
165 | to the target duration. This tag MUST appear once in a Media | ||
166 | Playlist file. It applies to the entire Playlist file. Its format | ||
167 | is: | ||
168 | |||
169 | #EXT-X-TARGETDURATION:<s> | ||
170 | |||
171 | where s is a decimal-integer indicating the target duration in | ||
172 | seconds. | ||
173 | |||
174 | The EXT-X-TARGETDURATION tag MUST NOT appear in a Master Playlist. | ||
175 | */ | ||
176 | |||
177 | test('valid target duration', function() { | ||
178 | var | ||
179 | playlistTemplate = Handlebars.compile(window.playlist_target_duration_template), | ||
180 | testData = {targetDuration: '10'}, | ||
181 | playlistData = playlistTemplate(testData), | ||
182 | data = m3u8parser.parse(playlistData); | ||
183 | notEqual(data, null, 'data is not NULL'); | ||
184 | equal(data.targetDuration, 10, 'data has correct TARGET DURATION'); | ||
185 | equal(data.invalidReasons.length, 0, 'data has 1 invalid reasons'); | ||
186 | }); | ||
187 | |||
188 | test('NaN target duration', function() { | ||
189 | var | ||
190 | playlistTemplate = Handlebars.compile(window.playlist_target_duration_template), | ||
191 | testData = {targetDuration: '10'}, | ||
192 | playlistData = playlistTemplate(testData), | ||
193 | data = m3u8parser.parse(playlistData); | ||
194 | notEqual(data, null, 'data is not NULL'); | ||
195 | equal(data.targetDuration, 10, 'data has correct TARGET DURATION'); | ||
196 | equal(data.invalidReasons.length, 0, 'data has 1 invalid reasons'); | ||
197 | |||
198 | testData = {targetDuration: 'string'}; | ||
199 | playlistData = playlistTemplate(testData), | ||
200 | data = m3u8parser.parse(playlistData); | ||
201 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
202 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
203 | equal(data.invalidReasons[0], 'Invalid Target Duration Value: string'); | ||
204 | |||
205 | testData = {targetDuration: ''}; | ||
206 | playlistData = playlistTemplate(testData), | ||
207 | data = m3u8parser.parse(playlistData); | ||
208 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
209 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
210 | equal(data.invalidReasons[0], 'Invalid Target Duration Value: \'\''); | ||
211 | |||
212 | testData = {} | ||
213 | playlistData = playlistTemplate(testData), | ||
214 | notEqual(data, null, 'data is not NULL'); | ||
215 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
216 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
217 | equal(data.invalidReasons[0], 'Invalid Target Duration Value: '+ undefined); | ||
218 | |||
219 | }); | ||
220 | |||
221 | test('target duration lower than segment', function() { | ||
222 | var | ||
223 | playlistTemplate = Handlebars.compile(window.playlist_target_duration_template), | ||
224 | testData = {targetDuration: '4'}, | ||
225 | playlistData = playlistTemplate(testData), | ||
226 | data = m3u8parser.parse(playlistData); | ||
227 | |||
228 | notEqual(data, null, 'data is not NULL'); | ||
229 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
230 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
231 | equal(data.invalidReasons[0], 'Invalid Target Duration Value: 4 is lower than segments'); | ||
232 | }); | ||
233 | |||
234 | /*3.4.3. EXT-X-MEDIA-SEQUENCE | ||
235 | |||
236 | Each media segment in a Playlist has a unique integer sequence | ||
237 | number. The sequence number of a segment is equal to the sequence | ||
238 | number of the segment that preceded it plus one. The EXT-X-MEDIA- | ||
239 | SEQUENCE tag indicates the sequence number of the first segment that | ||
240 | appears in a Playlist file. Its format is: | ||
241 | |||
242 | #EXT-X-MEDIA-SEQUENCE:<number> | ||
243 | |||
244 | where number is a decimal-integer. The sequence number MUST NOT | ||
245 | decrease. | ||
246 | |||
247 | A Media Playlist file MUST NOT contain more than one EXT-X-MEDIA- | ||
248 | SEQUENCE tag. If the Media Playlist file does not contain an EXT-X- | ||
249 | MEDIA-SEQUENCE tag then the sequence number of the first segment in | ||
250 | the playlist SHALL be considered to be 0. A client MUST NOT assume | ||
251 | that segments with the same sequence number in different Media | ||
252 | Playlists contain matching content. | ||
253 | |||
254 | A media URI is not required to contain its sequence number. | ||
255 | */ | ||
256 | |||
257 | test('media sequence is valid in the playlist', function() { | ||
258 | var | ||
259 | playlistTemplate = Handlebars.compile(window.playlist_media_sequence_template), | ||
260 | testData = {mediaSequence: '0'}, | ||
261 | playlistData = playlistTemplate(testData), | ||
262 | data = m3u8parser.parse(playlistData); | ||
263 | |||
264 | notEqual(data, null, 'data is not NULL'); | ||
265 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
266 | equal(data.invalidReasons.length, 0, 'data has 0 invalid reasons'); | ||
267 | equal(data.mediaSequence, 0, 'MEDIA SEQUENCE is correct'); | ||
268 | }); | ||
269 | |||
270 | test('media sequence is encountered twice in the playlist', function() { | ||
271 | var | ||
272 | playlistTemplate = Handlebars.compile(window.playlist_media_sequence_template), | ||
273 | testData = {mediaSequence: '0', mediaSequence1: '1'}, | ||
274 | playlistData = playlistTemplate(testData), | ||
275 | data = m3u8parser.parse(playlistData); | ||
276 | |||
277 | notEqual(data, null, 'data is not NULL'); | ||
278 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
279 | equal(data.invalidReasons.length, 0, 'data has 0 invalid reasons'); | ||
280 | equal(data.mediaSequence, 0, 'MEDIA SEQUENCE tags after the first should be ignored'); | ||
281 | }); | ||
282 | |||
283 | test('media sequence is undefined in the playlist', function() { | ||
284 | var | ||
285 | playlistTemplate = Handlebars.compile(window.playlist_media_sequence_template), | ||
286 | testData = {mediaSequence: ''}, | ||
287 | playlistData = playlistTemplate(testData), | ||
288 | data = m3u8parser.parse(playlistData); | ||
289 | |||
290 | notEqual(data, null, 'data is not NULL'); | ||
291 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
292 | equal(data.invalidReasons.length, 0, 'data has 0 invalid reasons'); | ||
293 | equal(data.mediaSequence, 0, 'MEDIA SEQUENCE should default to 0 when not present.'); | ||
294 | }); | ||
295 | |||
296 | test('media sequence is empty in the playlist', function() { | ||
297 | var | ||
298 | playlistTemplate = Handlebars.compile(window.playlist_media_sequence_template), | ||
299 | testData = {mediaSequence: ''}, | ||
300 | playlistData = playlistTemplate(testData), | ||
301 | data = m3u8parser.parse(playlistData); | ||
302 | |||
303 | notEqual(data, null, 'data is not NULL'); | ||
304 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
305 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
306 | equal(data.mediaSequence, 0, 'Invalid Media Sequence Value: \'\''); | ||
307 | }); | ||
308 | |||
309 | test('media sequence is high (non-zero in first file) in the playlist', function() { | ||
310 | var | ||
311 | playlistTemplate = Handlebars.compile(window.playlist_media_sequence_template), | ||
312 | testData = {mediaSequence: '1'}, | ||
313 | playlistData = playlistTemplate(testData), | ||
314 | data = m3u8parser.parse(playlistData); | ||
315 | |||
316 | notEqual(data, null, 'data is not NULL'); | ||
317 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
318 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
319 | equal(data.invalidReasons[0], 'Invalid Media Sequence Value: 1'); | ||
320 | }); | ||
321 | |||
322 | test('media sequence (-1) in the playlist', function() { | ||
323 | var | ||
324 | playlistTemplate = Handlebars.compile(window.playlist_media_sequence_template), | ||
325 | testData = {mediaSequence: '-1'}, | ||
326 | playlistData = playlistTemplate(testData), | ||
327 | data = m3u8parser.parse(playlistData); | ||
328 | |||
329 | notEqual(data, null, 'data is not NULL'); | ||
330 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
331 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
332 | equal(data.invalidReasons[0], 'Invalid Media Sequence Value: -1'); | ||
333 | }); | ||
334 | |||
335 | test('media sequence invalid (string) in the playlist', function() { | ||
336 | var | ||
337 | playlistTemplate = Handlebars.compile(window.playlist_media_sequence_template), | ||
338 | testData = {mediaSequence: 'asdfkasdkfl'}, | ||
339 | playlistData = playlistTemplate(testData), | ||
340 | data = m3u8parser.parse(playlistData); | ||
341 | |||
342 | notEqual(data, null, 'data is not NULL'); | ||
343 | notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); | ||
344 | equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); | ||
345 | equal(data.invalidReasons[0], 'Invalid Media Sequence Value: asdfkasdkfl'); | ||
346 | }); | ||
347 | |||
90 | module('brightcove playlist', { | 348 | module('brightcove playlist', { |
91 | setup: function() { | 349 | setup: function() { |
92 | m3u8parser = new window.videojs.hls.M3U8Parser(); | 350 | m3u8parser = new window.videojs.hls.M3U8Parser(); | ... | ... |
1 | window.playlist_media_sequence_template = '#EXTM3U\n'+ | ||
2 | '#EXT-X-PLAYLIST-TYPE:VOD\n'+ | ||
3 | '{{#if mediaSequence}}#EXT-X-MEDIA-SEQUENCE:{{{mediaSequence}}}{{/if}}\n'+ | ||
4 | '{{#if mediaSequence2}}#EXT-X-MEDIA-SEQUENCE:{{{mediaSequence2}}}{{/if}}\n'+ | ||
5 | '#EXT-X-ALLOW-CACHE:YES\n'+ | ||
6 | '#EXT-X-TARGETDURATION:8\n'+ | ||
7 | '#EXTINF:6.640,{}\n'+ | ||
8 | '/test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts\n'+ | ||
9 | '#EXTINF:6.080,{}\n'+ | ||
10 | '/test/ts-files/tvy7/56be1cef869a1c0cc8e38864ad1add17d187f051-hi720.ts\n'+ | ||
11 | '#EXTINF:6.600,{}\n'+ | ||
12 | '/test/ts-files/tvy7/549c8c77f55f049741a06596e5c1e01dacaa46d0-hi720.ts\n'+ | ||
13 | '#EXTINF:5.000,{}\n'+ | ||
14 | '/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts\n'+ | ||
15 | '#EXT-X-ENDLIST' |
1 | window.playlist_target_duration_template = '#EXTM3U\n'+ | ||
2 | '#EXT-X-PLAYLIST-TYPE:VOD\n'+ | ||
3 | '#EXT-X-MEDIA-SEQUENCE:0\n'+ | ||
4 | '#EXT-X-ALLOW-CACHE:YES\n'+ | ||
5 | '{{#if targetDuration}}#EXT-X-TARGETDURATION:{{{targetDuration}}}{{/if}}\n'+ | ||
6 | '#EXTINF:6.640,{}\n'+ | ||
7 | '/test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts\n'+ | ||
8 | '#EXTINF:6.080,{}\n'+ | ||
9 | '/test/ts-files/tvy7/56be1cef869a1c0cc8e38864ad1add17d187f051-hi720.ts\n'+ | ||
10 | '#EXTINF:6.600,{}\n'+ | ||
11 | '/test/ts-files/tvy7/549c8c77f55f049741a06596e5c1e01dacaa46d0-hi720.ts\n'+ | ||
12 | '#EXTINF:5.000,{}\n'+ | ||
13 | '/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts\n'+ | ||
14 | '#EXT-X-ENDLIST' |
test/manifest/playlist_type_template.js
0 → 100644
1 | window.playlist_type_template = '#EXTM3U\n' + | ||
2 | '{{#if playlistType}}#EXT-X-PLAYLIST-TYPE:{{{playlistType}}}{{/if}}\n' + | ||
3 | '#EXT-X-TARGETDURATION:10\n' + | ||
4 | '#EXTINF:10,\n' + | ||
5 | '/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00001.ts\n' + | ||
6 | '#EXTINF:10,\n' + | ||
7 | '/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00002.ts\n' + | ||
8 | '#EXTINF:10,\n' + | ||
9 | '/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00003.ts\n' + | ||
10 | '#EXTINF:10,\n' + | ||
11 | '/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00004.ts\n' + | ||
12 | '#EXTINF:10,\n' + | ||
13 | '/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00005.ts\n' + | ||
14 | '#EXTINF:8,\n' + | ||
15 | '/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts\n' + | ||
16 | '#ZEN-TOTAL-DURATION:57.9911\n' + | ||
17 | '#EXT-X-ENDLIST'; |
... | @@ -7,6 +7,9 @@ | ... | @@ -7,6 +7,9 @@ |
7 | <link rel="stylesheet" href="../libs/qunit/qunit.css" media="screen"> | 7 | <link rel="stylesheet" href="../libs/qunit/qunit.css" media="screen"> |
8 | <script src="../libs/qunit/qunit.js"></script> | 8 | <script src="../libs/qunit/qunit.js"></script> |
9 | 9 | ||
10 | <!-- handlebars.js --> | ||
11 | <script src="../libs/handlebars/handlebars-v1.1.2.js"></script> | ||
12 | |||
10 | <!-- video.js --> | 13 | <!-- video.js --> |
11 | <script src="../node_modules/video.js/video.dev.js"></script> | 14 | <script src="../node_modules/video.js/video.dev.js"></script> |
12 | 15 | ||
... | @@ -26,6 +29,9 @@ | ... | @@ -26,6 +29,9 @@ |
26 | <!-- M3U8 TEST DATA --> | 29 | <!-- M3U8 TEST DATA --> |
27 | <script src="manifest/playlistM3U8data.js"></script> | 30 | <script src="manifest/playlistM3U8data.js"></script> |
28 | <script src="manifest/brightcove_playlist_m3u8.js"></script> | 31 | <script src="manifest/brightcove_playlist_m3u8.js"></script> |
32 | <script src="manifest/playlist_type_template.js"></script> | ||
33 | <script src="manifest/playlist_media_sequence_template.js"></script> | ||
34 | <script src="manifest/playlist_target_duration_template.js"></script> | ||
29 | <!-- M3U8 --> | 35 | <!-- M3U8 --> |
30 | <!-- SEGMENT --> | 36 | <!-- SEGMENT --> |
31 | <script src="tsSegment-bc.js"></script> | 37 | <script src="tsSegment-bc.js"></script> | ... | ... |
-
Please register or sign in to post a comment