mirror of
				https://github.com/qist/tvbox.git
				synced 2025-10-29 19:22:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			504 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			504 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| (function(global, factory) {
 | |
|     typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.jinja = {}))
 | |
| })(this, function(jinja) {
 | |
|     "use strict";
 | |
|     var STRINGS = /'(\\.|[^'])*'|"(\\.|[^"'"])*"/g;
 | |
|     var IDENTS_AND_NUMS = /([$_a-z][$\w]*)|([+-]?\d+(\.\d+)?)/g;
 | |
|     var NUMBER = /^[+-]?\d+(\.\d+)?$/;
 | |
|     var NON_PRIMITIVES = /\[[@#~](,[@#~])*\]|\[\]|\{([@i]:[@#~])(,[@i]:[@#~])*\}|\{\}/g;
 | |
|     var IDENTIFIERS = /[$_a-z][$\w]*/gi;
 | |
|     var VARIABLES = /i(\.i|\[[@#i]\])*/g;
 | |
|     var ACCESSOR = /(\.i|\[[@#i]\])/g;
 | |
|     var OPERATORS = /(===?|!==?|>=?|<=?|&&|\|\||[+\-\*\/%])/g;
 | |
|     var EOPS = /(^|[^$\w])(and|or|not|is|isnot)([^$\w]|$)/g;
 | |
|     var LEADING_SPACE = /^\s+/;
 | |
|     var TRAILING_SPACE = /\s+$/;
 | |
|     var START_TOKEN = /\{\{\{|\{\{|\{%|\{#/;
 | |
|     var TAGS = {
 | |
|         "{{{": /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}\}/,
 | |
|         "{{": /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}/,
 | |
|         "{%": /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?%\}/,
 | |
|         "{#": /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?#\}/
 | |
|     };
 | |
|     var delimeters = {
 | |
|         "{%": "directive",
 | |
|         "{{": "output",
 | |
|         "{#": "comment"
 | |
|     };
 | |
|     var operators = {
 | |
|         and: "&&",
 | |
|         or: "||",
 | |
|         not: "!",
 | |
|         is: "==",
 | |
|         isnot: "!="
 | |
|     };
 | |
|     var constants = {
 | |
|         true: true,
 | |
|         false: false,
 | |
|         null: null
 | |
|     };
 | |
| 
 | |
|     function Parser() {
 | |
|         this.nest = [];
 | |
|         this.compiled = [];
 | |
|         this.childBlocks = 0;
 | |
|         this.parentBlocks = 0;
 | |
|         this.isSilent = false
 | |
|     }
 | |
|     Parser.prototype.push = function(line) {
 | |
|         if (!this.isSilent) {
 | |
|             this.compiled.push(line)
 | |
|         }
 | |
|     };
 | |
|     Parser.prototype.parse = function(src) {
 | |
|         this.tokenize(src);
 | |
|         return this.compiled
 | |
|     };
 | |
|     Parser.prototype.tokenize = function(src) {
 | |
|         var lastEnd = 0,
 | |
|             parser = this,
 | |
|             trimLeading = false;
 | |
|         matchAll(src, START_TOKEN, function(open, index, src) {
 | |
|             var match = src.slice(index + open.length).match(TAGS[open]);
 | |
|             match = match ? match[0] : "";
 | |
|             var simplified = match.replace(STRINGS, "@");
 | |
|             if (!match || ~simplified.indexOf(open)) {
 | |
|                 return index + 1
 | |
|             }
 | |
|             var inner = match.slice(0, 0 - open.length);
 | |
|             if (inner.charAt(0) === "-") var wsCollapseLeft = true;
 | |
|             if (inner.slice(-1) === "-") var wsCollapseRight = true;
 | |
|             inner = inner.replace(/^-|-$/g, "").trim();
 | |
|             if (parser.rawMode && open + inner !== "{%endraw") {
 | |
|                 return index + 1
 | |
|             }
 | |
|             var text = src.slice(lastEnd, index);
 | |
|             lastEnd = index + open.length + match.length;
 | |
|             if (trimLeading) text = trimLeft(text);
 | |
|             if (wsCollapseLeft) text = trimRight(text);
 | |
|             if (wsCollapseRight) trimLeading = true;
 | |
|             if (open === "{{{") {
 | |
|                 open = "{{";
 | |
|                 inner += "|safe"
 | |
|             }
 | |
|             parser.textHandler(text);
 | |
|             parser.tokenHandler(open, inner)
 | |
|         });
 | |
|         var text = src.slice(lastEnd);
 | |
|         if (trimLeading) text = trimLeft(text);
 | |
|         this.textHandler(text)
 | |
|     };
 | |
|     Parser.prototype.textHandler = function(text) {
 | |
|         this.push("write(" + JSON.stringify(text) + ");")
 | |
|     };
 | |
|     Parser.prototype.tokenHandler = function(open, inner) {
 | |
|         var type = delimeters[open];
 | |
|         if (type === "directive") {
 | |
|             this.compileTag(inner)
 | |
|         } else if (type === "output") {
 | |
|             var extracted = this.extractEnt(inner, STRINGS, "@");
 | |
|             extracted.src = extracted.src.replace(/\|\|/g, "~").split("|");
 | |
|             extracted.src = extracted.src.map(function(part) {
 | |
|                 return part.split("~").join("||")
 | |
|             });
 | |
|             var parts = this.injectEnt(extracted, "@");
 | |
|             if (parts.length > 1) {
 | |
|                 var filters = parts.slice(1).map(this.parseFilter.bind(this));
 | |
|                 this.push("filter(" + this.parseExpr(parts[0]) + "," + filters.join(",") + ");")
 | |
|             } else {
 | |
|                 this.push("filter(" + this.parseExpr(parts[0]) + ");")
 | |
|             }
 | |
|         }
 | |
|     };
 | |
|     Parser.prototype.compileTag = function(str) {
 | |
|         var directive = str.split(" ")[0];
 | |
|         var handler = tagHandlers[directive];
 | |
|         if (!handler) {
 | |
|             throw new Error("Invalid tag: " + str)
 | |
|         }
 | |
|         handler.call(this, str.slice(directive.length).trim())
 | |
|     };
 | |
|     Parser.prototype.parseFilter = function(src) {
 | |
|         src = src.trim();
 | |
|         var match = src.match(/[:(]/);
 | |
|         var i = match ? match.index : -1;
 | |
|         if (i < 0) return JSON.stringify([src]);
 | |
|         var name = src.slice(0, i);
 | |
|         var args = src.charAt(i) === ":" ? src.slice(i + 1) : src.slice(i + 1, -1);
 | |
|         args = this.parseExpr(args, {
 | |
|             terms: true
 | |
|         });
 | |
|         return "[" + JSON.stringify(name) + "," + args + "]"
 | |
|     };
 | |
|     Parser.prototype.extractEnt = function(src, regex, placeholder) {
 | |
|         var subs = [],
 | |
|             isFunc = typeof placeholder == "function";
 | |
|         src = src.replace(regex, function(str) {
 | |
|             var replacement = isFunc ? placeholder(str) : placeholder;
 | |
|             if (replacement) {
 | |
|                 subs.push(str);
 | |
|                 return replacement
 | |
|             }
 | |
|             return str
 | |
|         });
 | |
|         return {
 | |
|             src: src,
 | |
|             subs: subs
 | |
|         }
 | |
|     };
 | |
|     Parser.prototype.injectEnt = function(extracted, placeholder) {
 | |
|         var src = extracted.src,
 | |
|             subs = extracted.subs,
 | |
|             isArr = Array.isArray(src);
 | |
|         var arr = isArr ? src : [src];
 | |
|         var re = new RegExp("[" + placeholder + "]", "g"),
 | |
|             i = 0;
 | |
|         arr.forEach(function(src, index) {
 | |
|             arr[index] = src.replace(re, function() {
 | |
|                 return subs[i++]
 | |
|             })
 | |
|         });
 | |
|         return isArr ? arr : arr[0]
 | |
|     };
 | |
|     Parser.prototype.replaceComplex = function(s) {
 | |
|         var parsed = this.extractEnt(s, /i(\.i|\[[@#i]\])+/g, "v");
 | |
|         parsed.src = parsed.src.replace(NON_PRIMITIVES, "~");
 | |
|         return this.injectEnt(parsed, "v")
 | |
|     };
 | |
|     Parser.prototype.parseExpr = function(src, opts) {
 | |
|         opts = opts || {};
 | |
|         var parsed1 = this.extractEnt(src, STRINGS, "@");
 | |
|         parsed1.src = parsed1.src.replace(EOPS, function(s, before, op, after) {
 | |
|             return op in operators ? before + operators[op] + after : s
 | |
|         });
 | |
|         var parsed2 = this.extractEnt(parsed1.src, IDENTS_AND_NUMS, function(s) {
 | |
|             return s in constants || NUMBER.test(s) ? "#" : null
 | |
|         });
 | |
|         var parsed3 = this.extractEnt(parsed2.src, IDENTIFIERS, "i");
 | |
|         parsed3.src = parsed3.src.replace(/\s+/g, "");
 | |
|         var simplified = parsed3.src;
 | |
|         while (simplified !== (simplified = this.replaceComplex(simplified)));
 | |
|         while (simplified !== (simplified = simplified.replace(/i(\.i|\[[@#i]\])+/, "v")));
 | |
|         simplified = simplified.replace(/[iv]\[v?\]/g, "x");
 | |
|         simplified = simplified.replace(/[@#~v]/g, "i");
 | |
|         simplified = simplified.replace(OPERATORS, "%");
 | |
|         simplified = simplified.replace(/!+[i]/g, "i");
 | |
|         var terms = opts.terms ? simplified.split(",") : [simplified];
 | |
|         terms.forEach(function(term) {
 | |
|             while (term !== (term = term.replace(/\(i(%i)*\)/g, "i")));
 | |
|             if (!term.match(/^i(%i)*/)) {
 | |
|                 throw new Error("Invalid expression: " + src + " " + term)
 | |
|             }
 | |
|         });
 | |
|         parsed3.src = parsed3.src.replace(VARIABLES, this.parseVar.bind(this));
 | |
|         parsed2.src = this.injectEnt(parsed3, "i");
 | |
|         parsed1.src = this.injectEnt(parsed2, "#");
 | |
|         return this.injectEnt(parsed1, "@")
 | |
|     };
 | |
|     Parser.prototype.parseVar = function(src) {
 | |
|         var args = Array.prototype.slice.call(arguments);
 | |
|         var str = args.pop(),
 | |
|             index = args.pop();
 | |
|         if (src === "i" && str.charAt(index + 1) === ":") {
 | |
|             return '"i"'
 | |
|         }
 | |
|         var parts = ['"i"'];
 | |
|         src.replace(ACCESSOR, function(part) {
 | |
|             if (part === ".i") {
 | |
|                 parts.push('"i"')
 | |
|             } else if (part === "[i]") {
 | |
|                 parts.push('get("i")')
 | |
|             } else {
 | |
|                 parts.push(part.slice(1, -1))
 | |
|             }
 | |
|         });
 | |
|         return "get(" + parts.join(",") + ")"
 | |
|     };
 | |
|     Parser.prototype.escName = function(str) {
 | |
|         return str.replace(/\W/g, function(s) {
 | |
|             return "$" + s.charCodeAt(0).toString(16)
 | |
|         })
 | |
|     };
 | |
|     Parser.prototype.parseQuoted = function(str) {
 | |
|         if (str.charAt(0) === "'") {
 | |
|             str = str.slice(1, -1).replace(/\\.|"/, function(s) {
 | |
|                 if (s === "\\'") return "'";
 | |
|                 return s.charAt(0) === "\\" ? s : "\\" + s
 | |
|             });
 | |
|             str = '"' + str + '"'
 | |
|         }
 | |
|         return JSON.parse(str)
 | |
|     };
 | |
|     var tagHandlers = {
 | |
|         if: function(expr) {
 | |
|             this.push("if (" + this.parseExpr(expr) + ") {");
 | |
|             this.nest.unshift("if")
 | |
|         },
 | |
|         else: function() {
 | |
|             if (this.nest[0] === "for") {
 | |
|                 this.push("}, function() {")
 | |
|             } else {
 | |
|                 this.push("} else {")
 | |
|             }
 | |
|         },
 | |
|         elseif: function(expr) {
 | |
|             this.push("} else if (" + this.parseExpr(expr) + ") {")
 | |
|         },
 | |
|         endif: function() {
 | |
|             this.nest.shift();
 | |
|             this.push("}")
 | |
|         },
 | |
|         for: function(str) {
 | |
|             var i = str.indexOf(" in ");
 | |
|             var name = str.slice(0, i).trim();
 | |
|             var expr = str.slice(i + 4).trim();
 | |
|             this.push("each(" + this.parseExpr(expr) + "," + JSON.stringify(name) + ",function() {");
 | |
|             this.nest.unshift("for")
 | |
|         },
 | |
|         endfor: function() {
 | |
|             this.nest.shift();
 | |
|             this.push("});")
 | |
|         },
 | |
|         raw: function() {
 | |
|             this.rawMode = true
 | |
|         },
 | |
|         endraw: function() {
 | |
|             this.rawMode = false
 | |
|         },
 | |
|         set: function(stmt) {
 | |
|             var i = stmt.indexOf("=");
 | |
|             var name = stmt.slice(0, i).trim();
 | |
|             var expr = stmt.slice(i + 1).trim();
 | |
|             this.push("set(" + JSON.stringify(name) + "," + this.parseExpr(expr) + ");")
 | |
|         },
 | |
|         block: function(name) {
 | |
|             if (this.isParent) {
 | |
|                 ++this.parentBlocks;
 | |
|                 var blockName = "block_" + (this.escName(name) || this.parentBlocks);
 | |
|                 this.push("block(typeof " + blockName + ' == "function" ? ' + blockName + " : function() {")
 | |
|             } else if (this.hasParent) {
 | |
|                 this.isSilent = false;
 | |
|                 ++this.childBlocks;
 | |
|                 blockName = "block_" + (this.escName(name) || this.childBlocks);
 | |
|                 this.push("function " + blockName + "() {")
 | |
|             }
 | |
|             this.nest.unshift("block")
 | |
|         },
 | |
|         endblock: function() {
 | |
|             this.nest.shift();
 | |
|             if (this.isParent) {
 | |
|                 this.push("});")
 | |
|             } else if (this.hasParent) {
 | |
|                 this.push("}");
 | |
|                 this.isSilent = true
 | |
|             }
 | |
|         },
 | |
|         extends: function(name) {
 | |
|             name = this.parseQuoted(name);
 | |
|             var parentSrc = this.readTemplateFile(name);
 | |
|             this.isParent = true;
 | |
|             this.tokenize(parentSrc);
 | |
|             this.isParent = false;
 | |
|             this.hasParent = true;
 | |
|             this.isSilent = true
 | |
|         },
 | |
|         include: function(name) {
 | |
|             name = this.parseQuoted(name);
 | |
|             var incSrc = this.readTemplateFile(name);
 | |
|             this.isInclude = true;
 | |
|             this.tokenize(incSrc);
 | |
|             this.isInclude = false
 | |
|         }
 | |
|     };
 | |
|     tagHandlers.assign = tagHandlers.set;
 | |
|     tagHandlers.elif = tagHandlers.elseif;
 | |
|     var getRuntime = function runtime(data, opts) {
 | |
|         var defaults = {
 | |
|             autoEscape: "toJson"
 | |
|         };
 | |
|         var _toString = Object.prototype.toString;
 | |
|         var _hasOwnProperty = Object.prototype.hasOwnProperty;
 | |
|         var getKeys = Object.keys || function(obj) {
 | |
|             var keys = [];
 | |
|             for (var n in obj)
 | |
|                 if (_hasOwnProperty.call(obj, n)) keys.push(n);
 | |
|             return keys
 | |
|         };
 | |
|         var isArray = Array.isArray || function(obj) {
 | |
|             return _toString.call(obj) === "[object Array]"
 | |
|         };
 | |
|         var create = Object.create || function(obj) {
 | |
|             function F() {}
 | |
|             F.prototype = obj;
 | |
|             return new F
 | |
|         };
 | |
|         var toString = function(val) {
 | |
|             if (val == null) return "";
 | |
|             return typeof val.toString == "function" ? val.toString() : _toString.call(val)
 | |
|         };
 | |
|         var extend = function(dest, src) {
 | |
|             var keys = getKeys(src);
 | |
|             for (var i = 0, len = keys.length; i < len; i++) {
 | |
|                 var key = keys[i];
 | |
|                 dest[key] = src[key]
 | |
|             }
 | |
|             return dest
 | |
|         };
 | |
|         var get = function() {
 | |
|             var val, n = arguments[0],
 | |
|                 c = stack.length;
 | |
|             while (c--) {
 | |
|                 val = stack[c][n];
 | |
|                 if (typeof val != "undefined") break
 | |
|             }
 | |
|             for (var i = 1, len = arguments.length; i < len; i++) {
 | |
|                 if (val == null) continue;
 | |
|                 n = arguments[i];
 | |
|                 val = _hasOwnProperty.call(val, n) ? val[n] : typeof val._get == "function" ? val[n] = val._get(n) : null
 | |
|             }
 | |
|             return val == null ? "" : val
 | |
|         };
 | |
|         var set = function(n, val) {
 | |
|             stack[stack.length - 1][n] = val
 | |
|         };
 | |
|         var push = function(ctx) {
 | |
|             stack.push(ctx || {})
 | |
|         };
 | |
|         var pop = function() {
 | |
|             stack.pop()
 | |
|         };
 | |
|         var write = function(str) {
 | |
|             output.push(str)
 | |
|         };
 | |
|         var filter = function(val) {
 | |
|             for (var i = 1, len = arguments.length; i < len; i++) {
 | |
|                 var arr = arguments[i],
 | |
|                     name = arr[0],
 | |
|                     filter = filters[name];
 | |
|                 if (filter) {
 | |
|                     arr[0] = val;
 | |
|                     val = filter.apply(data, arr)
 | |
|                 } else {
 | |
|                     throw new Error("Invalid filter: " + name)
 | |
|                 }
 | |
|             }
 | |
|             if (opts.autoEscape && name !== opts.autoEscape && name !== "safe") {
 | |
|                 val = filters[opts.autoEscape].call(data, val)
 | |
|             }
 | |
|             output.push(val)
 | |
|         };
 | |
|         var each = function(obj, loopvar, fn1, fn2) {
 | |
|             if (obj == null) return;
 | |
|             var arr = isArray(obj) ? obj : getKeys(obj),
 | |
|                 len = arr.length;
 | |
|             var ctx = {
 | |
|                 loop: {
 | |
|                     length: len,
 | |
|                     first: arr[0],
 | |
|                     last: arr[len - 1]
 | |
|                 }
 | |
|             };
 | |
|             push(ctx);
 | |
|             for (var i = 0; i < len; i++) {
 | |
|                 extend(ctx.loop, {
 | |
|                     index: i + 1,
 | |
|                     index0: i
 | |
|                 });
 | |
|                 fn1(ctx[loopvar] = arr[i])
 | |
|             }
 | |
|             if (len === 0 && fn2) fn2();
 | |
|             pop()
 | |
|         };
 | |
|         var block = function(fn) {
 | |
|             push();
 | |
|             fn();
 | |
|             pop()
 | |
|         };
 | |
|         var render = function() {
 | |
|             return output.join("")
 | |
|         };
 | |
|         data = data || {};
 | |
|         opts = extend(defaults, opts || {});
 | |
|         var filters = extend({
 | |
|             html: function(val) {
 | |
|                 return toString(val).split("&").join("&").split("<").join("<").split(">").join(">").split('"').join(""")
 | |
|             },
 | |
|             safe: function(val) {
 | |
|                 return val
 | |
|             },
 | |
|             toJson: function(val) {
 | |
|                 if (typeof val === "object") {
 | |
|                     return JSON.stringify(val)
 | |
|                 }
 | |
|                 return toString(val)
 | |
|             }
 | |
|         }, opts.filters || {});
 | |
|         var stack = [create(data || {})],
 | |
|             output = [];
 | |
|         return {
 | |
|             get: get,
 | |
|             set: set,
 | |
|             push: push,
 | |
|             pop: pop,
 | |
|             write: write,
 | |
|             filter: filter,
 | |
|             each: each,
 | |
|             block: block,
 | |
|             render: render
 | |
|         }
 | |
|     };
 | |
|     var runtime;
 | |
|     jinja.compile = function(markup, opts) {
 | |
|         opts = opts || {};
 | |
|         var parser = new Parser;
 | |
|         parser.readTemplateFile = this.readTemplateFile;
 | |
|         var code = [];
 | |
|         code.push("function render($) {");
 | |
|         code.push("var get = $.get, set = $.set, push = $.push, pop = $.pop, write = $.write, filter = $.filter, each = $.each, block = $.block;");
 | |
|         code.push.apply(code, parser.parse(markup));
 | |
|         code.push("return $.render();");
 | |
|         code.push("}");
 | |
|         code = code.join("\n");
 | |
|         if (opts.runtime === false) {
 | |
|             var fn = new Function("data", "options", "return (" + code + ")(runtime(data, options))")
 | |
|         } else {
 | |
|             runtime = runtime || (runtime = getRuntime.toString());
 | |
|             fn = new Function("data", "options", "return (" + code + ")((" + runtime + ")(data, options))")
 | |
|         }
 | |
|         return {
 | |
|             render: fn
 | |
|         }
 | |
|     };
 | |
|     jinja.render = function(markup, data, opts) {
 | |
|         var tmpl = jinja.compile(markup);
 | |
|         return tmpl.render(data, opts)
 | |
|     };
 | |
|     jinja.templateFiles = [];
 | |
|     jinja.readTemplateFile = function(name) {
 | |
|         var templateFiles = this.templateFiles || [];
 | |
|         var templateFile = templateFiles[name];
 | |
|         if (templateFile == null) {
 | |
|             throw new Error("Template file not found: " + name)
 | |
|         }
 | |
|         return templateFile
 | |
|     };
 | |
| 
 | |
|     function trimLeft(str) {
 | |
|         return str.replace(LEADING_SPACE, "")
 | |
|     }
 | |
| 
 | |
|     function trimRight(str) {
 | |
|         return str.replace(TRAILING_SPACE, "")
 | |
|     }
 | |
| 
 | |
|     function matchAll(str, reg, fn) {
 | |
|         reg = new RegExp(reg.source, "g" + (reg.ignoreCase ? "i" : "") + (reg.multiline ? "m" : ""));
 | |
|         var match;
 | |
|         while (match = reg.exec(str)) {
 | |
|             var result = fn(match[0], match.index, str);
 | |
|             if (typeof result == "number") {
 | |
|                 reg.lastIndex = result
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }); |