UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

503 lines (502 loc) 20.7 kB
/*! * CanJS - 2.3.34 * http://canjs.com/ * Copyright (c) 2018 Bitovi * Mon, 30 Apr 2018 20:56:51 GMT * Licensed MIT */ /*can@2.3.34#view/mustache/mustache*/ define([ 'can/util/library', 'can/view/scope', 'can/view', 'can/scanner', 'can/compute', 'can/render', 'can/view/bindings' ], function (can) { can.view.ext = '.mustache'; var SCOPE = 'scope', HASH = '___h4sh', CONTEXT_OBJ = '{scope:' + SCOPE + ',options:options}', SPECIAL_CONTEXT_OBJ = '{scope:' + SCOPE + ',options:options, special: true}', ARG_NAMES = SCOPE + ',options', argumentsRegExp = /((([^'"\s]+?=)?('.*?'|".*?"))|.*?)\s/g, literalNumberStringBooleanRegExp = /^(('.*?'|".*?"|[0-9]+\.?[0-9]*|true|false|null|undefined)|((.+?)=(('.*?'|".*?"|[0-9]+\.?[0-9]*|true|false)|(.+))))$/, makeLookupLiteral = function (type) { return '{get:"' + type.replace(/"/g, '\\"') + '"}'; }, isLookup = function (obj) { return obj && typeof obj.get === 'string'; }, isObserveLike = function (obj) { return obj instanceof can.Map || obj && !!obj._get; }, isArrayLike = function (obj) { return obj && obj.splice && typeof obj.length === 'number'; }, makeConvertToScopes = function (original, scope, options) { var originalWithScope = function (ctx, opts) { return original(ctx || scope, opts); }; return function (updatedScope, updatedOptions) { if (updatedScope !== undefined && !(updatedScope instanceof can.view.Scope)) { updatedScope = scope.add(updatedScope); } if (updatedOptions !== undefined && !(updatedOptions instanceof can.view.Options)) { updatedOptions = options.add(updatedOptions); } return originalWithScope(updatedScope, updatedOptions || options); }; }; var Mustache = function (options, helpers) { if (this.constructor !== Mustache) { var mustache = new Mustache(options); return function (data, options) { return mustache.render(data, options); }; } if (typeof options === 'function') { this.template = { fn: options }; return; } can.extend(this, options); this.template = this.scanner.scan(this.text, this.name); }; can.Mustache = can.global.Mustache = Mustache; Mustache.prototype.render = function (data, options) { if (!(data instanceof can.view.Scope)) { data = new can.view.Scope(data || {}); } if (!(options instanceof can.view.Options)) { options = new can.view.Options(options || {}); } options = options || {}; return this.template.fn.call(data, data, options); }; can.extend(Mustache.prototype, { scanner: new can.view.Scanner({ text: { start: '', scope: SCOPE, options: ',options: options', argNames: ARG_NAMES }, tokens: [ [ 'returnLeft', '{{{', '{{[{&]' ], [ 'commentFull', '{{!}}', '^[\\s\\t]*{{!.+?}}\\n' ], [ 'commentLeft', '{{!', '(\\n[\\s\\t]*{{!|{{!)' ], [ 'escapeFull', '{{}}', '(^[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}$)', function (content) { return { before: /^\n.+?\n$/.test(content) ? '\n' : '', content: content.match(/\{\{(.+?)\}\}/)[1] || '' }; } ], [ 'escapeLeft', '{{' ], [ 'returnRight', '}}}' ], [ 'right', '}}' ] ], helpers: [ { name: /^>[\s]*\w*/, fn: function (content, cmd) { var templateName = can.trim(content.replace(/^>\s?/, '')).replace(/["|']/g, ''); return 'can.Mustache.renderPartial(\'' + templateName + '\',' + ARG_NAMES + ')'; } }, { name: /^\s*data\s/, fn: function (content, cmd) { var attr = content.match(/["|'](.*)["|']/)[1]; return 'can.proxy(function(__){' + 'can.data(can.$(__),\'' + attr + '\', this.attr(\'.\')); }, ' + SCOPE + ')'; } }, { name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/, fn: function (content) { var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/, parts = content.match(quickFunc); return 'can.proxy(function(__){var ' + parts[1] + '=can.$(__);with(' + SCOPE + '.attr(\'.\')){' + parts[2] + '}}, this);'; } }, { name: /^.*$/, fn: function (content, cmd) { var mode = false, result = { content: '', startTxt: false, startOnlyTxt: false, end: false }; content = can.trim(content); if (content.length && (mode = content.match(/^([#^/]|else$)/))) { mode = mode[0]; switch (mode) { case '#': case '^': if (cmd.specialAttribute) { result.startOnlyTxt = true; } else { result.startTxt = true; result.escaped = 0; } break; case '/': result.end = true; result.content += 'return ___v1ew.join("");}}])'; return result; } content = content.substring(1); } if (mode !== 'else') { var args = [], hashes = [], i = 0, m; result.content += 'can.Mustache.txt(\n' + (cmd.specialAttribute ? SPECIAL_CONTEXT_OBJ : CONTEXT_OBJ) + ',\n' + (mode ? '"' + mode + '"' : 'null') + ','; (can.trim(content) + ' ').replace(argumentsRegExp, function (whole, arg) { if (i && (m = arg.match(literalNumberStringBooleanRegExp))) { if (m[2]) { args.push(m[0]); } else { hashes.push(m[4] + ':' + (m[6] ? m[6] : makeLookupLiteral(m[5]))); } } else { args.push(makeLookupLiteral(arg)); } i++; }); result.content += args.join(','); if (hashes.length) { result.content += ',{' + HASH + ':{' + hashes.join(',') + '}}'; } } if (mode && mode !== 'else') { result.content += ',[\n\n'; } switch (mode) { case '^': case '#': result.content += '{fn:function(' + ARG_NAMES + '){var ___v1ew = [];'; break; case 'else': result.content += 'return ___v1ew.join("");}},\n{inverse:function(' + ARG_NAMES + '){\nvar ___v1ew = [];'; break; default: result.content += ')'; break; } if (!mode) { result.startTxt = true; result.end = true; } return result; } } ] }) }); var helpers = can.view.Scanner.prototype.helpers; for (var i = 0; i < helpers.length; i++) { Mustache.prototype.scanner.helpers.unshift(helpers[i]); } Mustache.txt = function (scopeAndOptions, mode, name) { var scope = scopeAndOptions.scope, options = scopeAndOptions.options, args = [], helperOptions = { fn: function () { }, inverse: function () { } }, hash, context = scope.attr('.'), getHelper = true, helper; for (var i = 3; i < arguments.length; i++) { var arg = arguments[i]; if (mode && can.isArray(arg)) { helperOptions = can.extend.apply(can, [helperOptions].concat(arg)); } else if (arg && arg[HASH]) { hash = arg[HASH]; for (var prop in hash) { if (isLookup(hash[prop])) { hash[prop] = Mustache.get(hash[prop].get, scopeAndOptions, false, true); } } } else if (arg && isLookup(arg)) { args.push(Mustache.get(arg.get, scopeAndOptions, false, true, true)); } else { args.push(arg); } } if (isLookup(name)) { var get = name.get; name = Mustache.get(name.get, scopeAndOptions, args.length, false); getHelper = get === name; } helperOptions.fn = makeConvertToScopes(helperOptions.fn, scope, options); helperOptions.inverse = makeConvertToScopes(helperOptions.inverse, scope, options); if (mode === '^') { var tmp = helperOptions.fn; helperOptions.fn = helperOptions.inverse; helperOptions.inverse = tmp; } if (helper = getHelper && (typeof name === 'string' && Mustache.getHelper(name, options)) || can.isFunction(name) && !name.isComputed && { fn: name }) { can.extend(helperOptions, { context: context, scope: scope, contexts: scope, hash: hash }); args.push(helperOptions); return function () { var result = helper.fn.apply(context, args); return result == null ? '' : result; }; } return function () { var value; if (can.isFunction(name) && name.isComputed) { value = name(); } else { value = name; } var validArgs = args.length ? args : [value], valid = true, result = [], i, argIsObserve, arg; if (mode) { for (i = 0; i < validArgs.length; i++) { arg = validArgs[i]; argIsObserve = typeof arg !== 'undefined' && isObserveLike(arg); if (isArrayLike(arg)) { if (mode === '#') { valid = valid && !!(argIsObserve ? arg.attr('length') : arg.length); } else if (mode === '^') { valid = valid && !(argIsObserve ? arg.attr('length') : arg.length); } } else { valid = mode === '#' ? valid && !!arg : mode === '^' ? valid && !arg : valid; } } } if (valid) { if (mode === '#') { if (isArrayLike(value)) { var isObserveList = isObserveLike(value); for (i = 0; i < value.length; i++) { result.push(helperOptions.fn(isObserveList ? value.attr('' + i) : value[i])); } return result.join(''); } else { return helperOptions.fn(value || {}) || ''; } } else if (mode === '^') { return helperOptions.inverse(value || {}) || ''; } else { return '' + (value != null ? value : ''); } } return ''; }; }; Mustache.get = function (key, scopeAndOptions, isHelper, isArgument, isLookup) { var context = scopeAndOptions.scope.attr('.'), options = scopeAndOptions.options || {}; if (isHelper) { if (Mustache.getHelper(key, options)) { return key; } if (scopeAndOptions.scope && can.isFunction(context[key])) { return context[key]; } } var computeData = scopeAndOptions.scope.computeData(key, { isArgument: isArgument, args: [ context, scopeAndOptions.scope ] }), compute = computeData.compute; can.compute.temporarilyBind(compute); var initialValue = computeData.initialValue, helperObj = Mustache.getHelper(key, options); if (!isLookup && (initialValue === undefined || computeData.scope !== scopeAndOptions.scope) && Mustache.getHelper(key, options)) { return key; } if (!compute.computeInstance.hasDependencies) { return initialValue; } else { return compute; } }; Mustache.resolve = function (value) { if (isObserveLike(value) && isArrayLike(value) && value.attr('length')) { return value; } else if (can.isFunction(value)) { return value(); } else { return value; } }; Mustache._helpers = {}; Mustache.registerHelper = function (name, fn) { this._helpers[name] = { name: name, fn: fn }; }; Mustache.registerSimpleHelper = function (name, fn) { Mustache.registerHelper(name, can.view.simpleHelper(fn)); }; Mustache.getHelper = function (name, options) { var helper; if (options) { helper = options.get('helpers.' + name, { proxyMethods: false }); } return helper ? { fn: helper } : this._helpers[name]; }; Mustache.render = function (partial, scope, options) { if (!can.view.cached[partial]) { can.__notObserve(function () { var scopePartialName = scope.attr(partial); if (scopePartialName) { partial = scopePartialName; } })(); } return can.view.render(partial, scope, options); }; Mustache.safeString = function (str) { return { toString: function () { return str; } }; }; Mustache.renderPartial = function (partialName, scope, options) { var partial = options.get('partials.' + partialName, { proxyMethods: false }); if (partial) { return partial.render ? partial.render(scope, options) : partial(scope, options); } else { return can.Mustache.render(partialName, scope, options); } }; can.each({ 'if': function (expr, options) { var value; if (can.isFunction(expr)) { value = can.compute.truthy(expr)(); } else { value = !!Mustache.resolve(expr); } if (value) { return options.fn(options.contexts || this); } else { return options.inverse(options.contexts || this); } }, 'is': function () { var lastValue, curValue, options = arguments[arguments.length - 1]; if (arguments.length - 2 <= 0) { return options.inverse(); } for (var i = 0; i < arguments.length - 1; i++) { curValue = Mustache.resolve(arguments[i]); curValue = can.isFunction(curValue) ? curValue() : curValue; if (i > 0) { if (curValue !== lastValue) { return options.inverse(); } } lastValue = curValue; } return options.fn(); }, 'eq': function () { return Mustache._helpers.is.fn.apply(this, arguments); }, 'unless': function (expr, options) { return Mustache._helpers['if'].fn.apply(this, [ expr, can.extend({}, options, { fn: options.inverse, inverse: options.fn }) ]); }, 'each': function (expr, options) { var resolved = Mustache.resolve(expr), result = [], keys, key, i; if (can.view.lists && (resolved instanceof can.List || expr && expr.isComputed && resolved === undefined)) { return can.view.lists(expr, function (item, index) { return options.fn(options.scope.add({ '@index': index }).add(item)); }); } expr = resolved; if (!!expr && isArrayLike(expr)) { for (i = 0; i < expr.length; i++) { result.push(options.fn(options.scope.add({ '@index': i }).add(expr[i]))); } return result.join(''); } else if (isObserveLike(expr)) { keys = can.Map.keys(expr); for (i = 0; i < keys.length; i++) { key = keys[i]; result.push(options.fn(options.scope.add({ '@key': key }).add(expr[key]))); } return result.join(''); } else if (expr instanceof Object) { for (key in expr) { result.push(options.fn(options.scope.add({ '@key': key }).add(expr[key]))); } return result.join(''); } }, 'with': function (expr, options) { var ctx = expr; expr = Mustache.resolve(expr); if (!!expr) { return options.fn(ctx); } }, 'log': function (expr, options) { if (typeof console !== 'undefined' && console.log) { if (!options) { console.log(expr.context); } else { console.log(expr, options.context); } } }, '@index': function (offset, options) { if (!options) { options = offset; offset = 0; } var index = options.scope.read('@index', { isArgument: true }).value; return '' + ((can.isFunction(index) ? index() : index) + offset); } }, function (fn, name) { Mustache.registerHelper(name, fn); }); can.view.register({ suffix: 'mustache', contentType: 'x-mustache-template', script: function (id, src) { return 'can.Mustache(function(' + ARG_NAMES + ') { ' + new Mustache({ text: src, name: id }).template.out + ' })'; }, renderer: function (id, text) { return Mustache({ text: text, name: id }); } }); can.mustache.registerHelper = can.proxy(can.Mustache.registerHelper, can.Mustache); can.mustache.safeString = can.Mustache.safeString; return can; });