can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
295 lines (294 loc) • 12.5 kB
JavaScript
/*!
* 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*/
define([
'can/util/library',
'can/view/parser',
'can/view/target',
'can/view/html_section',
'can/view/text_section',
'can/view/mustache_core',
'can/view/mustache_helpers',
'can/view/intermediate_and_imports',
'can/view/callbacks',
'can/view/bindings'
], function (can, parser, target, HTMLSectionBuilder, TextSectionBuilder, mustacheCore, mustacheHelpers, getIntermediateAndImports, viewCallbacks) {
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);
});
};
return stache;
});