can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
384 lines (383 loc) • 18.9 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/scanner*/
define([
'can/view',
'can/elements',
'can/view/callbacks'
], function (can, elements, viewCallbacks) {
var newLine = /(\r|\n)+/g, notEndTag = /\//, clean = function (content) {
return content.split('\\').join('\\\\').split('\n').join('\\n').split('"').join('\\"').split('\t').join('\\t');
}, getTag = function (tagName, tokens, i) {
if (tagName) {
return tagName;
} else {
while (i < tokens.length) {
if (tokens[i] === '<' && !notEndTag.test(tokens[i + 1])) {
return elements.reverseTagMap[tokens[i + 1]] || 'span';
}
i++;
}
}
return '';
}, bracketNum = function (content) {
return --content.split('{').length - --content.split('}').length;
}, myEval = function (script) {
eval(script);
}, attrReg = /([^\s]+)[\s]*=[\s]*$/, startTxt = 'var ___v1ew = [];', finishTxt = 'return ___v1ew.join(\'\')', put_cmd = '___v1ew.push(\n', insert_cmd = put_cmd, htmlTag = null, quote = null, beforeQuote = null, rescan = null, getAttrName = function () {
var matches = beforeQuote.match(attrReg);
return matches && matches[1];
}, status = function () {
return quote ? '\'' + getAttrName() + '\'' : htmlTag ? 1 : 0;
}, top = function (stack) {
return stack[stack.length - 1];
}, Scanner;
can.view.Scanner = Scanner = function (options) {
can.extend(this, {
text: {},
tokens: []
}, options);
this.text.options = this.text.options || '';
this.tokenReg = [];
this.tokenSimple = {
'<': '<',
'>': '>',
'"': '"',
'\'': '\''
};
this.tokenComplex = [];
this.tokenMap = {};
for (var i = 0, token; token = this.tokens[i]; i++) {
if (token[2]) {
this.tokenReg.push(token[2]);
this.tokenComplex.push({
abbr: token[1],
re: new RegExp(token[2]),
rescan: token[3]
});
} else {
this.tokenReg.push(token[1]);
this.tokenSimple[token[1]] = token[0];
}
this.tokenMap[token[0]] = token[1];
}
this.tokenReg = new RegExp('(' + this.tokenReg.slice(0).concat([
'<',
'>',
'"',
'\''
]).join('|') + ')', 'g');
};
Scanner.prototype = {
helpers: [],
scan: function (source, name) {
var tokens = [], last = 0, simple = this.tokenSimple, complex = this.tokenComplex;
source = source.replace(newLine, '\n');
if (this.transform) {
source = this.transform(source);
}
source.replace(this.tokenReg, function (whole, part) {
var offset = arguments[arguments.length - 2];
if (offset > last) {
tokens.push(source.substring(last, offset));
}
if (simple[whole]) {
tokens.push(whole);
} else {
for (var i = 0, token; token = complex[i]; i++) {
if (token.re.test(whole)) {
tokens.push(token.abbr);
if (token.rescan) {
tokens.push(token.rescan(part));
}
break;
}
}
}
last = offset + part.length;
});
if (last < source.length) {
tokens.push(source.substr(last));
}
var content = '', buff = [startTxt + (this.text.start || '')], put = function (content, bonus) {
buff.push(put_cmd, '"', clean(content), '"' + (bonus || '') + ');');
}, endStack = [], lastToken, startTag = null, magicInTag = false, specialStates = {
attributeHookups: [],
tagHookups: [],
lastTagHookup: ''
}, popTagHookup = function () {
specialStates.lastTagHookup = specialStates.tagHookups.pop() + specialStates.tagHookups.length;
}, tagName = '', tagNames = [], popTagName = false, bracketCount, specialAttribute = false, i = 0, token, tmap = this.tokenMap, attrName;
htmlTag = quote = beforeQuote = null;
for (; (token = tokens[i++]) !== undefined;) {
if (startTag === null) {
switch (token) {
case tmap.left:
case tmap.escapeLeft:
case tmap.returnLeft:
magicInTag = htmlTag && 1;
case tmap.commentLeft:
startTag = token;
if (content.length) {
put(content);
}
content = '';
break;
case tmap.escapeFull:
magicInTag = htmlTag && 1;
rescan = 1;
startTag = tmap.escapeLeft;
if (content.length) {
put(content);
}
rescan = tokens[i++];
content = rescan.content || rescan;
if (rescan.before) {
put(rescan.before);
}
tokens.splice(i, 0, tmap.right);
break;
case tmap.commentFull:
break;
case tmap.templateLeft:
content += tmap.left;
break;
case '<':
if (tokens[i].indexOf('!--') !== 0) {
htmlTag = 1;
magicInTag = 0;
}
content += token;
break;
case '>':
htmlTag = 0;
var emptyElement = content.substr(content.length - 1) === '/' || content.substr(content.length - 2) === '--', attrs = '';
if (specialStates.attributeHookups.length) {
attrs = 'attrs: [\'' + specialStates.attributeHookups.join('\',\'') + '\'], ';
specialStates.attributeHookups = [];
}
if (tagName + specialStates.tagHookups.length !== specialStates.lastTagHookup && tagName === top(specialStates.tagHookups)) {
if (emptyElement) {
content = content.substr(0, content.length - 1);
}
buff.push(put_cmd, '"', clean(content), '"', ',can.view.pending({tagName:\'' + tagName + '\',' + attrs + 'scope: ' + (this.text.scope || 'this') + this.text.options);
if (emptyElement) {
buff.push('}));');
content = '/>';
popTagHookup();
} else if (tokens[i] === '<' && tokens[i + 1] === '/' + tagName) {
buff.push('}));');
content = token;
popTagHookup();
} else {
buff.push(',subtemplate: function(' + this.text.argNames + '){\n' + startTxt + (this.text.start || ''));
content = '';
}
} else if (magicInTag || !popTagName && elements.tagToContentPropMap[tagNames[tagNames.length - 1]] || attrs) {
var pendingPart = ',can.view.pending({' + attrs + 'scope: ' + (this.text.scope || 'this') + this.text.options + '}),"';
if (emptyElement) {
put(content.substr(0, content.length - 1), pendingPart + '/>"');
} else {
put(content, pendingPart + '>"');
}
content = '';
magicInTag = 0;
} else {
content += token;
}
if (emptyElement || popTagName) {
tagNames.pop();
tagName = tagNames[tagNames.length - 1];
popTagName = false;
}
specialStates.attributeHookups = [];
break;
case '\'':
case '"':
if (htmlTag) {
if (quote && quote === token) {
quote = null;
var attr = getAttrName();
if (viewCallbacks.attr(attr)) {
specialStates.attributeHookups.push(attr);
}
if (specialAttribute) {
content += token;
put(content);
buff.push(finishTxt, '}));\n');
content = '';
specialAttribute = false;
break;
}
} else if (quote === null) {
quote = token;
beforeQuote = lastToken;
attrName = getAttrName();
if (tagName === 'img' && attrName === 'src' || attrName === 'style') {
put(content.replace(attrReg, ''));
content = '';
specialAttribute = true;
buff.push(insert_cmd, 'can.view.txt(2,\'' + getTag(tagName, tokens, i) + '\',' + status() + ',this,function(){', startTxt);
put(attrName + '=' + token);
break;
}
}
}
default:
if (lastToken === '<') {
tagName = token.substr(0, 3) === '!--' ? '!--' : token.split(/\s/)[0];
var isClosingTag = false, cleanedTagName;
if (tagName.indexOf('/') === 0) {
isClosingTag = true;
cleanedTagName = tagName.substr(1);
}
if (isClosingTag) {
if (top(tagNames) === cleanedTagName) {
tagName = cleanedTagName;
popTagName = true;
}
if (top(specialStates.tagHookups) === cleanedTagName) {
put(content.substr(0, content.length - 1));
buff.push(finishTxt + '}}) );');
content = '><';
popTagHookup();
}
} else {
if (tagName.lastIndexOf('/') === tagName.length - 1) {
tagName = tagName.substr(0, tagName.length - 1);
}
if (tagName !== '!--' && viewCallbacks.tag(tagName)) {
if (tagName === 'content' && elements.tagMap[top(tagNames)]) {
token = token.replace('content', elements.tagMap[top(tagNames)]);
}
specialStates.tagHookups.push(tagName);
}
tagNames.push(tagName);
}
}
content += token;
break;
}
} else {
switch (token) {
case tmap.right:
case tmap.returnRight:
switch (startTag) {
case tmap.left:
bracketCount = bracketNum(content);
if (bracketCount === 1) {
buff.push(insert_cmd, 'can.view.txt(0,\'' + getTag(tagName, tokens, i) + '\',' + status() + ',this,function(){', startTxt, content);
endStack.push({
before: '',
after: finishTxt + '}));\n'
});
} else {
last = endStack.length && bracketCount === -1 ? endStack.pop() : { after: ';' };
if (last.before) {
buff.push(last.before);
}
buff.push(content, ';', last.after);
}
break;
case tmap.escapeLeft:
case tmap.returnLeft:
bracketCount = bracketNum(content);
if (bracketCount) {
endStack.push({
before: finishTxt,
after: '}));\n'
});
}
var escaped = startTag === tmap.escapeLeft ? 1 : 0, commands = {
insert: insert_cmd,
tagName: getTag(tagName, tokens, i),
status: status(),
specialAttribute: specialAttribute
};
for (var ii = 0; ii < this.helpers.length; ii++) {
var helper = this.helpers[ii];
if (helper.name.test(content)) {
content = helper.fn(content, commands);
if (helper.name.source === /^>[\s]*\w*/.source) {
escaped = 0;
}
break;
}
}
if (typeof content === 'object') {
if (content.startTxt && content.end && specialAttribute) {
buff.push(insert_cmd, 'can.view.toStr( ', content.content, '() ) );');
} else {
if (content.startTxt) {
buff.push(insert_cmd, 'can.view.txt(\n' + (typeof status() === 'string' || (content.escaped != null ? content.escaped : escaped)) + ',\n\'' + tagName + '\',\n' + status() + ',\nthis,\n');
} else if (content.startOnlyTxt) {
buff.push(insert_cmd, 'can.view.onlytxt(this,\n');
}
buff.push(content.content);
if (content.end) {
buff.push('));');
}
}
} else if (specialAttribute) {
buff.push(insert_cmd, content, ');');
} else {
buff.push(insert_cmd, 'can.view.txt(\n' + (typeof status() === 'string' || escaped) + ',\n\'' + tagName + '\',\n' + status() + ',\nthis,\nfunction(){ ' + (this.text.escape || '') + 'return ', content, bracketCount ? startTxt : '}));\n');
}
if (rescan && rescan.after && rescan.after.length) {
put(rescan.after.length);
rescan = null;
}
break;
}
startTag = null;
content = '';
break;
case tmap.templateLeft:
content += tmap.left;
break;
default:
content += token;
break;
}
}
lastToken = token;
}
if (content.length) {
put(content);
}
buff.push(';');
var template = buff.join(''), out = { out: (this.text.outStart || '') + template + ' ' + finishTxt + (this.text.outEnd || '') };
myEval.call(out, 'this.fn = (function(' + this.text.argNames + '){' + out.out + '});\r\n//# sourceURL=' + name + '.js');
return out;
}
};
can.view.pending = function (viewData) {
var hooks = can.view.getHooks();
return can.view.hook(function (el) {
can.each(hooks, function (fn) {
fn(el);
});
viewData.templateType = 'legacy';
if (viewData.tagName) {
viewCallbacks.tagHandler(el, viewData.tagName, viewData);
}
can.each(viewData && viewData.attrs || [], function (attributeName) {
viewData.attributeName = attributeName;
var callback = viewCallbacks.attr(attributeName);
if (callback) {
callback(el, viewData);
}
});
});
};
can.view.tag('content', function (el, tagData) {
return tagData.scope;
});
can.view.Scanner = Scanner;
return Scanner;
});