is-extended
Version:
Module for type detection
501 lines (428 loc) • 15.7 kB
JavaScript
(function () {
"use strict";
function defineIsa(extended) {
var pSlice = Array.prototype.slice;
var hasOwn = Object.prototype.hasOwnProperty;
var toStr = Object.prototype.toString;
function argsToArray(args, slice) {
var i = -1, j = 0, l = args.length, ret = [];
slice = slice || 0;
i += slice;
while (++i < l) {
ret[j++] = args[i];
}
return ret;
}
function keys(obj) {
var ret = [];
for (var i in obj) {
if (hasOwn.call(obj, i)) {
ret.push(i);
}
}
return ret;
}
//taken from node js assert.js
//https://github.com/joyent/node/blob/master/lib/assert.js
function deepEqual(actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
} else if (typeof Buffer !== "undefined" && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
if (actual.length !== expected.length) {
return false;
}
for (var i = 0; i < actual.length; i++) {
if (actual[i] !== expected[i]) {
return false;
}
}
return true;
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
} else if (isDate(actual) && isDate(expected)) {
return actual.getTime() === expected.getTime();
// 7.3 If the expected value is a RegExp object, the actual value is
// equivalent if it is also a RegExp object with the same source and
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
} else if (isRegExp(actual) && isRegExp(expected)) {
return actual.source === expected.source &&
actual.global === expected.global &&
actual.multiline === expected.multiline &&
actual.lastIndex === expected.lastIndex &&
actual.ignoreCase === expected.ignoreCase;
// 7.4. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
} else if (isString(actual) && isString(expected) && actual !== expected) {
return false;
} else if (typeof actual !== 'object' && typeof expected !== 'object') {
return actual === expected;
// 7.5 For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return objEquiv(actual, expected);
}
}
function objEquiv(a, b) {
var key;
if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) {
return false;
}
// an identical 'prototype' property.
if (a.prototype !== b.prototype) {
return false;
}
//~~~I've managed to break Object.keys through screwy arguments passing.
// Converting to array solves the problem.
if (isArguments(a)) {
if (!isArguments(b)) {
return false;
}
a = pSlice.call(a);
b = pSlice.call(b);
return deepEqual(a, b);
}
try {
var ka = keys(a),
kb = keys(b),
i;
// having the same number of owned properties (keys incorporates
// hasOwnProperty)
if (ka.length !== kb.length) {
return false;
}
//the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
//~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] !== kb[i]) {
return false;
}
}
//equivalent values for every corresponding key, and
//~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!deepEqual(a[key], b[key])) {
return false;
}
}
} catch (e) {//happens when one is a string literal and the other isn't
return false;
}
return true;
}
var isFunction = function (obj) {
return toStr.call(obj) === '[object Function]';
};
//ie hack
if ("undefined" !== typeof window && !isFunction(window.alert)) {
(function (alert) {
isFunction = function (obj) {
return toStr.call(obj) === '[object Function]' || obj === alert;
};
}(window.alert));
}
function isObject(obj) {
var undef;
return obj !== null && typeof obj === "object";
}
function isHash(obj) {
var ret = isObject(obj);
return ret && obj.constructor === Object && !obj.nodeType && !obj.setInterval;
}
function isEmpty(object) {
if (isArguments(object)) {
return object.length === 0;
} else if (isObject(object)) {
return keys(object).length === 0;
} else if (isString(object) || isArray(object)) {
return object.length === 0;
}
return true;
}
function isBoolean(obj) {
return obj === true || obj === false || toStr.call(obj) === "[object Boolean]";
}
function isUndefined(obj) {
return typeof obj === 'undefined';
}
function isDefined(obj) {
return !isUndefined(obj);
}
function isUndefinedOrNull(obj) {
return isUndefined(obj) || isNull(obj);
}
function isNull(obj) {
return obj === null;
}
var isArguments = function _isArguments(object) {
return toStr.call(object) === '[object Arguments]';
};
if (!isArguments(arguments)) {
isArguments = function _isArguments(obj) {
return !!(obj && hasOwn.call(obj, "callee"));
};
}
function isInstanceOf(obj, clazz) {
if (isFunction(clazz)) {
return obj instanceof clazz;
} else {
return false;
}
}
function isRegExp(obj) {
return toStr.call(obj) === '[object RegExp]';
}
var isArray = Array.isArray || function isArray(obj) {
return toStr.call(obj) === "[object Array]";
};
function isDate(obj) {
return toStr.call(obj) === '[object Date]';
}
function isString(obj) {
return toStr.call(obj) === '[object String]';
}
function isNumber(obj) {
return toStr.call(obj) === '[object Number]';
}
function isTrue(obj) {
return obj === true;
}
function isFalse(obj) {
return obj === false;
}
function isNotNull(obj) {
return !isNull(obj);
}
function isEq(obj, obj2) {
/*jshint eqeqeq:false*/
return obj == obj2;
}
function isNeq(obj, obj2) {
/*jshint eqeqeq:false*/
return obj != obj2;
}
function isSeq(obj, obj2) {
return obj === obj2;
}
function isSneq(obj, obj2) {
return obj !== obj2;
}
function isIn(obj, arr) {
if ((isArray(arr) && Array.prototype.indexOf) || isString(arr)) {
return arr.indexOf(obj) > -1;
} else if (isArray(arr)) {
for (var i = 0, l = arr.length; i < l; i++) {
if (isEq(obj, arr[i])) {
return true;
}
}
}
return false;
}
function isNotIn(obj, arr) {
return !isIn(obj, arr);
}
function isLt(obj, obj2) {
return obj < obj2;
}
function isLte(obj, obj2) {
return obj <= obj2;
}
function isGt(obj, obj2) {
return obj > obj2;
}
function isGte(obj, obj2) {
return obj >= obj2;
}
function isLike(obj, reg) {
if (isString(reg)) {
return ("" + obj).match(reg) !== null;
} else if (isRegExp(reg)) {
return reg.test(obj);
}
return false;
}
function isNotLike(obj, reg) {
return !isLike(obj, reg);
}
function contains(arr, obj) {
return isIn(obj, arr);
}
function notContains(arr, obj) {
return !isIn(obj, arr);
}
function containsAt(arr, obj, index) {
if (isArray(arr) && arr.length > index) {
return isEq(arr[index], obj);
}
return false;
}
function notContainsAt(arr, obj, index) {
if (isArray(arr)) {
return !isEq(arr[index], obj);
}
return false;
}
function has(obj, prop) {
return hasOwn.call(obj, prop);
}
function notHas(obj, prop) {
return !has(obj, prop);
}
function length(obj, l) {
if (has(obj, "length")) {
return obj.length === l;
}
return false;
}
function notLength(obj, l) {
if (has(obj, "length")) {
return obj.length !== l;
}
return false;
}
var isa = {
isFunction: isFunction,
isObject: isObject,
isEmpty: isEmpty,
isHash: isHash,
isNumber: isNumber,
isString: isString,
isDate: isDate,
isArray: isArray,
isBoolean: isBoolean,
isUndefined: isUndefined,
isDefined: isDefined,
isUndefinedOrNull: isUndefinedOrNull,
isNull: isNull,
isArguments: isArguments,
instanceOf: isInstanceOf,
isRegExp: isRegExp,
deepEqual: deepEqual,
isTrue: isTrue,
isFalse: isFalse,
isNotNull: isNotNull,
isEq: isEq,
isNeq: isNeq,
isSeq: isSeq,
isSneq: isSneq,
isIn: isIn,
isNotIn: isNotIn,
isLt: isLt,
isLte: isLte,
isGt: isGt,
isGte: isGte,
isLike: isLike,
isNotLike: isNotLike,
contains: contains,
notContains: notContains,
has: has,
notHas: notHas,
isLength: length,
isNotLength: notLength,
containsAt: containsAt,
notContainsAt: notContainsAt
};
var tester = {
constructor: function () {
this._testers = [];
},
noWrap: {
tester: function () {
var testers = this._testers;
return function tester(value) {
var isa = false;
for (var i = 0, l = testers.length; i < l && !isa; i++) {
isa = testers[i](value);
}
return isa;
};
}
}
};
var switcher = {
constructor: function () {
this._cases = [];
this.__default = null;
},
def: function (val, fn) {
this.__default = fn;
},
noWrap: {
switcher: function () {
var testers = this._cases, __default = this.__default;
return function tester() {
var handled = false, args = argsToArray(arguments), caseRet;
for (var i = 0, l = testers.length; i < l && !handled; i++) {
caseRet = testers[i](args);
if (caseRet.length > 1) {
if (caseRet[1] || caseRet[0]) {
return caseRet[1];
}
}
}
if (!handled && __default) {
return __default.apply(this, args);
}
};
}
}
};
function addToTester(func) {
tester[func] = function isaTester() {
this._testers.push(isa[func]);
};
}
function addToSwitcher(func) {
switcher[func] = function isaTester() {
var args = argsToArray(arguments, 1), isFunc = isa[func], handler, doBreak = true;
if (args.length <= isFunc.length - 1) {
throw new TypeError("A handler must be defined when calling using switch");
} else {
handler = args.pop();
if (isBoolean(handler)) {
doBreak = handler;
handler = args.pop();
}
}
if (!isFunction(handler)) {
throw new TypeError("handler must be defined");
}
this._cases.push(function (testArgs) {
if (isFunc.apply(isa, testArgs.concat(args))) {
return [doBreak, handler.apply(this, testArgs)];
}
return [false];
});
};
}
for (var i in isa) {
if (hasOwn.call(isa, i)) {
addToSwitcher(i);
addToTester(i);
}
}
var is = extended.define(isa).expose(isa);
is.tester = extended.define(tester);
is.switcher = extended.define(switcher);
return is;
}
if ("undefined" !== typeof exports) {
if ("undefined" !== typeof module && module.exports) {
module.exports = defineIsa(require("extended"));
}
} else if ("function" === typeof define && define.amd) {
define(["extended"], function (extended) {
return defineIsa(extended);
});
} else {
this.isExtended = defineIsa(this.extended);
}
}).call(this);