/** * Check is a string starts with $ or _ * * @param {String} str * @return {Boolean} */ exports.isReserved = function (str) { var c = (str + '').charCodeAt(0) return c === 0x24 || c === 0x5F } /** * Guard text output, make sure undefined outputs * empty string * * @param {*} value * @return {String} */ exports.toString = function (value) { return value == null ? '' : value.toString() } /** * Check and convert possible numeric numbers before * setting back to data * * @param {*} value * @return {*|Number} */ exports.toNumber = function (value) { return ( isNaN(value) || value === null || typeof value === 'boolean' ) ? value : Number(value) } /** * Convert string boolean literals into real booleans. * * @param {*} value * @return {*|Boolean} */ exports.toBoolean = function (value) { return value === 'true' ? true : value === 'false' ? false : value } /** * Strip quotes from a string * * @param {String} str * @return {String | false} */ exports.stripQuotes = function (str) { var a = str.charCodeAt(0) var b = str.charCodeAt(str.length - 1) return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : false } /** * Replace helper * * @param {String} _ - matched delimiter * @param {String} c - matched char * @return {String} */ function toUpper (_, c) { return c ? c.toUpperCase () : '' } /** * Camelize a hyphen-delmited string. * * @param {String} str * @return {String} */ var camelRE = /-(\w)/g exports.camelize = function (str) { return str.replace(camelRE, toUpper) } /** * Converts hyphen/underscore/slash delimitered names into * camelized classNames. * * e.g. my-component => MyComponent * some_else => SomeElse * some/comp => SomeComp * * @param {String} str * @return {String} */ var classifyRE = /(?:^|[-_\/])(\w)/g exports.classify = function (str) { return str.replace(classifyRE, toUpper) } /** * Simple bind, faster than native * * @param {Function} fn * @param {Object} ctx * @return {Function} */ exports.bind = function (fn, ctx) { return function (a) { var l = arguments.length return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx) } } /** * Convert an Array-like object to a real Array. * * @param {Array-like} list * @param {Number} [start] - start index * @return {Array} */ exports.toArray = function (list, start) { start = start || 0 var i = list.length - start var ret = new Array(i) while (i--) { ret[i] = list[i + start] } return ret } /** * Mix properties into target object. * * @param {Object} to * @param {Object} from */ exports.extend = function (to, from) { for (var key in from) { to[key] = from[key] } return to } /** * Quick object check - this is primarily used to tell * Objects from primitive values when we know the value * is a JSON-compliant type. * * @param {*} obj * @return {Boolean} */ exports.isObject = function (obj) { return obj !== null && typeof obj === 'object' } /** * Strict object type check. Only returns true * for plain JavaScript objects. * * @param {*} obj * @return {Boolean} */ var toString = Object.prototype.toString exports.isPlainObject = function (obj) { return toString.call(obj) === '[object Object]' } /** * Array type check. * * @param {*} obj * @return {Boolean} */ exports.isArray = Array.isArray /** * Define a non-enumerable property * * @param {Object} obj * @param {String} key * @param {*} val * @param {Boolean} [enumerable] */ exports.define = function (obj, key, val, enumerable) { Object.defineProperty(obj, key, { value : val, enumerable : !!enumerable, writable : true, configurable : true }) } /** * Debounce a function so it only gets called after the * input stops arriving after the given wait period. * * @param {Function} func * @param {Number} wait * @return {Function} - the debounced function */ exports.debounce = function(func, wait) { var timeout, args, context, timestamp, result var later = function() { var last = Date.now() - timestamp if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last) } else { timeout = null result = func.apply(context, args) if (!timeout) context = args = null } } return function() { context = this args = arguments timestamp = Date.now() if (!timeout) { timeout = setTimeout(later, wait) } return result } } /** * Manual indexOf because it's slightly faster than * native. * * @param {Array} arr * @param {*} obj */ exports.indexOf = function (arr, obj) { for (var i = 0, l = arr.length; i < l; i++) { if (arr[i] === obj) return i } return -1 } /** * Make a cancellable version of an async callback. * * @param {Function} fn * @return {Function} */ exports.cancellable = function (fn) { var cb = function () { if (!cb.cancelled) { return fn.apply(this, arguments) } } cb.cancel = function () { cb.cancelled = true } return cb }