UNPKG

todomvc

Version:

> Helping you select an MV\* framework

2,386 lines (1,986 loc) 277 kB
// source /src/license.txt /*! * MaskJS v0.10.0 * Part of the Atma.js Project * http://atmajs.com/ * * MIT license * http://opensource.org/licenses/MIT * * (c) 2012, 2014 Atma.js and other contributors */ // end:source /src/license.txt // source /src/umd-head.js (function (root, factory) { 'use strict'; var _global = typeof window === 'undefined' || window.navigator == null ? global : window, _exports, _document; if (typeof exports !== 'undefined' && (root == null || root === exports || root === _global)){ // raw commonjs module root = exports; } _document = _global.document; _exports = root || _global; function construct(){ return factory(_global, _exports, _document); } if (typeof define === 'function' && define.amd) { return define(construct); } // Browser OR Node return construct(); }(this, function (global, exports, document) { 'use strict'; // end:source /src/umd-head.js // source /ref-utils/lib/utils.embed.js // source /src/coll.js var coll_each, coll_remove, coll_map, coll_indexOf, coll_find; (function(){ coll_each = function(coll, fn, ctx){ if (ctx == null) ctx = coll; if (coll == null) return coll; var imax = coll.length, i = 0; for(; i< imax; i++){ fn.call(ctx, coll[i], i); } return ctx; }; coll_indexOf = function(coll, x){ if (coll == null) return -1; var imax = coll.length, i = 0; for(; i < imax; i++){ if (coll[i] === x) return i; } return -1; }; coll_remove = function(coll, x){ var i = coll_indexOf(coll, x); if (i === -1) return false; coll.splice(i, 1); return true; }; coll_map = function(coll, fn, ctx){ var arr = new Array(coll.length); coll_each(coll, function(x, i){ arr[i] = fn.call(this, x, i); }, ctx); return arr; }; coll_find = function(coll, fn, ctx){ var imax = coll.length, i = 0; for(; i < imax; i++){ if (fn.call(ctx || coll, coll[i], i)) return true; } return false; }; }()); // end:source /src/coll.js // source /src/polyfill/arr.js if (Array.prototype.forEach === void 0) { Array.prototype.forEach = function(fn, ctx){ coll_each(this, fn, ctx); }; } if (Array.prototype.indexOf === void 0) { Array.prototype.indexOf = function(x){ return coll_indexOf(this, x); }; } // end:source /src/polyfill/arr.js // source /src/polyfill/str.js if (String.prototype.trim == null){ String.prototype.trim = function(){ var start = -1, end = this.length, code; if (end === 0) return this; while(++start < end){ code = this.charCodeAt(start); if (code > 32) break; } while(--end !== 0){ code = this.charCodeAt(end); if (code > 32) break; } return start !== 0 && end !== length - 1 ? this.substring(start, end + 1) : this; }; } // end:source /src/polyfill/str.js // source /src/polyfill/fn.js if (Function.prototype.bind == null) { var _Array_slice; Function.prototype.bind = function(){ if (arguments.length < 2 && typeof arguments[0] === "undefined") return this; var fn = this, args = _Array_slice.call(arguments), ctx = args.shift(); return function() { return fn.apply(ctx, args.concat(_Array_slice.call(arguments))); }; }; } // end:source /src/polyfill/fn.js // source /src/is.js var is_Function, is_Array, is_ArrayLike, is_String, is_Object, is_notEmptyString, is_rawObject; (function() { is_Function = function(x) { return typeof x === 'function'; }; is_Object = function(x) { return x != null && typeof x === 'object'; }; is_Array = is_ArrayLike = function(arr) { return arr != null && typeof arr === 'object' && typeof arr.length === 'number' && typeof arr.slice === 'function' ; }; is_String = function(x) { return typeof x === 'string'; }; is_notEmptyString = function(x) { return typeof x === 'string' && x !== ''; }; is_rawObject = function(obj) { if (obj == null || typeof obj !== 'object') return false; return obj.constructor === Object; }; }()); // end:source /src/is.js // source /src/obj.js var obj_getProperty, obj_setProperty, obj_extend, obj_create; (function(){ obj_getProperty = function(obj, path){ if ('.' === path) // obsolete return obj; var chain = path.split('.'), imax = chain.length, i = -1; while ( obj != null && ++i < imax ) { obj = obj[chain[i]]; } return obj; }; obj_setProperty = function(obj, path, val) { var chain = path.split('.'), imax = chain.length - 1, i = -1, key; while ( ++i < imax ) { key = chain[i]; if (obj[key] == null) obj[key] = {}; obj = obj[key]; } obj[chain[i]] = val; }; obj_extend = function(a, b){ if (b == null) return a || {}; if (a == null) return obj_create(b); for(var key in b){ a[key] = b[key]; } return a; }; obj_create = Object.create || function(x) { var Ctor = function(){}; Ctor.prototype = x; return new Ctor; }; }()); // end:source /src/obj.js // source /src/arr.js var arr_remove, arr_each, arr_indexOf, arr_contains; (function(){ arr_remove = function(array, x){ var i = array.indexOf(x); if (i === -1) return false; array.splice(i, 1); return true; }; arr_each = function(arr, fn, ctx){ arr.forEach(fn, ctx); }; arr_indexOf = function(arr, x){ return arr.indexOf(x); }; arr_contains = function(arr, x){ return arr.indexOf(x) !== -1; }; }()); // end:source /src/arr.js // source /src/fn.js var fn_proxy, fn_apply, fn_doNothing; (function(){ fn_proxy = function(fn, ctx) { return function(){ return fn_apply(fn, ctx, arguments); }; }; fn_apply = function(fn, ctx, args){ var l = args.length; if (0 === l) return fn.call(ctx); if (1 === l) return fn.call(ctx, args[0]); if (2 === l) return fn.call(ctx, args[0], args[1]); if (3 === l) return fn.call(ctx, args[0], args[1], args[2]); if (4 === l) return fn.call(ctx, args[0], args[1], args[2], args[3]); return fn.apply(ctx, args); }; fn_doNothing = function(){ return false; }; }()); // end:source /src/fn.js // source /src/refs.js var _Array_slice = Array.prototype.slice, _Array_splice = Array.prototype.splice, _Array_indexOf = Array.prototype.indexOf, _Object_create = obj_create, _Object_hasOwnProp = Object.hasOwnProperty; // end:source /src/refs.js // end:source /ref-utils/lib/utils.embed.js // source /src/scope-vars.js var __rgxEscapedChar = { "'": /\\'/g, '"': /\\"/g, '{': /\\\{/g, '>': /\\>/g, ';': /\\>/g }, __cfg = { // Relevant to node.js only. Disable compo caching allowCache: true }; // end:source /src/scope-vars.js // source /src/util/util.js /** * - arr (Array) - array that was prepaired by parser - * every even index holds interpolate value that was in #{some value} * - model: current model * - type (String const) (node | attr): tell custom utils what part we are * interpolating * - cntx (Object): current render context object * - element (HTMLElement): * type node - this is a container * type attr - this is element itself * - name * type attr - attribute name * type node - undefined * * -returns Array | String * * If we rendere interpolation in a TextNode, then custom util can return not only string values, * but also any HTMLElement, then TextNode will be splitted and HTMLElements will be inserted within. * So in that case we return array where we hold strings and that HTMLElements. * * If custom utils returns only strings, then String will be returned by this function * */ function util_interpolate(arr, type, model, ctx, element, controller, name) { var imax = arr.length, i = -1, array = null, string = '', even = true, utility, value, index, key, handler; while ( ++i < imax ) { if (even === true) { if (array == null){ string += arr[i]; } else{ array.push(arr[i]); } } else { key = arr[i]; value = null; index = key.indexOf(':'); if (index === -1) { value = obj_getPropertyEx(key, model, ctx, controller); } else { utility = index > 0 ? key.substring(0, index).trim() : ''; if (utility === '') { utility = 'expression'; } key = key.substring(index + 1); handler = custom_Utils[utility]; if (handler == null) { log_error('Undefined custom util `%s`', utility); continue; } value = handler(key, model, ctx, element, controller, name, type); } if (value != null){ if (typeof value === 'object' && array == null){ array = [string]; } if (array == null){ string += value; } else { array.push(value); } } } even = !even; } return array == null ? string : array ; } // end:source /src/util/util.js // source /src/util/attr.js var attr_extend; (function(){ attr_extend = function (a, b) { if (a == null) { return b == null ? {} : obj_create(b); } if (b == null) return a; var key; for(key in b) { if ('class' === key && typeof a[key] === 'string') { a[key] += ' ' + b[key]; continue; } a[key] = b[key]; } return a; }; }()); // end:source /src/util/attr.js // source /src/util/template.js function Template(template) { this.template = template; this.index = 0; this.length = template.length; } Template.prototype = { skipWhitespace: function () { var template = this.template, index = this.index, length = this.length; for (; index < length; index++) { if (template.charCodeAt(index) > 32 /*' '*/) { break; } } this.index = index; return this; }, skipToAttributeBreak: function () { var template = this.template, index = this.index, length = this.length, c; do { c = template.charCodeAt(++index); /* if c == # && next() == { - continue */ if (c === 35 && template.charCodeAt(index + 1) === 123) { // goto end of template declaration this.index = index; this.sliceToChar('}'); this.index++; return; } } while (c !== 46 && c !== 35 && c !== 62 && c !== 123 && c !== 32 && c !== 59 && index < length); //while(!== ".#>{ ;"); this.index = index; }, sliceToChar: function (c) { var template = this.template, index = this.index, start = index, isEscaped = false, value, nindex; while ((nindex = template.indexOf(c, index)) > -1) { index = nindex; if (template.charCodeAt(index - 1) !== 92 /*'\\'*/) { break; } isEscaped = true; index++; } value = template.substring(start, index); this.index = index; return isEscaped ? value.replace(__rgxEscapedChar[c], c) : value; } }; // end:source /src/util/template.js // source /src/util/array.js var arr_pushMany; (function(){ arr_pushMany = function(arr, arrSource){ if (arrSource == null || arr == null) return; var il = arr.length, jl = arrSource.length, j = -1 ; while( ++j < jl ){ arr[il + j] = arrSource[j]; } }; }()); // end:source /src/util/array.js // source /src/util/string.js // end:source /src/util/string.js // source /src/util/object.js var obj_getPropertyEx, obj_toDictionary; (function(){ obj_getPropertyEx = function(path, model, ctx, ctr){ if (path === '.') return model; var props = path.split('.'), value = model, i = -1, imax = props.length, key = props[0], start_i ; if ('$c' === key) { value = ctr; i++; } else if ('$a' === key) { value = ctr && ctr.attr; i++; } else if ('$u' === key) { value = customUtil_$utils; i++; } else if ('$ctx' === key) { value = ctx; i++; } start_i = i; while (value != null && ++i < imax) { value = value[props[i]]; } if (value == null && start_i === -1) { var $scope; while (ctr != null){ $scope = ctr.scope; if ($scope != null) { value = getProperty_($scope, props, 0, imax); if (value != null) return value; } ctr = ctr.parent; } } return value; }; obj_toDictionary = function(obj){ var array = [], i = 0, key ; for(key in obj){ array[i++] = { key: key, value: obj[key] }; } return array; }; // = private function getProperty_(obj, props, i, imax) { var val = obj; while(i < imax && val != null){ val = val[props[i]]; i++; } return val; } }()); // end:source /src/util/object.js // source /src/util/listeners.js var listeners_on, listeners_off, listeners_emit; (function(){ listeners_on = function(event, fn) { (bin[event] || (bin[event] = [])).push(fn); }; listeners_off = function(event, fn){ if (fn == null) { bin[event] = []; return; } arr_remove(bin[event], fn); }; listeners_emit = function(event){ var fns = bin[event]; if (fns == null) return; var imax = fns.length, i = -1, args = _Array_slice.call(arguments, 1) ; while ( ++i < imax) fns[i].apply(null, args); }; // === private var bin = { compoCreated: null, error: null }; }()); // end:source /src/util/listeners.js // source /src/util/reporters.js var throw_, parser_error, parser_warn, log_warn, log_error; (function(){ throw_ = function(error){ log_error(error); listeners_emit('error', error); }; parser_error = function(msg, str, i, token, state, file){ var error = createMsg('error', msg, str, i, token, state, file); log_error(error.message); log_warn(error.stack); listeners_emit('error', error); }; parser_warn = function(msg, str, i, token, state, file){ var error = createMsg('warn', msg, str, i, token, state, file); log_warn(error.message); log_warn(error.stack); listeners_emit('error', error); }; if (typeof console === 'undefined') { log_warn = log_error = function(){}; } else { var bind = Function.prototype.bind; log_warn = bind.call(console.warn , console, 'MaskJS [Warn] :'); log_error = bind.call(console.error, console, 'MaskJS [Error] :'); } var ParserError = createError('Error'), ParserWarn = createError('Warning'); function createError(type) { function ParserError(msg, orig, index){ this.type = 'Parser' + type; this.message = msg; this.original = orig; this.index = index; this.stack = prepairStack(); } inherit(ParserError, Error); return ParserError; } function prepairStack(){ var stack = new Error().stack; if (stack == null) return null; return stack .split('\n') .slice(6, 8) .join('\n'); } function inherit(Ctor, Base){ if (Object.create) Ctor.prototype = Object.create(Base.prototype); } function createMsg(type, msg, str, index, token, state, filename){ msg += formatToken(token) + formatFilename(str, index, filename) + formatStopped(type, str, index) + formatState(state) ; var Ctor = type === 'error' ? ParserError : ParserWarn; return new Ctor(msg, str, index); } function formatToken(token){ if (token == null) return ''; if (typeof token === 'number') token = String.fromCharCode(token); return ' Invalid token: `'+ token + '`'; } function formatFilename(str, index, filename) { if (index == null && !filename) return ''; var lines = str.substring(0, index).split('\n'), line = lines.length, row = index + 1 - lines.slice(0, line - 2).join('\n').length; return ' at ' + (filename || '') + '(' + line + ':' + row + ')'; } function formatState(state){ var states = { '2': 'tag', '3': 'tag', '5': 'attribute key', '6': 'attribute value', '8': 'literal', 'var': 'VarStatement', 'expr': 'Expression' }; if (state == null || states[state] == null) return ''; return '\n , when parsing ' + states[state]; } function formatStopped(type, str, index){ if (index == null) return ''; var stopped = str.substring(index); if (stopped.length > 30) stopped = stopped.substring(0, 30) + '...'; return '\n Parser ' + type + ' at: ' + stopped; } }()); // end:source /src/util/reporters.js // source /src/custom/exports.js var custom_Utils, custom_Statements, custom_Attributes, custom_Tags, custom_Tags_defs, customUtil_get, customUtil_$utils, customUtil_register, customTag_register ; (function(){ initialize(); // source tag.js (function(repository){ customTag_register = function(name, Handler){ if (Handler != null && typeof Handler === 'object') { //> static Handler.__Ctor = wrapStatic(Handler); } repository[name] = Handler; }; function wrapStatic(proto) { function Ctor(node, parent) { this.tagName = node.tagName; this.attr = node.attr; this.expression = node.expression; this.nodes = node.nodes; this.nextSibling = node.nextSibling; this.parent = parent; this.components = null; } Ctor.prototype = proto; return Ctor; } }(custom_Tags)); // end:source tag.js // source util.js (function(repository) { customUtil_$utils = {}; customUtil_register = function(name, mix) { if (is_Function(mix)) { repository[name] = mix; return; } repository[name] = createUtil(mix); if (mix.arguments === 'parsed') customUtil_$utils[name] = mix.process; }; customUtil_get = function(name) { return name != null ? repository[name] : repository ; }; // = private function createUtil(obj) { if (obj.arguments === 'parsed') return processParsedDelegate(obj.process); var fn = fn_proxy(obj.process || processRawFn, obj); // <static> save reference to the initial util object. // Mask.Bootstrap need the original util // @workaround fn.util = obj; return fn; } function processRawFn(expr, model, ctx, element, controller, attrName, type) { if ('node' === type) { this.nodeRenderStart(expr, model, ctx, element, controller); return this.node(expr, model, ctx, element, controller); } // asume 'attr' this.attrRenderStart(expr, model, ctx, element, controller, attrName); return this.attr(expr, model, ctx, element, controller, attrName); } function processParsedDelegate(fn) { return function(expr, model, ctx, element, controller) { var args = ExpressionUtil .evalStatements(expr, model, ctx, controller); return fn.apply(null, args); }; } }(custom_Utils)); // end:source util.js function initialize() { custom_Utils = { expression: function(value, model, ctx, element, controller){ return ExpressionUtil.eval(value, model, ctx, controller); }, }; custom_Statements = {}; custom_Attributes = { 'class': null, id: null, style: null, name: null, type: null }; custom_Tags = { /* * Most common html tags * http://jsperf.com/not-in-vs-null/3 */ div: null, span: null, input: null, button: null, textarea: null, select: null, option: null, h1: null, h2: null, h3: null, h4: null, h5: null, h6: null, a: null, p: null, img: null, table: null, td: null, tr: null, pre: null, ul: null, li: null, ol: null, i: null, em: null, b: null, strong: null, form: null, audio: null, video: null, canvas: null, svg: null }; // use on server to define reserved tags and its meta info custom_Tags_defs = {}; } }()); // end:source /src/custom/exports.js // source /src/expression/exports.js /** * ExpressionUtil * * Helper to work with expressions **/ var ExpressionUtil = (function(){ // source 1.scope-vars.js var index = 0, length = 0, cache = {}, template, ast; var op_Minus = '-', //1, op_Plus = '+', //2, op_Divide = '/', //3, op_Multip = '*', //4, op_Modulo = '%', //5, op_LogicalOr = '||', //6, op_LogicalAnd = '&&', //7, op_LogicalNot = '!', //8, op_LogicalEqual = '==', //9, op_LogicalEqual_Strict = '===', // 111 op_LogicalNotEqual = '!=', //11, op_LogicalNotEqual_Strict = '!==', // 112 op_LogicalGreater = '>', //12, op_LogicalGreaterEqual = '>=', //13, op_LogicalLess = '<', //14, op_LogicalLessEqual = '<=', //15, op_Member = '.', // 16 punc_ParantheseOpen = 20, punc_ParantheseClose = 21, punc_BracketOpen = 22, punc_BracketClose = 23, punc_BraceOpen = 24, punc_BraceClose = 25, punc_Comma = 26, punc_Dot = 27, punc_Question = 28, punc_Colon = 29, punc_Semicolon = 30, go_ref = 31, go_acs = 32, go_string = 33, go_number = 34, go_objectKey = 35; var type_Body = 1, type_Statement = 2, type_SymbolRef = 3, type_FunctionRef = 4, type_Accessor = 5, type_AccessorExpr = 6, type_Value = 7, type_Number = 8, type_String = 9, type_Object = 10, type_Array = 11, type_UnaryPrefix = 12, type_Ternary = 13; var state_body = 1, state_arguments = 2; var precedence = {}; precedence[op_Member] = 1; precedence[op_Divide] = 2; precedence[op_Multip] = 2; precedence[op_Minus] = 3; precedence[op_Plus] = 3; precedence[op_LogicalGreater] = 4; precedence[op_LogicalGreaterEqual] = 4; precedence[op_LogicalLess] = 4; precedence[op_LogicalLessEqual] = 4; precedence[op_LogicalEqual] = 5; precedence[op_LogicalEqual_Strict] = 5; precedence[op_LogicalNotEqual] = 5; precedence[op_LogicalNotEqual_Strict] = 5; precedence[op_LogicalAnd] = 6; precedence[op_LogicalOr] = 6; // end:source 1.scope-vars.js // source 2.ast.js var Ast_Body, Ast_Statement, Ast_Value, Ast_Array, Ast_Object, Ast_FunctionRef, Ast_SymbolRef, Ast_Accessor, Ast_AccessorExpr, Ast_UnaryPrefix, Ast_TernaryStatement ; (function(){ Ast_Body = function(parent) { this.parent = parent; this.type = type_Body; this.body = []; this.join = null; }; Ast_Statement = function(parent) { this.parent = parent; }; Ast_Statement.prototype = { constructor: Ast_Statement, type: type_Statement, join: null, body: null }; Ast_Value = function(value) { this.type = type_Value; this.body = value; this.join = null; }; Ast_Array = function(parent){ this.type = type_Array; this.parent = parent; this.body = new Ast_Body(this); }; Ast_Object = function(parent){ this.type = type_Object; this.parent = parent; this.props = {}; } Ast_Object.prototype = { nextProp: function(prop){ var body = new Ast_Statement(this); this.props[prop] = body; return body; }, }; Ast_FunctionRef = function(parent, ref) { this.parent = parent; this.type = type_FunctionRef; this.body = ref; this.arguments = []; this.next = null; } Ast_FunctionRef.prototype = { constructor: Ast_FunctionRef, newArgument: function() { var body = new Ast_Body(this); this.arguments.push(body); return body; } }; Ast_SymbolRef = function(parent, ref) { this.type = type_SymbolRef; this.parent = parent; this.body = ref; this.next = null; }; Ast_Accessor = function(parent, ref) { this.type = type_Accessor; this.parent = parent; this.body = ref; this.next = null; }; Ast_AccessorExpr = function(parent){ this.parent = parent; this.body = new Ast_Statement(this); this.body.body = new Ast_Body(this.body); this.next = null; }; Ast_AccessorExpr.prototype = { type: type_AccessorExpr, getBody: function(){ return this.body.body; } }; Ast_UnaryPrefix = function(parent, prefix) { this.parent = parent; this.prefix = prefix; }; Ast_UnaryPrefix.prototype = { constructor: Ast_UnaryPrefix, type: type_UnaryPrefix, body: null }; Ast_TernaryStatement = function(assertions){ this.body = assertions; this.case1 = new Ast_Body(this); this.case2 = new Ast_Body(this); }; Ast_TernaryStatement.prototype = { constructor: Ast_TernaryStatement, type: type_Ternary, case1: null, case2: null }; }()); // end:source 2.ast.js // source 2.ast.utils.js var ast_handlePrecedence, ast_append; (function(){ ast_append = function(current, next) { switch(current.type) { case type_Body: current.body.push(next); return next; case type_Statement: if (next.type === type_Accessor || next.type === type_AccessorExpr) { return (current.next = next) } /* fall through */ case type_UnaryPrefix: return (current.body = next); case type_SymbolRef: case type_FunctionRef: case type_Accessor: case type_AccessorExpr: return (current.next = next); } return util_throw('Invalid expression'); }; ast_handlePrecedence = function(ast) { if (ast.type !== type_Body){ if (ast.body != null && typeof ast.body === 'object') ast_handlePrecedence(ast.body); return; } var body = ast.body, i = 0, length = body.length, x, prev, array; for(; i < length; i++){ ast_handlePrecedence(body[i]); } for(i = 1; i < length; i++){ x = body[i]; prev = body[i-1]; if (precedence[prev.join] > precedence[x.join]) break; } if (i === length) return; array = [body[0]]; for(i = 1; i < length; i++){ x = body[i]; prev = body[i-1]; var prec_Prev = precedence[prev.join]; if (prec_Prev > precedence[x.join] && i < length - 1){ var start = i, nextJoin, arr; // collect all with join smaller or equal to previous // 5 == 3 * 2 + 1 -> 5 == (3 * 2 + 1); while (++i < length){ nextJoin = body[i].join; if (nextJoin == null) break; if (prec_Prev <= precedence[nextJoin]) break; } arr = body.slice(start, i + 1); x = ast_join(arr); ast_handlePrecedence(x); } array.push(x); } ast.body = array; }; // = private function ast_join(bodyArr){ if (bodyArr.length === 0) return null; var body = new Ast_Body(bodyArr[0].parent); body.join = bodyArr[bodyArr.length - 1].join; body.body = bodyArr; return body; } }()); // end:source 2.ast.utils.js // source 3.util.js var util_resolveRef, util_throw; (function(){ util_throw = function(msg, token){ return parser_error(msg , template , index , token , 'expr' ); }; util_resolveRef = function(astRef, model, ctx, controller) { var current = astRef, key = astRef.body, object, value, args, i, imax ; if ('$c' === key) { value = controller; var next = current.next, nextBody = next != null && next.body; if (nextBody != null && value[nextBody] == null){ if (next.type === type_FunctionRef && typeof Compo.prototype[nextBody] === 'function') { // use fn from prototype if possible, like `closest` object = controller; value = Compo.prototype[nextBody]; current = next; } else { // find the closest controller, which has the property while (true) { value = value.parent; if (value == null) break; if (value[nextBody] == null) continue; object = value; value = value[nextBody]; current = next; break; } } if (value == null) { // prepair for warn message key = '$c.' + nextBody; current = next; } } } else if ('$a' === key) value = controller && controller.attr; else if ('$u' === key) value = customUtil_$utils; else if ('$ctx' === key) value = ctx; else { // scope resolver if (model != null) { object = model; value = model[key]; } if (value == null) { while (controller != null) { object = controller.scope; if (object != null) value = object[key]; if (value != null) break; controller = controller.parent; } } } if (value == null) { if (current == null || current.next != null){ // notify that value is not in model, ctx, controller; log_warn('<mask:expression> Accessor error:', key); } return null; } do { if (current.type === type_FunctionRef) { args = []; i = -1; imax = current.arguments.length; while( ++i < imax ) args[i] = expression_evaluate(current.arguments[i], model, ctx, controller); value = value.apply(object, args); } if (value == null || current.next == null) break; current = current.next; key = current.type === type_AccessorExpr ? expression_evaluate(current.body, model, ctx, controller) : current.body ; object = value; value = value[key]; if (value == null) break; } while (true); return value; }; }()); // end:source 3.util.js // source 4.parser.helper.js var parser_skipWhitespace, parser_getString, parser_getNumber, parser_getArray, parser_getObject, parser_getRef, parser_getDirective ; (function(){ parser_skipWhitespace = function() { var c; while (index < length) { c = template.charCodeAt(index); if (c > 32) return c; index++; } return null; }; parser_getString = function(c) { var isEscaped = false, _char = c === 39 ? "'" : '"', start = index, nindex, string; while ((nindex = template.indexOf(_char, index)) > -1) { index = nindex; if (template.charCodeAt(nindex - 1) !== 92 /*'\\'*/ ) { break; } isEscaped = true; index++; } string = template.substring(start, index); if (isEscaped === true) { string = string.replace(__rgxEscapedChar[_char], _char); } return string; }; parser_getNumber = function() { var start = index, code, isDouble; while (true) { code = template.charCodeAt(index); if (code === 46) { // . if (isDouble === true) { util_throw('Invalid number', code); return null; } isDouble = true; } if ((code >= 48 && code <= 57 || code === 46) && index < length) { index++; continue; } break; } return +template.substring(start, index); }; parser_getRef = function() { var start = index, c = template.charCodeAt(index), ref; if (c === 34 || c === 39) { // ' | " index++; ref = parser_getString(c); index++; return ref; } while (true) { if (index === length) break; c = template.charCodeAt(index); if (c === 36 || c === 95) { // $ _ index++; continue; } if ((48 <= c && c <= 57) || // 0-9 (65 <= c && c <= 90) || // A-Z (97 <= c && c <= 122)) { // a-z index++; continue; } // - [removed] (exit on not allowed chars) 5ba755ca break; } return template.substring(start, index); }; parser_getDirective = function(code) { if (code == null && index === length) return null; switch (code) { case 40: // ( return punc_ParantheseOpen; case 41: // ) return punc_ParantheseClose; case 123: // { return punc_BraceOpen; case 125: // } return punc_BraceClose; case 91: // [ return punc_BracketOpen; case 93: // ] return punc_BracketClose; case 44: // , return punc_Comma; case 46: // . return punc_Dot; case 59: // ; return punc_Semicolon; case 43: // + return op_Plus; case 45: // - return op_Minus; case 42: // * return op_Multip; case 47: // / return op_Divide; case 37: // % return op_Modulo; case 61: // = if (template.charCodeAt(++index) !== code) { util_throw( 'Assignment violation: View can only access model/controllers', '=' ); return null; } if (template.charCodeAt(index + 1) === code) { index++; return op_LogicalEqual_Strict; } return op_LogicalEqual; case 33: // ! if (template.charCodeAt(index + 1) === 61) { // = index++; if (template.charCodeAt(index + 1) === 61) { // = index++; return op_LogicalNotEqual_Strict; } return op_LogicalNotEqual; } return op_LogicalNot; case 62: // > if (template.charCodeAt(index + 1) === 61) { index++; return op_LogicalGreaterEqual; } return op_LogicalGreater; case 60: // < if (template.charCodeAt(index + 1) === 61) { index++; return op_LogicalLessEqual; } return op_LogicalLess; case 38: // & if (template.charCodeAt(++index) !== code) { util_throw( 'Not supported: Bitwise AND', code ); return null; } return op_LogicalAnd; case 124: // | if (template.charCodeAt(++index) !== code) { util_throw( 'Not supported: Bitwise OR', code ); return null; } return op_LogicalOr; case 63: // ? return punc_Question; case 58: // : return punc_Colon; } if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code === 95) || (code === 36)) { // A-Z a-z _ $ return go_ref; } if (code >= 48 && code <= 57) { // 0-9 . return go_number; } if (code === 34 || code === 39) { // " ' return go_string; } util_throw( 'Unexpected or unsupported directive', code ); return null; }; }()); // end:source 4.parser.helper.js // source 5.parser.js /* * earlyExit - only first statement/expression is consumed */ function expression_parse(expr, earlyExit) { if (earlyExit == null) earlyExit = false; template = expr; index = 0; length = expr.length; ast = new Ast_Body(); var current = ast, state = state_body, c, next, directive; outer: while (true) { if (index < length && (c = template.charCodeAt(index)) < 33) { index++; continue; } if (index >= length) break; directive = parser_getDirective(c); if (directive == null && index < length) { break; } if (directive === punc_Semicolon) { if (earlyExit === true) return [ast, index]; break; } if (earlyExit === true) { var p = current.parent; if (p != null && p.type === type_Body && p.parent == null) { // is in root body if (directive === go_ref) return [ast, index]; } } if (directive === punc_Semicolon) { break; } switch (directive) { case punc_ParantheseOpen: current = ast_append(current, new Ast_Statement(current)); current = ast_append(current, new Ast_Body(current)); index++; continue; case punc_ParantheseClose: var closest = type_Body; if (state === state_arguments) { state = state_body; closest = type_FunctionRef; } do { current = current.parent; } while (current != null && current.type !== closest); if (closest === type_Body) { current = current.parent; } if (current == null) { util_throw('OutOfAst Exception', c); break outer; } index++; continue; case punc_BraceOpen: current = ast_append(current, new Ast_Object(current)); directive = go_objectKey; index++; break; case punc_BraceClose: while (current != null && current.type !== type_Object){ current = current.parent; } index++; continue; case punc_Comma: if (state !== state_arguments) { state = state_body; do { current = current.parent; } while (current != null && current.type !== type_Body && current.type !== type_Object ); index++; if (current == null) { util_throw('Unexpected comma', c); break outer; } if (current.type === type_Object) { directive = go_objectKey; break; } continue; } do { current = current.parent; } while (current != null && current.type !== type_FunctionRef); if (current == null) { util_throw('OutOfAst Exception', c); break outer; } current = current.newArgument(); index++; continue; case punc_Question: ast = new Ast_TernaryStatement(ast); current = ast.case1; index++; continue; case punc_Colon: current = ast.case2; index++; continue; case punc_Dot: c = template.charCodeAt(index + 1); if (c >= 48 && c <= 57) { directive = go_number; } else { directive = current.type === type_Body ? go_ref : go_acs ; index++; } break; case punc_BracketOpen: if (current.type === type_SymbolRef || current.type === type_AccessorExpr || current.type === type_Accessor ) { current = ast_append(current, new Ast_AccessorExpr(current)) current = current.getBody(); index++; continue; } current = ast_append(current, new Ast_Array(current)); current = current.body; index++; continue; case punc_BracketClose: do { current = current.parent; } while (current != null && current.type !== type_AccessorExpr && current.type !== type_Array ); index++; continue; } if (current.type === type_Body) { current = ast_append(current, new Ast_Statement(current)); } if ((op_Minus === directive || op_LogicalNot === directive) && current.body == null) { current = ast_append(current, new Ast_UnaryPrefix(current, directive)); index++; continue; } switch (directive) { case op_Minus: case op_Plus: case op_Multip: case op_Divide: case op_Modulo: case op_LogicalAnd: case op_LogicalOr: case op_LogicalEqual: case op_LogicalEqual_Strict: case op_LogicalNotEqual: case op_LogicalNotEqual_Strict: case op_LogicalGreater: case op_LogicalGreaterEqual: case op_LogicalLess: case op_LogicalLessEqual: while (current && current.type !== type_Statement) { current = current.parent; } if (current.body == null) { return util_throw( 'Unexpected operator', c ); } current.join = directive; do { current = current.parent; } while (current != null && current.type !== type_Body); if (current == null) { return util_throw( 'Unexpected operator' , c ); } index++; continue; case go_string: case go_number: if (current.body != null && current.join == null) { return util_throw( 'Directive expected', c ); } if (go_string === directive) { index++; ast_append(current, new Ast_Value(parser_getString(c))); index++; } if (go_number === directive) { ast_append(current, new Ast_Value(parser_getNumber(c))); } continue; case go_ref: case go_acs: var ref = parser_getRef(); if (directive === go_ref) { if (ref === 'null') ref = null; if (ref === 'false') ref = false; if (ref === 'true') ref = true; if (typeof ref !== 'string') { ast_append(current, new Ast_Value(ref)); continue; } } while (index < length) { c = template.charCodeAt(index); if (c < 33) { index++; continue; } break; } if (c === 40) { // ( // function ref state = state_arguments; index++; var fn = ast_append(current, new Ast_FunctionRef(current, ref)); current = fn.newArgument(); continue; } var Ctor = directive === go_ref ? Ast_SymbolRef : Ast_Accessor current = ast_append(current, new Ctor(current, ref)); break; case go_objectKey: if (parser_skipWhitespace() === 125) continue; var key = parser_getRef(); if (parser_skipWhitespace() !== 58) { //: return util_throw( 'Object parser. Semicolon expeted', c ); } index++; current = current.nextProp(key); directive = go_ref; continue; } } if (current.body == null && current.type === type_Statement) { return util_throw( 'Unexpected end of expression', c ); } ast_handlePrecedence(ast); return ast; } // end:source 5.parser.js // source 6.eval.js function expression_evaluate(mix, model, ctx, controller) { var result, ast; if (null == mix) return null; if ('.' === mix) return model; if (typeof mix === 'string'){ ast = cache.hasOwnProperty(mix) === true ? (cache[mix]) : (cache[mix] = expression_parse(mix)) ; }else{ ast = mix; } if (ast == null) return null; var type = ast.type, i, x, length; if (type_Body === type) { var value, prev; outer: for (i = 0, length = ast.body.length; i < length; i++) { x = ast.body[i]; value = expression_evaluate(x, model, ctx, controller); if (prev == null || prev.join == null) { prev = x; result = value; continue; } if (prev.join === op_LogicalAnd) { if (!result) { for (; i < length; i++) { if (ast.body[i].join === op_LogicalOr) { break; } } }else{ result = value; } } if (prev.join === op_LogicalOr) { if (result){ break outer; } if (value) { result = value; break outer; } } switch (prev.join) { case op_Minus: result -= value; break; case op_Plus: result += value; break; case op_Divide: result /= value; break; case op_Multip: result *= value; break; case op_Modulo: result %= value; break; case op_LogicalNotEqual: /* jshint eqeqeq: false */ result = result != value; /* jshint eqeqeq: true */ break; case op_LogicalNotEqual_Strict: result = result !== value; break; case op_LogicalEqual: /* jshint eqeqeq: false */ result = result == value; /* jshint eqeqeq: true */ break; case op_LogicalEqual_Strict: result = result === value; break; case op_LogicalGreater: result = result > value; break; case op_LogicalGreaterEqual: result = result >= value; break; case op_LogicalLess: result = result < value; break; case op_LogicalLessEqual: result = result <= value; break; } prev = x; } } if (type_Statement === type) { result = expression_evaluate(ast.body, model, ctx, controller); if (ast.next == null) return result; //debugger; return util_resolveRef(ast.next, result); } if (type_Value === type) { return ast.body; } if (type_Array === type) { var body = ast.body.body, imax = body.length, i = -1; result = new Array(imax); while( ++i < imax ){ result[i] = expression_evaluate(body[i], model, ctx, controller); } return result; } if (type_Object === type) { result = {}; var props = ast.props; for(var key in props){ result[key] = expression_evaluate(props[key], model, ctx, controller); } return result; } if (type_SymbolRef === type || type_FunctionRef === type || type_AccessorExpr === type || type_Accessor === type) { return util_resolveRef(ast, model, ctx, controller); } if (type_UnaryPrefix === type) { result = expression_evaluate(ast.body, model, ctx, controller); switch (ast.prefix) { case op_Minus: result = -result; break; case op_LogicalNot: result = !result; break; } } if (type_Ternary === type){ result = expression_evaluate(ast.body, model, ctx, controller); result = expression_evaluate(result ? ast.case1 : ast.case2, model, ctx, controller); } return result; } // end:source 6.eval.js // source 7.vars.helper.js var refs_extractVars; (function() { /** * extract symbol references * ~[:user.name + 'px'] -> 'user.name' * ~[:someFn(varName) + user.name] -> ['varName', 'user.name'] * * ~[:someFn().user.name] -> {accessor: (Accessor AST function call) , ref: 'user.name'} */ refs_extractVars = function(expr, model, ctx, ctr){ if (typeof expr === 'string') expr = expression_parse(expr); return _extractVars(expr, model, ctx, ctr); }; function _extractVars(expr, model, ctx, ctr) { if (expr == null) return null; var exprType = expr.type, refs, x; if (type_Body === exprType) { var body = expr.body, imax = body.length, i = -1; while ( ++i < imax ){ x = _extractVars(body[i], model, ctx, ctr); refs = _append(refs, x); } } if (type_SymbolRef === exprType || type_Accessor === exprType || type_AccessorExpr === exprType) { var path = expr.body, next = expr.next, nextType; while (next != null) { nextType = next.type; if (type_FunctionRef === nextType) { return _extractVars(next, model, ctx, ctr); } if ((type_SymbolRef !== nextType) && (type_Accessor !== nextType) && (type_AccessorExpr !== nextType)) { log_error('Ast Exception: next should be a symbol/function ref'); return null; } var prop = nextType === type_AccessorExpr ? expression_evaluate(next.body, model, ctx, ctr) : next.body ; if (typeof prop !== 'string') { log_warn('Can`t extract accessor name', path); return null; } path += '.' + prop; next = next.next; } return path; } switch (exprType) { case type_Statement: case type_UnaryPrefix: case type_Ternary: x = _extractVars(expr.body, model, ctx, ctr); refs = _append(refs, x); break; } // get also from case1 and case2 if (type_Ternary === exprType) { x = _extractVars(ast.case1, model, ctx, ctr); refs = _append(refs, x); x = _extractVars(ast.case2, model, ctx, ctr); refs = _append(refs, x); } if (type_FunctionRef === exprType) { var args = expr.arguments, imax = args.length, i = -1; while ( ++i < imax ){ x = _extractVars(args[i], model, ctx, ctr); refs = _append(refs, x); } x = null; var parent = expr; outer: while ((parent = parent.parent)) { switch (parent.type) { case type_SymbolRef: case type_Accessor: case type_AccessorExpr: x = parent.body + (x == null ? '' : '.' + x); break; case type_Body: case type_Statement: break outer; default: x = null; break outer; } } if (x != null) { refs = _append(refs, x); } if (expr.next) { x = _extractVars(expr.next, model, ctx, ctr); refs = _append(refs, {accessor: _getAccessor(expr), ref: x}); } } return refs; } function _append(current, x) { if (current == null) { return x; } if (x == null) { return current; } if (!(typeof current === 'object' && current.length != null)) { current = [current]; } if (!(typeof x === 'object' && x.length != null)) { if (current.indexOf(x) === -1) { current.push(x); } return current; }