Last active
June 5, 2016 22:01
-
-
Save espretto/c9a8961645e2754155af to your computer and use it in GitHub Desktop.
JavaScript: type/class/node check functions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* is, released under MIT licence | |
* http://mariusrunge.com/mit-licence.html | |
* inspired by / partially taken from underscorejs.org | |
* | |
* how to use: | |
* - comment the code-sections and/or `classNames` you don't need | |
* - integrate `is` into your lib via the `.noConflict()` option | |
* - eventually clean up the global namespace via `delete window.is` (try-catch for IE) | |
* | |
* and off you go with some uniform type checking functions! | |
*/ | |
(function(root) { | |
// setup | |
// ----- | |
var // one to var them all | |
is = {}, | |
// this also represents the api e.g. `is.Undefined(your_input)` | |
classNames = [ | |
// -------------------- | |
// JavaScript natives | |
// -------------------- | |
'Arguments', | |
'Array', | |
'Boolean', | |
'Date', | |
'Function', | |
'Object', | |
'RegExp', | |
'String' | |
// NaN | |
// Null | |
// Number | |
// Undefined | |
// -------------------- | |
// convenience wrappers | |
// -------------------- | |
// None | |
// Numeric | |
// Arrayish | |
// Thenable | |
// Primitive | |
// -------------------- | |
// DOM nodes | |
// -------------------- | |
// Node | |
// ElementNode | |
// AttributeNode | |
// TextNode | |
// CDATASectionNode | |
// EntityReferenceNode | |
// EntityNode | |
// ProcessingInstructionNode | |
// CommentNode | |
// DocumentNode | |
// DocumentTypeNode | |
// DocumentFragmentNode | |
// NotationNode | |
], | |
// typeof strings | |
typeString = 'string', | |
typeObject = 'object', | |
typeFunction = 'function', | |
// shortcuts to natives | |
arrayForEach = classNames.forEach || function(iterator) { | |
// no spec compliance intended - for internal use only! | |
for (var array = this, i = array.length; i--;) iterator(array[ i ], i); | |
}, | |
objectToString = is.toString, | |
stringClassCheck, | |
booleanClassCheck; | |
// generate class checks | |
// --------------------- | |
arrayForEach.call(classNames, function(className) { | |
// cache representation within forEach scope | |
var classRepr = '[object ' + className + ']'; | |
// exclude falsies directly | |
is[ className ] = function(value) { | |
return value && objectToString.call(value) === classRepr; | |
}; | |
}); | |
// main | |
// ---- | |
// override with native `isArray` if available | |
is.Array = Array.isArray || is.Array; | |
// old webkit says 'function' | |
if (typeof /r/ === typeObject) { | |
is.Function = function(value) { | |
return typeof value === typeFunction; | |
}; | |
} | |
// fallback to duck typing | |
(function(){ | |
if (!is.Arguments(arguments)) { | |
is.Arguments = function(value) { | |
return value && is.Function(value.callee); | |
} | |
} | |
}()); | |
// shortcut for primitives via typeof | |
stringClassCheck = is.String; | |
is.String = function(value){ | |
return typeof value === typeString || stringClassCheck(value); | |
}; | |
// shortcut for primitives via type-safe comparison | |
booleanClassCheck = is.Boolean; | |
is.Boolean = function(value){ | |
return value === !!value || booleanClassCheck(value); | |
}; | |
// parse object to primitive first. | |
// catch `NaN` being the only value that doesn't equal itself. | |
is.Number = function(value){ | |
return (+value) === value || value !== value; | |
}; | |
// `undefined` is overridable in legacy browsers. | |
// this is not always a replacement for `typeof noname === 'undefined'`. | |
// the given value must have been assigned a name either by `var` or arguments. | |
is.Undefined = function(value) { | |
return value === void 0; | |
}; | |
// `null` points to the empty object and | |
// `undefined` is its primitive value | |
is.Null = function(value) { | |
return value === null; | |
}; | |
// matches both `null` and `undefined` just like | |
// ```js | |
// 'foo' == new String('foo'); // true | |
// 'foo' === new String('foo'); // false | |
// ``` | |
is.None = function(value) { | |
return value == null; | |
}; | |
// strictly speaking `null` should not be considered a primitive | |
// but only its value `undefined`. stick to the [spec][1] though. | |
// [1]: http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.2 | |
is.Primitive = function(value){ | |
var type = typeof value; | |
return value == null || type !== typeObject && type !== typeFunction; | |
}; | |
// test if parsable | |
is.Numeric = function (value) { | |
return (value = +value) === value | |
} | |
// whether or not generic Array methods can be applied. | |
// to allow strings as well use `value != null` instead of `!is.Primitive(value)` | |
is.Arrayish = function(value){ | |
return !is.Primitive(value) && is.Number(value.length); | |
} | |
// [Promises/A+][1] compliant | |
// - If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason. [link][2] | |
// - If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where: ... [link][3] | |
// [1]: http://promises-aplus.github.io/promises-spec/ | |
// [2]: http://promises-aplus.github.io/promises-spec/#point-55 | |
// [3]: http://promises-aplus.github.io/promises-spec/#point-56 | |
is.Thenable = function(value){ | |
try { | |
return !is.Primitive(value) && is.Function(value.then); | |
} catch (e){ | |
// fall through | |
} | |
return false; | |
} | |
// generate node checks | |
// -------------------- | |
is.Node = function(value){ | |
return value != null && value.nodeType; // never zero | |
} | |
// the index represents the node's `nodeType - 1` property | |
// as of [w3c-spec](http://www.w3schools.com/dom/dom_nodetype.asp). | |
// produce functions: `is.ElementNode()`, `is.AttributeNode()`, ... | |
var nodeClassNames = [ | |
'Element', | |
'Attribute', | |
'Text', | |
'CDATASection', | |
'EntityReference', | |
'Entity', | |
'ProcessingInstruction', | |
'Comment', | |
'Document', | |
'DocumentType', | |
'DocumentFragment', | |
'Notation' | |
]; | |
arrayForEach.call(nodeClassNames, function(nodeClassName, nodeType){ | |
nodeType += 1; | |
nodeClassName += 'Node'; | |
is[nodeClassName] = function(value){ | |
return value && value.nodeType === nodeType; | |
}; | |
}); | |
// export | |
// ------ | |
// | |
// - cjs | |
// - amd - anonymous | |
// - browser - opt to rename | |
if (typeof module === typeObject && module.exports) { | |
module.exports = is; | |
} else if (typeof define === typeFunction && define.amd) { | |
define(function() { | |
return is; | |
}); | |
} else { | |
// to clean up the global namespace completely | |
// embed this after calling `.noConflict()`. | |
// ``` | |
// try{ | |
// delete window.is; // IE fails | |
// } catch(e){} | |
// ``` | |
var previousIs = root.is; | |
is.noConflict = function() { | |
root.is = previousIs; | |
return is; | |
} | |
root.is = is; | |
} | |
}(this)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment