559 lines
13 KiB
JavaScript
559 lines
13 KiB
JavaScript
|
|
/**
|
|
* The riot template engine
|
|
* @version v1.0.0
|
|
*/
|
|
|
|
var skipRegex = (function () { //eslint-disable-line no-unused-vars
|
|
|
|
var beforeReChars = '[{(,;:?=|&!^~>%*/'
|
|
|
|
var beforeReWords = [
|
|
'case',
|
|
'default',
|
|
'do',
|
|
'else',
|
|
'in',
|
|
'instanceof',
|
|
'prefix',
|
|
'return',
|
|
'typeof',
|
|
'void',
|
|
'yield'
|
|
]
|
|
|
|
var wordsLastChar = beforeReWords.reduce(function (s, w) {
|
|
return s + w.slice(-1)
|
|
}, '')
|
|
|
|
var RE_REGEX = /^\/(?=[^*>/])[^[/\\]*(?:(?:\\.|\[(?:\\.|[^\]\\]*)*\])[^[\\/]*)*?\/[gimuy]*/
|
|
var RE_VN_CHAR = /[$\w]/
|
|
|
|
function prev (code, pos) {
|
|
while (--pos >= 0 && /\s/.test(code[pos]));
|
|
return pos
|
|
}
|
|
|
|
function _skipRegex (code, start) {
|
|
|
|
var re = /.*/g
|
|
var pos = re.lastIndex = start++
|
|
var match = re.exec(code)[0].match(RE_REGEX)
|
|
|
|
if (match) {
|
|
var next = pos + match[0].length
|
|
|
|
pos = prev(code, pos)
|
|
var c = code[pos]
|
|
|
|
if (pos < 0 || ~beforeReChars.indexOf(c)) {
|
|
return next
|
|
}
|
|
|
|
if (c === '.') {
|
|
|
|
if (code[pos - 1] === '.') {
|
|
start = next
|
|
}
|
|
|
|
} else if (c === '+' || c === '-') {
|
|
|
|
if (code[--pos] !== c ||
|
|
(pos = prev(code, pos)) < 0 ||
|
|
!RE_VN_CHAR.test(code[pos])) {
|
|
start = next
|
|
}
|
|
|
|
} else if (~wordsLastChar.indexOf(c)) {
|
|
|
|
var end = pos + 1
|
|
|
|
while (--pos >= 0 && RE_VN_CHAR.test(code[pos]));
|
|
if (~beforeReWords.indexOf(code.slice(pos + 1, end))) {
|
|
start = next
|
|
}
|
|
}
|
|
}
|
|
|
|
return start
|
|
}
|
|
|
|
return _skipRegex
|
|
|
|
})()
|
|
|
|
/**
|
|
* riot.util.brackets
|
|
*
|
|
* - `brackets ` - Returns a string or regex based on its parameter
|
|
* - `brackets.set` - Change the current riot brackets
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
/* global riot */
|
|
|
|
export
|
|
var brackets = (function (UNDEF) {
|
|
|
|
var
|
|
REGLOB = 'g',
|
|
|
|
R_MLCOMMS = /\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//g,
|
|
|
|
R_STRINGS = /"[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|`[^`\\]*(?:\\[\S\s][^`\\]*)*`/g,
|
|
|
|
S_QBLOCKS = R_STRINGS.source + '|' +
|
|
/(?:\breturn\s+|(?:[$\w\)\]]|\+\+|--)\s*(\/)(?![*\/]))/.source + '|' +
|
|
/\/(?=[^*\/])[^[\/\\]*(?:(?:\[(?:\\.|[^\]\\]*)*\]|\\.)[^[\/\\]*)*?([^<]\/)[gim]*/.source,
|
|
|
|
UNSUPPORTED = RegExp('[\\' + 'x00-\\x1F<>a-zA-Z0-9\'",;\\\\]'),
|
|
|
|
NEED_ESCAPE = /(?=[[\]()*+?.^$|])/g,
|
|
|
|
S_QBLOCK2 = R_STRINGS.source + '|' + /(\/)(?![*\/])/.source,
|
|
|
|
FINDBRACES = {
|
|
'(': RegExp('([()])|' + S_QBLOCK2, REGLOB),
|
|
'[': RegExp('([[\\]])|' + S_QBLOCK2, REGLOB),
|
|
'{': RegExp('([{}])|' + S_QBLOCK2, REGLOB)
|
|
},
|
|
|
|
DEFAULT = '{ }'
|
|
|
|
var _pairs = [
|
|
'{', '}',
|
|
'{', '}',
|
|
/{[^}]*}/,
|
|
/\\([{}])/g,
|
|
/\\({)|{/g,
|
|
RegExp('\\\\(})|([[({])|(})|' + S_QBLOCK2, REGLOB),
|
|
DEFAULT,
|
|
/^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S.*)\s*}/,
|
|
/(^|[^\\]){=[\S\s]*?}/
|
|
]
|
|
|
|
var
|
|
cachedBrackets = UNDEF,
|
|
_regex,
|
|
_cache = [],
|
|
_settings
|
|
|
|
function _loopback (re) { return re }
|
|
|
|
function _rewrite (re, bp) {
|
|
if (!bp) bp = _cache
|
|
return new RegExp(
|
|
re.source.replace(/{/g, bp[2]).replace(/}/g, bp[3]), re.global ? REGLOB : ''
|
|
)
|
|
}
|
|
|
|
function _create (pair) {
|
|
if (pair === DEFAULT) return _pairs
|
|
|
|
var arr = pair.split(' ')
|
|
|
|
if (arr.length !== 2 || UNSUPPORTED.test(pair)) {
|
|
throw new Error('Unsupported brackets "' + pair + '"')
|
|
}
|
|
arr = arr.concat(pair.replace(NEED_ESCAPE, '\\').split(' '))
|
|
|
|
arr[4] = _rewrite(arr[1].length > 1 ? /{[\S\s]*?}/ : _pairs[4], arr)
|
|
arr[5] = _rewrite(pair.length > 3 ? /\\({|})/g : _pairs[5], arr)
|
|
arr[6] = _rewrite(_pairs[6], arr)
|
|
arr[7] = RegExp('\\\\(' + arr[3] + ')|([[({])|(' + arr[3] + ')|' + S_QBLOCK2, REGLOB)
|
|
arr[8] = pair
|
|
return arr
|
|
}
|
|
|
|
function _brackets (reOrIdx) {
|
|
return reOrIdx instanceof RegExp ? _regex(reOrIdx) : _cache[reOrIdx]
|
|
}
|
|
|
|
_brackets.split = function split (str, tmpl, _bp) {
|
|
// istanbul ignore next: _bp is for the compiler
|
|
if (!_bp) _bp = _cache
|
|
|
|
var
|
|
parts = [],
|
|
match,
|
|
isexpr,
|
|
start,
|
|
pos,
|
|
re = _bp[6]
|
|
|
|
var qblocks = []
|
|
var prevStr = ''
|
|
var mark, lastIndex
|
|
|
|
isexpr = start = re.lastIndex = 0
|
|
|
|
while ((match = re.exec(str))) {
|
|
|
|
lastIndex = re.lastIndex
|
|
pos = match.index
|
|
|
|
if (isexpr) {
|
|
|
|
if (match[2]) {
|
|
|
|
var ch = match[2]
|
|
var rech = FINDBRACES[ch]
|
|
var ix = 1
|
|
|
|
rech.lastIndex = lastIndex
|
|
while ((match = rech.exec(str))) {
|
|
if (match[1]) {
|
|
if (match[1] === ch) ++ix
|
|
else if (!--ix) break
|
|
} else {
|
|
rech.lastIndex = pushQBlock(match.index, rech.lastIndex, match[2])
|
|
}
|
|
}
|
|
re.lastIndex = ix ? str.length : rech.lastIndex
|
|
continue
|
|
}
|
|
|
|
if (!match[3]) {
|
|
re.lastIndex = pushQBlock(pos, lastIndex, match[4])
|
|
continue
|
|
}
|
|
}
|
|
|
|
if (!match[1]) {
|
|
unescapeStr(str.slice(start, pos))
|
|
start = re.lastIndex
|
|
re = _bp[6 + (isexpr ^= 1)]
|
|
re.lastIndex = start
|
|
}
|
|
}
|
|
|
|
if (str && start < str.length) {
|
|
unescapeStr(str.slice(start))
|
|
}
|
|
|
|
parts.qblocks = qblocks
|
|
|
|
return parts
|
|
|
|
function unescapeStr (s) {
|
|
if (prevStr) {
|
|
s = prevStr + s
|
|
prevStr = ''
|
|
}
|
|
if (tmpl || isexpr) {
|
|
parts.push(s && s.replace(_bp[5], '$1'))
|
|
} else {
|
|
parts.push(s)
|
|
}
|
|
}
|
|
|
|
function pushQBlock(_pos, _lastIndex, slash) { //eslint-disable-line
|
|
if (slash) {
|
|
_lastIndex = skipRegex(str, _pos)
|
|
}
|
|
|
|
if (tmpl && _lastIndex > _pos + 2) {
|
|
mark = '\u2057' + qblocks.length + '~'
|
|
qblocks.push(str.slice(_pos, _lastIndex))
|
|
prevStr += str.slice(start, _pos) + mark
|
|
start = _lastIndex
|
|
}
|
|
return _lastIndex
|
|
}
|
|
}
|
|
|
|
_brackets.hasExpr = function hasExpr (str) {
|
|
return _cache[4].test(str)
|
|
}
|
|
|
|
_brackets.loopKeys = function loopKeys (expr) {
|
|
var m = expr.match(_cache[9])
|
|
|
|
return m
|
|
? { key: m[1], pos: m[2], val: _cache[0] + m[3].trim() + _cache[1] }
|
|
: { val: expr.trim() }
|
|
}
|
|
|
|
_brackets.array = function array (pair) {
|
|
return pair ? _create(pair) : _cache
|
|
}
|
|
|
|
function _reset (pair) {
|
|
if ((pair || (pair = DEFAULT)) !== _cache[8]) {
|
|
_cache = _create(pair)
|
|
_regex = pair === DEFAULT ? _loopback : _rewrite
|
|
_cache[9] = _regex(_pairs[9])
|
|
}
|
|
cachedBrackets = pair
|
|
}
|
|
|
|
function _setSettings (o) {
|
|
var b
|
|
|
|
o = o || {}
|
|
b = o.brackets
|
|
Object.defineProperty(o, 'brackets', {
|
|
set: _reset,
|
|
get: function () { return cachedBrackets },
|
|
enumerable: true
|
|
})
|
|
_settings = o
|
|
_reset(b)
|
|
}
|
|
|
|
Object.defineProperty(_brackets, 'settings', {
|
|
set: _setSettings,
|
|
get: function () { return _settings }
|
|
})
|
|
|
|
/* istanbul ignore next: in the browser riot is always in the scope */
|
|
_brackets.settings = typeof riot !== 'undefined' && riot.settings || {}
|
|
_brackets.set = _reset
|
|
_brackets.skipRegex = skipRegex
|
|
|
|
_brackets.R_STRINGS = R_STRINGS
|
|
_brackets.R_MLCOMMS = R_MLCOMMS
|
|
_brackets.S_QBLOCKS = S_QBLOCKS
|
|
_brackets.S_QBLOCK2 = S_QBLOCK2
|
|
|
|
return _brackets
|
|
|
|
})()
|
|
|
|
/**
|
|
* @module tmpl
|
|
*
|
|
* tmpl - Root function, returns the template value, render with data
|
|
* tmpl.hasExpr - Test the existence of a expression inside a string
|
|
* tmpl.loopKeys - Get the keys for an 'each' loop (used by `_each`)
|
|
*/
|
|
|
|
export
|
|
var tmpl = (function () {
|
|
|
|
var _cache = {}
|
|
|
|
function _tmpl (str, data) {
|
|
if (!str) return str
|
|
|
|
return (_cache[str] || (_cache[str] = _create(str))).call(
|
|
data, _logErr.bind({
|
|
data: data,
|
|
tmpl: str
|
|
})
|
|
)
|
|
}
|
|
|
|
_tmpl.hasExpr = brackets.hasExpr
|
|
|
|
_tmpl.loopKeys = brackets.loopKeys
|
|
|
|
// istanbul ignore next
|
|
_tmpl.clearCache = function () { _cache = {} }
|
|
|
|
_tmpl.errorHandler = null
|
|
_tmpl.getStr = _getStr;
|
|
|
|
function _logErr (err, ctx) {
|
|
|
|
err.riotData = {
|
|
tagName: ctx && ctx.__ && ctx.__.tagName,
|
|
_riot_id: ctx && ctx._riot_id //eslint-disable-line camelcase
|
|
}
|
|
|
|
if (_tmpl.errorHandler) _tmpl.errorHandler(err)
|
|
else if (
|
|
typeof console !== 'undefined' &&
|
|
typeof console.error === 'function'
|
|
) {
|
|
console.error(err.message)
|
|
console.log('<%s> %s', err.riotData.tagName || 'Unknown tag', this.tmpl) // eslint-disable-line
|
|
console.log(this.data) // eslint-disable-line
|
|
}
|
|
}
|
|
|
|
function _getStr(str) {
|
|
var expr = _getTmpl(str)
|
|
|
|
if (expr.slice(0, 11) !== 'try{return ') expr = 'return ' + expr
|
|
|
|
expr = 'var ' + (typeof window !== 'object' ? 'global' : 'window') + ' = {}; ' + expr
|
|
|
|
return expr;
|
|
}
|
|
|
|
function _create (str) {
|
|
var expr = _getTmpl(str)
|
|
|
|
if (expr.slice(0, 11) !== 'try{return ') expr = 'return ' + expr
|
|
|
|
expr = 'var ' + (typeof window !== 'object' ? 'global' : 'window') + ' = {}; ' + expr
|
|
|
|
return new Function('E', expr + ';') // eslint-disable-line no-new-func
|
|
}
|
|
|
|
var RE_DQUOTE = /\u2057/g
|
|
var RE_QBMARK = /\u2057(\d+)~/g
|
|
|
|
function _getTmpl (str) {
|
|
var parts = brackets.split(str.replace(RE_DQUOTE, '"'), 1)
|
|
var qstr = parts.qblocks
|
|
var expr
|
|
|
|
if (parts.length > 2 || parts[0]) {
|
|
var i, j, list = []
|
|
|
|
for (i = j = 0; i < parts.length; ++i) {
|
|
|
|
expr = parts[i]
|
|
|
|
if (expr && (expr = i & 1
|
|
|
|
? _parseExpr(expr, 1, qstr)
|
|
|
|
: '"' + expr
|
|
.replace(/\\/g, '\\\\')
|
|
.replace(/\r\n?|\n/g, '\\n')
|
|
.replace(/"/g, '\\"') +
|
|
'"'
|
|
|
|
)) list[j++] = expr
|
|
|
|
}
|
|
|
|
expr = j < 2 ? list[0]
|
|
: '[' + list.join(',') + '].join("")'
|
|
|
|
} else {
|
|
|
|
expr = _parseExpr(parts[1], 0, qstr)
|
|
}
|
|
|
|
if (qstr.length) {
|
|
expr = expr.replace(RE_QBMARK, function (_, pos) {
|
|
return qstr[pos]
|
|
.replace(/\r/g, '\\r')
|
|
.replace(/\n/g, '\\n')
|
|
})
|
|
}
|
|
return expr
|
|
}
|
|
|
|
var RE_CSNAME = /^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\u2057(\d+)~):/
|
|
var
|
|
RE_BREND = {
|
|
'(': /[()]/g,
|
|
'[': /[[\]]/g,
|
|
'{': /[{}]/g
|
|
}
|
|
|
|
function _parseExpr (expr, asText, qstr) {
|
|
|
|
expr = expr
|
|
.replace(/\s+/g, ' ').trim()
|
|
.replace(/\ ?([[\({},?\.:])\ ?/g, '$1')
|
|
|
|
if (expr) {
|
|
var
|
|
list = [],
|
|
cnt = 0,
|
|
match
|
|
|
|
while (expr &&
|
|
(match = expr.match(RE_CSNAME)) &&
|
|
!match.index
|
|
) {
|
|
var
|
|
key,
|
|
jsb,
|
|
re = /,|([[{(])|$/g
|
|
|
|
expr = RegExp.rightContext
|
|
key = match[2] ? qstr[match[2]].slice(1, -1).trim().replace(/\s+/g, ' ') : match[1]
|
|
|
|
while (jsb = (match = re.exec(expr))[1]) skipBraces(jsb, re)
|
|
|
|
jsb = expr.slice(0, match.index)
|
|
expr = RegExp.rightContext
|
|
|
|
list[cnt++] = _wrapExpr(jsb, 1, key)
|
|
}
|
|
|
|
expr = !cnt ? _wrapExpr(expr, asText)
|
|
: cnt > 1 ? '[' + list.join(',') + '].join(" ").trim()' : list[0]
|
|
}
|
|
|
|
return expr
|
|
|
|
function skipBraces (ch, re) {
|
|
var
|
|
mm,
|
|
lv = 1,
|
|
ir = RE_BREND[ch]
|
|
|
|
ir.lastIndex = re.lastIndex
|
|
while (mm = ir.exec(expr)) {
|
|
if (mm[0] === ch) ++lv
|
|
else if (!--lv) break
|
|
}
|
|
re.lastIndex = lv ? expr.length : ir.lastIndex
|
|
}
|
|
}
|
|
|
|
// istanbul ignore next: not both
|
|
var // eslint-disable-next-line max-len
|
|
JS_CONTEXT = '"in this?this:' + (typeof window !== 'object' ? 'global' : 'window') + ').',
|
|
JS_VARNAME = /[,{][\$\w]+(?=:)|(^ *|[^$\w\.{])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g,
|
|
JS_NOPROPS = /^(?=(\.[$\w]+))\1(?:[^.[(]|$)/
|
|
|
|
function _wrapExpr (expr, asText, key) {
|
|
var tb
|
|
|
|
expr = expr.replace(JS_VARNAME, function (match, p, mvar, pos, s) {
|
|
if (mvar) {
|
|
pos = tb ? 0 : pos + match.length
|
|
|
|
if (mvar !== 'this' && mvar !== 'global' && mvar !== 'window') {
|
|
match = p + '("' + mvar + JS_CONTEXT + mvar
|
|
if (pos) tb = (s = s[pos]) === '.' || s === '(' || s === '['
|
|
} else if (pos) {
|
|
tb = !JS_NOPROPS.test(s.slice(pos))
|
|
}
|
|
}
|
|
return match
|
|
})
|
|
|
|
if (tb) {
|
|
expr = 'try{return ' + expr + '}catch(e){E(e,this)}'
|
|
}
|
|
|
|
if (key) {
|
|
|
|
expr = (tb
|
|
? 'function(){' + expr + '}.call(this)' : '(' + expr + ')'
|
|
) + '?"' + key + '":""'
|
|
|
|
} else if (asText) {
|
|
|
|
if (expr === 'false') {
|
|
expr = 'function(v){' + (tb
|
|
? expr.replace('return ', 'v=') : 'v=(' + expr + ')'
|
|
) + ';return false}.call(this)'
|
|
} else {
|
|
|
|
expr = 'function(v){' + (tb
|
|
? expr.replace('return ', 'v=') : 'v=(' + expr + ')'
|
|
) + ';return v||v===0||v===false?v:""}.call(this)'
|
|
}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
_tmpl.version = brackets.version = 'v1.0.0'
|
|
|
|
return _tmpl
|
|
|
|
})()
|