var config = require('./config'), toString = ({}).toString, win = window, console = win.console, timeout = win.setTimeout, THIS_RE = /[^\w]this[^\w]/, hasClassList = 'classList' in document.documentElement, ViewModel // late def var utils = module.exports = { /** * get a value from an object keypath */ get: function (obj, key) { /* jshint eqeqeq: false */ 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 */ 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) { if (obj.hasOwnProperty(key)) return Object.defineProperty(obj, key, { value : val, enumerable : !!enumerable, writable : !!writable, configurable : true }) }, /** * Accurate type check * internal use only, so no need to check for NaN */ typeOf: function (obj) { return toString.call(obj).slice(8, -1) }, /** * 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, protective) { for (var key in ext) { if ((protective && obj[key]) || obj[key] === ext[key]) continue 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 a string template to a dom fragment */ toFragment: function (template) { if (typeof template !== 'string') { return template } if (template.charAt(0) === '#') { var templateNode = document.getElementById(template.slice(1)) if (!templateNode) return template = templateNode.innerHTML } var node = document.createElement('div'), frag = document.createDocumentFragment(), child node.innerHTML = template.trim() /* jshint boss: true */ while (child = node.firstChild) { if (node.nodeType === 1) { frag.appendChild(child) } } return frag }, /** * Convert the object to a ViewModel constructor * if it is not already one */ toConstructor: function (obj) { ViewModel = ViewModel || require('./viewmodel') return utils.typeOf(obj) === 'Object' ? 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.toFragment(partials[key]) } } if (filters) { for (key in filters) { utils.checkFilter(filters[key]) } } if (template) { options.template = utils.toFragment(template) } }, /** * used to defer batch updates */ nextTick: function (cb) { timeout(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.typeOf(val) === 'Object' ? 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(msg) } } } }