/** * Check if 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 strings to numbers * before setting back to data * * @param {*} value * @return {*|Number} */ exports.toNumber = function (value) { if (typeof value !== 'string') { return value } else { var parsed = Number(value) return isNaN(parsed) ? value : parsed } } /** * 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 } /** * Camelize a hyphen-delmited string. * * @param {String} str * @return {String} */ exports.camelize = function (str) { return str.replace(/-(\w)/g, toUpper) } function toUpper (_, c) { return c ? c.toUpperCase() : '' } /** * Hyphenate a camelCase string. * * @param {String} str * @return {String} */ exports.hyphenate = function (str) { return str .replace(/([a-z\d])([A-Z])/g, '$1-$2') .toLowerCase() } /** * 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 var OBJECT_STRING = '[object Object]' exports.isPlainObject = function (obj) { return toString.call(obj) === OBJECT_STRING } /** * 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) { var i = arr.length while (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 } /** * Check if two values are loosely equal - that is, * if they are plain objects, do they have the same shape? * * @param {*} a * @param {*} b * @return {Boolean} */ exports.looseEqual = function (a, b) { /* eslint-disable eqeqeq */ return a == b || ( exports.isObject(a) && exports.isObject(b) ? JSON.stringify(a) === JSON.stringify(b) : false ) /* eslint-enable eqeqeq */ }