UNPKG

can

Version:

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

292 lines (291 loc) 11.5 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/stache/stache*/ var can = require('../../util/util.js'); var parser = require('../parser/parser.js'); var target = require('../target/target.js'); var HTMLSectionBuilder = require('./html_section.js'); var TextSectionBuilder = require('./text_section.js'); var mustacheCore = require('./mustache_core.js'); var mustacheHelpers = require('./mustache_helpers.js'); var getIntermediateAndImports = require('./intermediate_and_imports.js'); var viewCallbacks = require('../callbacks/callbacks.js'); require('../bindings/bindings.js'); parser = parser || can.view.parser; can.view.parser = parser; viewCallbacks = viewCallbacks || can.view.callbacks; var svgNamespace = 'http://www.w3.org/2000/svg'; var namespaces = { 'svg': svgNamespace, 'g': svgNamespace }, textContentOnlyTag = { style: true, script: true }; function stache(template) { if (typeof template === 'string') { template = mustacheCore.cleanLineEndings(template); } var section = new HTMLSectionBuilder(), state = { node: null, attr: null, sectionElementStack: [], text: false, namespaceStack: [], textContentOnly: null }, makeRendererAndUpdateSection = function (section, mode, stache) { if (mode === '>') { section.add(mustacheCore.makeLiveBindingPartialRenderer(stache, copyState())); } else if (mode === '/') { section.endSection(); if (section instanceof HTMLSectionBuilder) { state.sectionElementStack.pop(); } } else if (mode === 'else') { section.inverse(); } else { var makeRenderer = section instanceof HTMLSectionBuilder ? mustacheCore.makeLiveBindingBranchRenderer : mustacheCore.makeStringBranchRenderer; if (mode === '{' || mode === '&') { section.add(makeRenderer(null, stache, copyState())); } else if (mode === '#' || mode === '^') { section.startSection(makeRenderer(mode, stache, copyState())); if (section instanceof HTMLSectionBuilder) { state.sectionElementStack.push('section'); } } else { section.add(makeRenderer(null, stache, copyState({ text: true }))); } } }, copyState = function (overwrites) { var lastElement = state.sectionElementStack[state.sectionElementStack.length - 1]; var cur = { tag: state.node && state.node.tag, attr: state.attr && state.attr.name, directlyNested: state.sectionElementStack.length ? lastElement === 'section' || lastElement === 'custom' : true, textContentOnly: !!state.textContentOnly }; return overwrites ? can.simpleExtend(cur, overwrites) : cur; }, addAttributesCallback = function (node, callback) { if (!node.attributes) { node.attributes = []; } node.attributes.unshift(callback); }; parser(template, { start: function (tagName, unary) { var matchedNamespace = namespaces[tagName]; if (matchedNamespace && !unary) { state.namespaceStack.push(matchedNamespace); } state.node = { tag: tagName, children: [], namespace: matchedNamespace || can.last(state.namespaceStack) }; }, end: function (tagName, unary) { var isCustomTag = viewCallbacks.tag(tagName); if (unary) { section.add(state.node); if (isCustomTag) { addAttributesCallback(state.node, function (scope, options, parentNodeList) { viewCallbacks.tagHandler(this, tagName, { scope: scope, options: options, subtemplate: null, templateType: 'stache', parentNodeList: parentNodeList }); }); } } else { section.push(state.node); state.sectionElementStack.push(isCustomTag ? 'custom' : tagName); if (isCustomTag) { section.startSubSection(); } else if (textContentOnlyTag[tagName]) { state.textContentOnly = new TextSectionBuilder(); } } state.node = null; }, close: function (tagName) { var matchedNamespace = namespaces[tagName]; if (matchedNamespace) { state.namespaceStack.pop(); } var isCustomTag = viewCallbacks.tag(tagName), renderer; if (isCustomTag) { renderer = section.endSubSectionAndReturnRenderer(); } if (textContentOnlyTag[tagName]) { section.last().add(state.textContentOnly.compile(copyState())); state.textContentOnly = null; } var oldNode = section.pop(); if (isCustomTag) { addAttributesCallback(oldNode, function (scope, options, parentNodeList) { viewCallbacks.tagHandler(this, tagName, { scope: scope, options: options, subtemplate: renderer, templateType: 'stache', parentNodeList: parentNodeList }); }); } state.sectionElementStack.pop(); }, attrStart: function (attrName) { if (state.node.section) { state.node.section.add(attrName + '="'); } else { state.attr = { name: attrName, value: '' }; } }, attrEnd: function (attrName) { if (state.node.section) { state.node.section.add('" '); } else { if (!state.node.attrs) { state.node.attrs = {}; } state.node.attrs[state.attr.name] = state.attr.section ? state.attr.section.compile(copyState()) : state.attr.value; var attrCallback = viewCallbacks.attr(attrName); if (attrCallback) { if (!state.node.attributes) { state.node.attributes = []; } state.node.attributes.push(function (scope, options, nodeList) { attrCallback(this, { attributeName: attrName, scope: scope, options: options, nodeList: nodeList }); }); } state.attr = null; } }, attrValue: function (value) { var section = state.node.section || state.attr.section; if (section) { section.add(value); } else { state.attr.value += value; } }, chars: function (text) { (state.textContentOnly || section).add(text); }, special: function (text) { var firstAndText = mustacheCore.splitModeFromExpression(text, state), mode = firstAndText.mode, expression = firstAndText.expression; if (expression === 'else') { var inverseSection; if (state.attr && state.attr.section) { inverseSection = state.attr.section; } else if (state.node && state.node.section) { inverseSection = state.node.section; } else { inverseSection = state.textContentOnly || section; } inverseSection.inverse(); return; } if (mode === '!') { return; } if (state.node && state.node.section) { makeRendererAndUpdateSection(state.node.section, mode, expression); if (state.node.section.subSectionDepth() === 0) { state.node.attributes.push(state.node.section.compile(copyState())); delete state.node.section; } } else if (state.attr) { if (!state.attr.section) { state.attr.section = new TextSectionBuilder(); if (state.attr.value) { state.attr.section.add(state.attr.value); } } makeRendererAndUpdateSection(state.attr.section, mode, expression); } else if (state.node) { if (!state.node.attributes) { state.node.attributes = []; } if (!mode) { state.node.attributes.push(mustacheCore.makeLiveBindingBranchRenderer(null, expression, copyState())); } else if (mode === '#' || mode === '^') { if (!state.node.section) { state.node.section = new TextSectionBuilder(); } makeRendererAndUpdateSection(state.node.section, mode, expression); } else { throw new Error(mode + ' is currently not supported within a tag.'); } } else { makeRendererAndUpdateSection(state.textContentOnly || section, mode, expression); } }, comment: function (text) { section.add({ comment: text }); }, done: function () { } }); return section.compile(); } var escMap = { '\n': '\\n', '\r': '\\r', '\u2028': '\\u2028', '\u2029': '\\u2029' }; var esc = function (string) { return ('' + string).replace(/["'\\\n\r\u2028\u2029]/g, function (character) { if ('\'"\\'.indexOf(character) >= 0) { return '\\' + character; } else { return escMap[character]; } }); }; can.view.register({ suffix: 'stache', contentType: 'x-stache-template', fragRenderer: function (id, text) { return stache(text); }, script: function (id, src) { return 'can.stache("' + esc(src) + '")'; } }); can.view.ext = '.stache'; can.extend(can.stache, mustacheHelpers); can.extend(stache, mustacheHelpers); can.stache.safeString = stache.safeString = function (text) { return { toString: function () { return text; } }; }; can.stache.async = function (source) { var iAi = getIntermediateAndImports(source); var importPromises = can.map(iAi.imports, function (moduleName) { return can['import'](moduleName); }); return can.when.apply(can, importPromises).then(function () { return stache(iAi.intermediate); }); }; module.exports = stache;