Skip to content

Instantly share code, notes, and snippets.

@espretto
Last active June 5, 2016 22:01
Show Gist options
  • Save espretto/c9a8961645e2754155af to your computer and use it in GitHub Desktop.
Save espretto/c9a8961645e2754155af to your computer and use it in GitHub Desktop.
JavaScript: type/class/node check functions
/*!
* 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