var config = require('./config'), toString = ({}).toString, win = window, console = win.console, def = Object.defineProperty, OBJECT = 'object', THIS_RE = /[^\w]this[^\w]/, BRACKET_RE_S = /\['([^']+)'\]/g, BRACKET_RE_D = /\["([^"]+)"\]/g, hasClassList = 'classList' in document.documentElement, ViewModel // late def var defer = win.requestAnimationFrame || win.webkitRequestAnimationFrame || win.setTimeout /** * Normalize keypath with possible brackets into dot notations */ function normalizeKeypath (key) { return key.indexOf('[') < 0 ? key : key.replace(BRACKET_RE_S, '.$1') .replace(BRACKET_RE_D, '.$1') } var utils = module.exports = { /** * Convert a string template to a dom fragment */ toFragment: require('./fragment'), /** * Parse the various types of template options */ parseTemplateOption: require('./template-parser.js'), /** * get a value from an object keypath */ get: function (obj, key) { /* jshint eqeqeq: false */ key = normalizeKeypath(key) if (key.indexOf('.') < 0) { return obj[key] } var path = key.split('.'), d = -1, l = path.length while (++d < l && obj != null) { obj = obj[path[d]] } return obj }, /** * set a value to an object keypath */ set: function (obj, key, val) { /* jshint eqeqeq: false */ key = normalizeKeypath(key) if (key.indexOf('.') < 0) { obj[key] = val return } var path = key.split('.'), d = -1, l = path.length - 1 while (++d < l) { if (obj[path[d]] == null) { obj[path[d]] = {} } obj = obj[path[d]] } obj[path[d]] = val }, /** * return the base segment of a keypath */ baseKey: function (key) { return key.indexOf('.') > 0 ? key.split('.')[0] : key }, /** * Create a prototype-less object * which is a better hash/map */ hash: function () { return Object.create(null) }, /** * get an attribute and remove it. */ attr: function (el, type) { var attr = config.prefix + '-' + type, val = el.getAttribute(attr) if (val !== null) { el.removeAttribute(attr) } return val }, /** * Define an ienumerable property * This avoids it being included in JSON.stringify * or for...in loops. */ defProtected: function (obj, key, val, enumerable, writable) { def(obj, key, { value : val, enumerable : enumerable, writable : writable, configurable : true }) }, /** * A less bullet-proof but more efficient type check * than Object.prototype.toString */ isObject: function (obj) { return typeof obj === OBJECT && obj && !Array.isArray(obj) }, /** * A more accurate but less efficient type check */ isTrueObject: function (obj) { return toString.call(obj) === '[object Object]' }, /** * Most simple bind * enough for the usecase and fast than native bind() */ bind: function (fn, ctx) { return function (arg) { return fn.call(ctx, arg) } }, /** * Make sure null and undefined output empty string */ guard: function (value) { /* jshint eqeqeq: false, eqnull: true */ return value == null ? '' : (typeof value == 'object') ? JSON.stringify(value) : value }, /** * When setting value on the VM, parse possible numbers */ checkNumber: function (value) { return (isNaN(value) || value === null || typeof value === 'boolean') ? value : Number(value) }, /** * simple extend */ extend: function (obj, ext) { for (var key in ext) { if (obj[key] !== ext[key]) { obj[key] = ext[key] } } return obj }, /** * filter an array with duplicates into uniques */ unique: function (arr) { var hash = utils.hash(), i = arr.length, key, res = [] while (i--) { key = arr[i] if (hash[key]) continue hash[key] = 1 res.push(key) } return res }, /** * Convert the object to a ViewModel constructor * if it is not already one */ toConstructor: function (obj) { ViewModel = ViewModel || require('./viewmodel') return utils.isObject(obj) ? ViewModel.extend(obj) : typeof obj === 'function' ? obj : null }, /** * Check if a filter function contains references to `this` * If yes, mark it as a computed filter. */ checkFilter: function (filter) { if (THIS_RE.test(filter.toString())) { filter.computed = true } }, /** * convert certain option values to the desired format. */ processOptions: function (options) { var components = options.components, partials = options.partials, template = options.template, filters = options.filters, key if (components) { for (key in components) { components[key] = utils.toConstructor(components[key]) } } if (partials) { for (key in partials) { partials[key] = utils.parseTemplateOption(partials[key]) } } if (filters) { for (key in filters) { utils.checkFilter(filters[key]) } } if (template) { options.template = utils.parseTemplateOption(template) } }, /** * used to defer batch updates */ nextTick: function (cb) { defer(cb, 0) }, /** * add class for IE9 * uses classList if available */ addClass: function (el, cls) { if (hasClassList) { el.classList.add(cls) } else { var cur = ' ' + el.className + ' ' if (cur.indexOf(' ' + cls + ' ') < 0) { el.className = (cur + cls).trim() } } }, /** * remove class for IE9 */ removeClass: function (el, cls) { if (hasClassList) { el.classList.remove(cls) } else { var cur = ' ' + el.className + ' ', tar = ' ' + cls + ' ' while (cur.indexOf(tar) >= 0) { cur = cur.replace(tar, ' ') } el.className = cur.trim() } }, /** * Convert an object to Array * used in v-repeat and array filters */ objectToArray: function (obj) { var res = [], val, data for (var key in obj) { val = obj[key] data = utils.isObject(val) ? val : { $value: val } data.$key = key res.push(data) } return res } } enableDebug() function enableDebug () { /** * log for debugging */ utils.log = function (msg) { if (config.debug && console) { console.log(msg) } } /** * warnings, traces by default * can be suppressed by `silent` option. */ utils.warn = function (msg) { if (!config.silent && console) { console.warn(msg) if (config.debug && console.trace) { console.trace() } } } }