aid.js
Version:
A Bundle of Javascript util Library to help developers. No dependency to other Libraries.
5,281 lines • 158 kB
JavaScript
/*
* aid.js 0.1.96
* https://www.npmjs.com/package/aid.js
*
* The MIT License (MIT)
* Copyright (c) 2016-2019 Hyun-Seok.Kim, dragmove@gmail.com
*/
(function() {
'use strict';
// Establish the global object, `window` (`self`) in the browser, `global`
// on the server, or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
var global =
(typeof self == 'object' && self.self === self && self) ||
(typeof global == 'object' && global.global === global && global) ||
this ||
{};
var _slice = Array.prototype.slice;
var _hasOwnProperty = Object.prototype.hasOwnProperty;
var aid = {},
operator = {},
platform = {},
browser = {},
string = {},
math = {},
date = {},
array = {},
object = {},
element = {},
file = {},
clipboard = {},
monad = {}; // https://en.wikipedia.org/wiki/Monad_(functional_programming)
/**
* is null/undefined or other
*
* @static
* @method existy
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.existy(undefined) ); // false
* console.log( aid.existy(null) ); // false
* console.log( aid.existy('') ); // true
*/
aid.existy = function existy(any) {
return any != null;
};
/**
* check defined
*
* @static
* @method isDefined
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isDefined(undefined) ); // false
* console.log( aid.isDefined(null) ); // false
* console.log( aid.isDefined(0) ); // true
*/
aid.isDefined = function isDefined(any) {
if (any === null || typeof any === 'undefined') return false;
return true;
};
/**
* check type is Boolean
*
* @static
* @method isBoolean
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isBoolean(false) ); // true
*/
aid.isBoolean = function isBoolean(any) {
if (!aid.isDefined(any)) return false;
return any.constructor === Boolean;
};
/**
* check type is Number
*
* @static
* @method isNumber
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isNumber(-1) ); // true
*/
aid.isNumber = function isNumber(any) {
if (!aid.isDefined(any)) return false;
return !isNaN(any) && any.constructor === Number;
};
/**
* check type is Integer Number
*
* @static
* @method isInteger
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isInteger(-1) ); // true
*/
aid.isInteger = function isInteger(any) {
if (!aid.isNumber(any)) return false;
// https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
return isFinite(any) && Math.floor(any) === any;
};
/**
* check type is String
*
* @static
* @method isString
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isString('') ); // true
*/
aid.isString = function isString(any) {
if (!aid.isDefined(any)) return false;
return any.constructor === String;
};
/**
* check type is Array
*
* @static
* @method isArray
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isArray([]) ); // true
*/
aid.isArray = function isArray(any) {
if (!aid.isDefined(any)) return false;
return any.constructor === Array;
};
/**
* check type is Object
*
* @static
* @method isObject
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isObject({}) ); // true
*/
aid.isObject = function isObject(any) {
if (!aid.isDefined(any)) return false;
return any.constructor === Object;
};
/**
* check type is Function
*
* @static
* @method isFunction
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isFunction(function(){}) ); // true
*/
aid.isFunction = function isFunction(any) {
if (!aid.isDefined(any)) return false;
return any.constructor === Function;
};
/**
* check type is RegExp
*
* @static
* @method isRegExp
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isRegExp(new RegExp('^aid')) ); // true
* console.log( aid.isRegExp(/^aid/) ); // true
*/
aid.isRegExp = function isRegExp(any) {
if (!aid.isDefined(any)) return false;
return any.constructor === RegExp;
};
/**
* check type is Error (can check Error, EvalError, InternalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError)
* refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
*
* @static
* @method isError
* @param {*} any
* @param {Constructor} errorType (optional)
* @returns {Boolean} return boolean
* @example
* console.log( aid.isError(new Error('msg')) ); // true
* console.log( aid.isError(new TypeError('msg')) ); // true
* console.log( aid.isError(new RangeError('msg')) ); // true
* console.log( aid.isError(new TypeError('msg'), TypeError) ); // true
* console.log( aid.isError(new TypeError('msg'), Error) ); // false
* console.log( aid.isError(new TypeError('msg'), RangeError) ); // false
*/
aid.isError = function isError(any, errorType) {
if (!aid.isDefined(any)) return false;
var con = any.constructor;
if (!aid.isDefined(errorType)) {
// Non-standard - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/InternalError
if (window.InternalError && con === window.InternalError) return true;
return (
con === Error ||
con === EvalError ||
con === RangeError ||
con === ReferenceError ||
con === SyntaxError ||
con === TypeError ||
con === URIError
);
}
return con === errorType;
};
/**
* check type is HTML element
* refer to https://developer.mozilla.org/en-US/docs/Web/API/Element
*
* @static
* @method isElement
* @param {*} any
* @returns {Boolean} return boolean
* @example
* console.log( aid.isElement(document.createElement('div')) ); // true
*/
aid.isElement = function isElement(any) {
return aid.isDefined(any) && typeof any === 'object' && any.nodeType === 1 && any instanceof Node;
};
/**
* extend function
*
* @static
* @method extend
* @param {Object} prototype of class function, or object has variables or methods.
* @param {Object} object has variables or methods
* @example
* function Destination() {
* this.name = 'destination';
* this.type = 'json';
* };
*
* var source = {
* getName: function() { return this.name; },
* getType: function() { return this.type; }
* };
*
* aid.extend(Destination.prototype, source);
*
* var destination = new Destination();
* console.log( destination.getName() ); // 'destination'
* console.log( destination.getType() ); // 'json'
*/
aid.extend = function extend(destination, source) {
if (!(destination instanceof Object) || !(typeof destination === 'object')) {
throw new TypeError('[aid.extend] Type of destination parameter must be instance of Object, and object.');
}
if (!(typeof source === 'object')) throw new TypeError('[aid.extend] Type of source parameter must be Object.');
for (var key in source) {
if (source.hasOwnProperty(key)) {
destination[key] = source[key];
}
}
return destination;
};
/**
* inherit Class function
*
* @static
* @method inherit
* @param {class} ChildClass - child class function
* @param {class} ParentClass - parent class function
* @example
*/
aid.inherit = (function() {
// use closure, protect gabarge collection.
var F = function() {};
return function inherit(ChildClass, ParentClass) {
F.prototype = ParentClass.prototype;
ChildClass.prototype = new F();
ChildClass.prototype.constructor = ChildClass;
ChildClass.super = ParentClass.prototype;
};
})();
/**
* create namespace
*
* @static
* @method namespace
* @param {String} namespace
* @param {Object} parent
* @returns {Object} return object
* @example
* aid.namespace('first.second.third'); // create first.second.third object
*/
aid.namespace = function(namespace, parent) {
if (!aid.isString(namespace)) throw new TypeError('[aid.namespace] Type of namespace parameter must be String.');
if (!(aid.isObject(parent) || !aid.isDefined(parent))) {
throw new TypeError('[aid.namespace] Type of parent parameter must be Object or null or undefined.');
}
var ns = parent || global;
if (namespace) {
var parts = namespace.split('.');
parts.forEach(function(part) {
if (!ns[part]) ns[part] = {};
ns = ns[part];
});
}
return ns;
};
/**
* return memoized function
*
* @static
* @method memoize
* @param {Function} func
* @param {Function} hasher (optional)
* @returns {Function} return function
* @example
* var plus = function(a, b) { return a + b; };
* var memoized = aid.memoize(plus);
* console.log( memoized('aid', '.js') ); // aid.js
*/
aid.memoize = function memoize(func, hasher) {
if (!aid.isFunction(func)) throw new TypeError('[aid.memoize] Type of func parameter must be Function.');
if (aid.isDefined(hasher) && !aid.isFunction(hasher))
throw new TypeError('[aid.memoize] Type of hasher parameter must be undefined or null or Function.');
var memoized = function(_key) {
var cache = memoized.cache;
var key = hasher ? hasher.apply(null, _slice.call(arguments)) : _key;
if (cache.has(key)) return cache.get(key);
var result = func.apply(null, _slice.call(arguments));
cache.set(key, result);
return result;
};
memoized.cache = aid.createDictionary();
return memoized;
};
/**
* borrow method from donor object
*
* @static
* @method borrow
* @param {Object} borrower
* @param {Object} donor
* @param {String} funcName
* @example
* var borrower = {}, donor = { say: function() { return 'hello, world'; } };
* aid.borrow(borrower, donor, 'say');
* console.log( borrower.say() ); // 'hello, world'
*/
aid.borrow = function borrow(borrower, donor, funcName) {
if (!aid.isObject(borrower) || !aid.isObject(donor))
throw new TypeError('[aid.borrow] Type of borrower, donor parameters must be Object.');
if (!aid.isString(funcName)) throw new TypeError('[aid.borrow] Type of funcName parameter must be String.');
if (!aid.isDefined(donor[funcName]))
throw new Error('[aid.borrow] donor object parameter has not function with funcName parameter.');
if (aid.isDefined(borrower[funcName]))
throw new Error('[aid.borrow] borrower object parameter already has function with funcName parameter.');
borrower[funcName] = function() {
var args = _slice.call(arguments);
return donor[funcName].apply(this, args);
};
};
/**
* return function bind context and parameters
*
* @static
* @method bind
* @param {Function} func
* @param {Object} context
* @returns {Function} return function
* @example
* this.title = 'global - aid.js';
* var obj = { title: 'obj - aid.js', getTitle: function() { return this.title; } };
* var getObjTitle = bind(obj.getTitle, obj), getGlobalTitle = bind(obj.getTitle, window);
* console.log( getObjTitle() ); // 'obj - aid.js';
* console.log( getGlobalTitle() ); // 'global - aid.js';
*/
aid.bind = function bind(func, context) {
if (!aid.isFunction(func)) throw new TypeError('[aid.bind] Type of func parameter must be Function.');
return function() {
return func.apply(context, arguments);
};
};
/**
* return function composed function_a and function_b
*
* @static
* @method compose
* @param {Function} func_a
* @param {Function} func_b
* @returns {Function} return function
* @example
* var isNotNaN = aid.compose(aid.operator['!'], isNaN);
* console.log( isNotNaN(0) ); // true
*/
aid.compose = function compose(func_a, func_b) {
if (!aid.isFunction(func_a) || !aid.isFunction(func_b)) {
throw new TypeError('[aid.compose] Type of func_a, func_b parameters must be Function.');
}
return function() {
return func_a(func_b.apply(null, arguments));
};
};
/**
* return function negate object parameter
*
* @static
* @method not
* @param {Function} func
* @returns {Function} return function
* @example
* var isNotNaN = aid.not(isNaN);
* console.log( isNotNaN(0) ); // true
*/
aid.not = function not(func) {
if (!aid.isFunction(func)) throw new TypeError('[aid.not] Type of func parameter must be Function.');
return function() {
return !func.apply(null, arguments);
};
};
/**
* each method for data can loop
*
* @static
* @method each
* @param {(Array|String)} iterable
* @param {Function} func
* @param {Object} context
* @example
* var result = [];
* aid.each('aid.js', function(v) { result.push(v); }, null);
* console.log( result.join('') ); // 'aid.js'
*/
aid.each = function each(iterable, func, context) {
if (!(aid.isArray(iterable) || aid.isString(iterable))) {
throw new TypeError('[aid.each] Type of iterable parameter must be Array or String.');
}
var _context = aid.existy(context) ? context : null;
for (var i = 0, max = iterable.length; i < max; i++) {
func.call(_context, iterable[i]);
}
};
/**
* check object is truthy
*
* @static
* @method truthy
* @param {Object} obj
* @returns {Boolean} return boolean
* @example
* console.log( aid.truthy(true) ); // true
* console.log( aid.truthy([]) ); // true
* console.log( aid.truthy(0) ); // false
* console.log( aid.truthy('') ); // false
*/
aid.truthy = function truthy(obj) {
return !!obj;
};
/**
* check object is falsy
*
* @static
* @method falsy
* @param {Object} obj
* @returns {Boolean} return boolean
* @example
* console.log( aid.falsy(true) ); // false
* console.log( aid.falsy([]) ); // false
* console.log( aid.falsy(0) ); // true
* console.log( aid.falsy('') ); // true
*/
aid.falsy = function falsy(obj) {
return !!!obj;
};
/**
* get indexed value of array or string
*
* @static
* @method nth
* @param {Array|String} iterable
* @param {Number} index
* @example
* console.log( aid.nth('string', 1) ); // 't'
* console.log( aid.nth('string', -1) ); // null
* console.log( aid.nth([0, 'str', true], 2) ); // true
* console.log( aid.nth([0, 'str', true], 99) ); // null
*/
aid.nth = function nth(iterable, index) {
if (!(aid.isArray(iterable) || aid.isString(iterable))) {
throw new TypeError('[aid.nth] Type of iterable parameter must be Array or String.');
}
if (!aid.isInteger(index)) throw new TypeError('[aid.nth] Type of index parameter must be Integer Number.');
return index < 0 || index > iterable.length - 1 ? null : iterable[index];
};
/**
* check all arguments are true
*
* @static
* @method allOf
* @returns {Boolean} return boolean
* @example
* console.log( aid.allOf(true, true) ); // true
* console.log( aid.allOf(true, false) ); // false
*/
aid.allOf = function allOf(/*args*/) {
var args = _slice.call(arguments);
return args.every(function(val) {
return val === true;
});
};
/**
* check some argument is true
*
* @static
* @method anyOf
* @returns {Boolean} return boolean
* @example
* console.log( anyOf(true, false) ); // true
* console.log( anyOf(false, false) ); // false
*/
aid.anyOf = function anyOf(/*args*/) {
var args = _slice.call(arguments);
return args.some(function(val) {
return val === true;
});
};
/**
* return function always return constant value
* http://underscorejs.org/#constant
*
* @static
* @method constant
* @param {Object} obj
* @returns {Function} return function
* @example
* var obj = {name: 'aid.js'};
* console.log( aid.constant(obj)() === obj ); // true
*/
aid.constant = function constant(obj) {
return function() {
return obj;
};
};
/**
* return function pluck field of object, array, string
*
* @static
* @method plucker
* @param {(String|Number)} field of object, array, string
* @returns {Function} return function
* @example
* var getTitle = aid.plucker('title');
* var obj = {title: 'aid.js', description: 'A bundle of Javascript util Library for help developers. No dependency to other Libraries.'};
* console.log( getTitle(obj) ); // 'aid.js'
*
* var getFirst = aid.plucker(0);
* var str = 'aid.js';
* console.log( getFirst(str) ); // 'a'
*
* var getLength = aid.plucker('length');
* var arr = [1, 2, 3, 4, 5];
* console.log( getLength(arr) ); // 5
*/
aid.plucker = function plucker(field) {
if (!(aid.isString(field) || aid.isNumber(field))) {
throw new TypeError('[aid.plucker] Type of field parameter must be String or Number.');
}
return function(obj) {
if (!(aid.isObject(obj) || aid.isArray(obj) || aid.isString(obj))) {
throw new TypeError('[aid.plucker] Type of obj parameter must be Object or Array or String.');
}
return obj[field];
};
};
/**
* return best(optimized by condition function) value
*
* @static
* @method best
* @param {Function} condition function to find best value
* @param {Array} array
* @example
* console.log( aid.best(function(x, y) { return x > y; }, [2, 4, 1, 5, 3]) ); // 5
*/
aid.best = function best(conditionFunc, array) {
if (!aid.isFunction(conditionFunc))
throw new TypeError('[aid.best] Type of conditionFunc parameter must be Function.');
if (!aid.isArray(array)) throw new TypeError('[aid.best] Type of array parameter must be Array.');
return array.reduce(function(previousValue, currentValue) {
return conditionFunc(previousValue, currentValue) ? previousValue : currentValue;
});
};
/**
* return array has values filtered
*
* @static
* @method iterateUntil
* @param {Function} function return value
* @param {Function} function has condition
* @param {Object} initial value
* @returns {Array} return array has values filtered
* @example
* console.log( aid.iterateUntil(function(n) { return n + n; }, function(n) { return n <= 1042 }, 1) ); // [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
*/
aid.iterateUntil = function iterateUntil(calculateFunc, conditionFunc, initialValue) {
if (!aid.isFunction(calculateFunc))
throw new TypeError('[aid.iterateUntil] Type of calculateFunc parameter must be Function.');
if (!aid.isFunction(conditionFunc))
throw new TypeError('[aid.iterateUntil] Type of conditionFunc parameter must be Function.');
var array = [],
result = calculateFunc(initialValue);
while (conditionFunc(result)) {
array.push(result);
result = calculateFunc(result);
}
return array;
};
/**
* curry function can use one parameter
*
* @static
* @method curry
* @param {Function} function
* @returns {Function} return function
* @example
*/
aid.curry = function curry(func) {
if (!aid.isFunction(func)) throw new TypeError('[aid.curry] Type of func parameter must be Function.');
return function(arg) {
return func(arg);
};
};
/**
* curry function can use two parameter
*
* @static
* @method curry2
* @param {Function} function
* @returns {Function} return function
* @example
* var parseBinaryStr = aid.curry2(parseInt)(2);
* console.log( parseBinaryStr('111') ); // 7
*/
aid.curry2 = function curry2(func) {
if (!aid.isFunction(func)) throw new TypeError('[aid.curry2] Type of func parameter must be Function.');
return function(secondArg) {
return function(firstArg) {
return func(firstArg, secondArg);
};
};
};
/**
* curry function for variadic functions
*
* @static
* @method curryAll
* @param {Function} function
* @param {Number} minimum number of arguments to curry
* @returns {Function} return function
* @example
* console.log( curryAll(Math.max, 2)(1, 99) ); // 99
* console.log( curryAll(Math.max, 2)(1)(99) ); // 99
*/
aid.curryAll = function curryAll(func, curryArgsNum) {
if (!aid.isFunction(func)) throw new TypeError('[aid.curryAll] Type of func parameter must be Function.');
var arity = curryArgsNum || func.length;
return function curried() {
var args = _slice.call(arguments),
context = this;
return args.length >= arity
? func.apply(context, args)
: function() {
var rest = _slice.call(arguments);
return curried.apply(context, args.concat(rest));
};
};
};
/**
* reverse arguments of function
* refer to https://github.com/getify/Functional-Light-JS/blob/master/manuscript/ch3.md/#reversing-arguments
*
* @static
* @method reverseArgs
* @param {Function} function
* @returns {Function} return function
* @example
* var getReverseArgs = aid.reverseArgs(function() { return Array.prototype.slice.call(arguments); });
* console.log( getReverseArgs(99, 'aid.js') ); // ['aid.js', 99]
*/
aid.reverseArgs = function reverseArgs(func) {
if (!aid.isFunction(func)) throw new TypeError('[aid.reverseArgs] Type of func parameter must be Function.');
return function(/* args... */) {
var args = _slice.call(arguments);
return func.apply(null, args.concat().reverse());
};
};
/**
* partial application
*
* @static
* @method partial
* @param {Function} function
* @returns {Function} return function
* @example
* function sum() {
* var args = Array.prototype.slice.call(arguments);
* return args.reduce(function(acc, cur) {
* return acc + cur;
* }, 0);
* }
* console.log( aid.partial(sum)() ); // 0
* console.log( aid.partial(sum, 1)() ); // 1
* console.log( aid.partial(sum, 1)(2) ); // 3
* console.log( aid.partial(sum)(1, 2, 3) ); // 6
*/
aid.partial = function partial(func /*, args... */) {
if (!aid.isFunction(func)) throw new TypeError('[aid.partial] Type of func parameter must be Function.');
var args = aid.rest(_slice.call(arguments));
return function(/* args... */) {
return func.apply(func, args.concat(_slice.call(arguments)));
};
};
/**
* partial application for the right arguments
*
* @static
* @method partialRight
* @param {Function} function
* @returns {Function} return function
* @example
* function three(str, num, arr) { return str + ' ' + num + ' ' + arr; }
* console.log( aid.partialRight(three, 99, [1, 2, 3])('aid.js') ); // 'aid.js 99 1,2,3'
* console.log( aid.partial( aid.partialRight(three, [1, 2, 3]), 'aid.js' )(99) ); // 'aid.js 99 1,2,3'
*/
aid.partialRight = function partialRight(func /*, args... */) {
if (!aid.isFunction(func)) throw new TypeError('[aid.partialRight] Type of func parameter must be Function.');
var args = aid.rest(_slice.call(arguments));
return function(/* args... */) {
return func.apply(null, _slice.call(arguments).concat(args));
};
};
/**
* rest
* refer to _.rest function of underscore.js - https://github.com/jashkenas/underscore/blob/master/underscore.js
*
* @static
* @method rest
* @param {Array} array
* @param {Number} begin index to slice arguments.
* @returns {Array} return array
* @example
* var array = [1, 2, 3, 4, 5];
* console.log( aid.rest(array) ); // [2, 3, 4, 5]
* console.log( aid.rest(array, 2) ); // [3, 4, 5]
*/
aid.rest = function rest(array, beginIndex) {
if (!aid.isArray(array)) throw new TypeError('[aid.rest] Type of array parameter must be Array.');
var begin = !aid.existy(beginIndex) ? 1 : beginIndex;
return _slice.call(array, begin);
};
/**
* pipeline
*
* @static
* @method pipeline
* @param {Object} seed
* @returns {Object} return value
* @example
* function negative(n) { return n * -1; }
* function half(n) { return n / 2; }
* function negativeHalf(n) { return aid.pipeline(n, negative, half); }
* console.log( aid.pipeline(80, negative) ); // -80
* console.log( negativeHalf(80) ); // 80 * -1 / 2
*/
aid.pipeline = function pipeline(seed /* args */) {
var restArgs = aid.rest(_slice.call(arguments));
aid.each(
restArgs,
function(value) {
if (!aid.isFunction(value)) throw new TypeError('[aid.pipeline] Type of rest parameters must be Function.');
},
null
);
return restArgs.reduce(function(prev, current) {
return current(prev);
}, seed);
};
/**
* lazyChain
*
* @static
* @method lazyChain
* @param {Object} object
* @returns {Object} return {invoke, force}
* @example
* var lazy = aid.lazyChange([2, 1, 3]).invoke('concat', [7, 7, 8, 9, 0]).invoke('sort');
* console.log( lazy.force() ); // [0, 1, 2, 3, 7, 7, 8, 9]
*
* // with aid.pipeline
* function double(array) { return array.map(function(v) { return v * 2; }); }
* function lazyReverseAndNegative(array) { return aid.lazyChain(array).invoke('reverse').invoke('map', function(v) { return v * -1; }); }
* console.log( aid.pipeline([1, 2, 3], double, lazyReverseAndNegative).force() ); // [-6, -4, -2]
*/
aid.lazyChain = function lazyChain(obj) {
var calls = [];
return {
invoke: function(methodName /*, args */) {
var args = aid.rest(_slice.call(arguments));
calls.push(function(target) {
var method = target[methodName];
if (!aid.isDefined(method)) {
throw Error('[aid.lazyChain] ' + target.constructor.name + ' has not ' + methodName + ' method.');
}
return method.apply(target, args);
});
return this;
},
force: function() {
return calls.reduce(function(ret, thunk) {
return thunk(ret);
}, obj);
},
};
};
/**
* check two parameters are equal
*
* @static
* @method eq
* @param {Object} obj
* @param {Object} obj
* @returns {Boolean} return boolean
* @example
* console.log( aid.eq(99)(99) ); // true
*/
aid.eq = aid.curry2(function(lhs, rhs) {
return lhs === rhs;
});
/**
* identity combinator
*
* @static
* @method identity
* @param {Object} value
* @returns {Object} return value
* @example
* TODO:
*/
aid.identity = function identity(value) {
return value;
};
/**
* tab combinator
*
* @static
* @method tab
* @param {Function} function
* @returns {Function} return function
* @example
* TODO:
*/
aid.tab = function tab(func) {
if (!aid.isFunction(func)) throw new TypeError('[aid.tab] Type of func parameter must be Function.');
return function(value) {
func(value);
return value;
};
};
/**
* alt combinator
*
* @static
* @method alt
* @param {Function} func_a
* @param {Function} func_b
* @returns {Function} return function
* @example
* var alt = aid.alt(function(val) { return val; }, function(val) { return !val; });
* console.log( alt(false) ); // true
*/
aid.alt = function alt(func_a, func_b) {
if (!aid.isFunction(func_a) || !aid.isFunction(func_b)) {
throw new TypeError('[aid.alt] Type of func_a, func_b parameters must be Function.');
}
return function(value) {
var result_a = func_a(value);
// undefined, null, false
if (!aid.isDefined(result_a) || result_a === false) return func_b(value);
return result_a;
};
};
/**
* seq combinator
*
* @static
* @method seq
* @param {Function} functions
* @returns {Function} return function
* @example
* var seq = aid.seq(function(val) { console.log(val); }, function(val) { console.log(val / -1) });
* seq(99); // 99, -99
*/
aid.seq = function seq(/* functions */) {
var funcs = _slice.call(arguments);
funcs.forEach(function(func) {
if (!aid.isFunction(func)) throw new TypeError('[aid.seq] Requires function parameters.');
});
return function(value) {
funcs.forEach(function(func) {
func.call(null, value);
});
};
};
/**
* fork(join) combinator
*
* @static
* @method fork
* @param {Function} join
* @param {Function} func_a
* @param {Function} func_b
* @returns {Function} return function
* @example
* var fork = aid.fork(function(val_a, val_b) { return [val_a, val_b]; }, function(val) { return val; }, function(val) { return !val; });
* console.log( fork(true) ); // [true, false]
*/
aid.fork = function fork(join, func_a, func_b) {
if (!aid.isFunction(join) || !aid.isFunction(func_a) || !aid.isFunction(func_b)) {
throw new TypeError('[aid.fork] Type of join, func_a, func_b parameters must be Function.');
}
return function(value) {
return join.call(null, func_a.call(null, value), func_b.call(null, value));
};
};
/*
* Data Structure
*/
// Stack
var Stack = function() {
this._dataStore = [];
this._top = 0;
};
Stack.prototype.push = function stack_push(element) {
if (this._top < 0) this._top = 0;
this._dataStore[this._top++] = element;
};
Stack.prototype.pop = function stack_pop() {
return this._dataStore[--this._top];
};
Stack.prototype.peek = function stack_peek() {
return this._dataStore[this._top - 1];
};
Stack.prototype.length = function stack_length() {
return this._top > 0 ? this._top : 0;
};
Stack.prototype.clear = function stack_clear() {
this._dataStore = [];
this._top = 0;
};
/**
* createStack
*
* @static
* @method createStack
* @returns {Stack} return stack instance
* @example
* var stack = aid.createStack(); // use push, pop, peek, length, clear methods
*/
aid.createStack = function createStack() {
return new Stack();
};
// Queue
var Queue = function() {
this._dataStore = [];
};
Queue.prototype.enqueue = function queue_enqueue(element) {
this._dataStore.push(element);
};
Queue.prototype.dequeue = function queue_dequeue() {
return this._dataStore.shift();
};
Queue.prototype.front = function queue_front() {
return this._dataStore[0];
};
Queue.prototype.rear = function queue_rear() {
return this._dataStore[this._dataStore.length - 1];
};
Queue.prototype.length = function queue_length() {
return this._dataStore.length;
};
Queue.prototype.isEmpty = function queue_isEmpty() {
if (this._dataStore.length <= 0) return true;
return false;
};
/**
* createQueue
*
* @static
* @method createQueue
* @returns {Queue} return queue instance
* @example
* var queue = aid.createQueue(); // use enqueue, dequeue, front, rear, length, isEmpty methods
*/
aid.createQueue = function createQueue() {
return new Queue();
};
// LinkedList node
var LinkedListNode = function(data) {
this.data = data;
this.next = null;
};
// LinkedList
var LinkedList = function() {
this.head = new LinkedListNode('LinkedListHead_' + Date.now());
};
LinkedList.prototype.getHead = function linkedList_getHead() {
return this.head;
};
LinkedList.prototype.isEmpty = function linkedList_isEmpty() {
return !this.head.next;
};
LinkedList.prototype.find = function linkedList_find(data) {
var node = this.head;
while (node.data !== data) {
node = node.next;
if (!node) return node;
}
return node;
};
LinkedList.prototype.findPrevious = function linkedList_findPrevious(data) {
if (this.head.data === data) return null;
var node = this.head;
while (node.next && node.next.data !== data) {
node = node.next;
}
return node;
};
LinkedList.prototype.insert = function linkedList_insert(data, prevNodeData) {
var prevNode = this.find(prevNodeData);
if (!prevNode) return false;
var insertNode = new LinkedListNode(data);
insertNode.next = prevNode.next;
prevNode.next = insertNode;
return true;
};
LinkedList.prototype.remove = function linkedList_remove(data) {
var prevNode = this.findPrevious(data);
if (prevNode && prevNode.next) {
prevNode.next = prevNode.next.next;
return true;
}
return false;
};
LinkedList.prototype.append = function linkedList_append(data) {
var appendNode = new LinkedListNode(data);
var node = this.head;
while (node.next) {
node = node.next;
}
node.next = appendNode;
};
LinkedList.prototype.getAllNodes = function linkedList_getAllNodes() {
var nodes = [this.head],
node = this.head;
while (node.next) {
nodes.push(node.next);
node = node.next;
}
return nodes;
};
/**
* createLinkedList
*
* @static
* @method createLinkedList
* @returns {LinkedList} return linkedList instance
* @example
* var linkedList = aid.createLinkedList(); // use getHead, isEmpty, find, findPrevious, insert, remove, append, getAllNodes methods
*/
aid.createLinkedList = function createLinkedList() {
return new LinkedList();
};
// Set
var _Set = function() {
this.store = [];
};
_Set.prototype.size = function set_size() {
return this.store.length;
};
_Set.prototype.add = function set_add(value) {
if (!this.has(value)) {
this.store.push(value);
return true;
}
return false;
};
_Set.prototype.remove = function set_remove(value) {
var pos = this.store.indexOf(value);
if (pos >= 0) {
this.store.splice(pos, 1);
return true;
}
return false;
};
_Set.prototype.clear = function set_clear() {
this.store = [];
};
_Set.prototype.has = function set_has(value) {
return this.store.indexOf(value) >= 0;
};
_Set.prototype.values = function set_values() {
return this.store;
};
// A ∪ B
_Set.prototype.union = function set_union(otherSet) {
var unionSet = new _Set();
this.values().forEach(function(val) {
unionSet.add(val);
});
otherSet.values().forEach(function(val) {
unionSet.add(val);
});
return unionSet;
};
// A ∩ B
_Set.prototype.intersection = function set_intersection(otherSet) {
var intersectionSet = new _Set();
this.values().forEach(function(val) {
if (otherSet.has(val)) intersectionSet.add(val);
});
return intersectionSet;
};
// A - B
_Set.prototype.difference = function set_difference(otherSet) {
var differenceSet = new _Set();
this.values().forEach(function(val) {
if (!otherSet.has(val)) differenceSet.add(val);
});
return differenceSet;
};
// A ⊆ B
_Set.prototype.isSubset = function set_subset(superset) {
if (this.size() > superset.size()) return false;
var isSupersetContainsSubset = this.values().every(function(val) {
return superset.has(val);
});
return isSupersetContainsSubset;
};
/**
* createSet
*
* @static
* @method createSet
* @returns {Set} return set instance
* @example
* var set = aid.createSet(); // use size, add, remove, clear, has, values, union, intersection, difference, isSubset methods
*/
aid.createSet = function createSet() {
return new _Set();
};
// HashTable
var HashTable = function(hashFunc) {
this.table = [];
this._hashFunc = aid.isFunction(hashFunc) ? hashFunc : this._djb2Hash;
};
HashTable.prototype.put = function hashTable_put(key, value) {
var position = this._hashFunc.call(null, key);
if (this.table[position] === undefined) this.table[position] = aid.createLinkedList();
var linkedList = this.table[position];
linkedList.append({ key: key, value: value });
};
HashTable.prototype.get = function hashTable_get(key) {
var position = this._hashFunc.call(null, key);
var linkedList = this.table[position];
if (linkedList) {
var head = linkedList.getHead();
if (!head.next) return undefined;
var node = head;
while (node && node.data && node.data.key !== key) {
node = node.next;
}
if (node && node.data) return node.data.value;
}
return undefined;
};
HashTable.prototype.remove = function hashTable_remove(key) {
var position = this._hashFunc.call(null, key);
var linkedList = this.table[position];
if (linkedList) {
var head = linkedList.getHead();
if (!head.next) return false;
// find previous node
var node = head;
while (node.next && node.next.data && node.next.data.key !== key) {
node = node.next;
}
// remove node
if (node && node.next) {
node.next = node.next.next;
if (linkedList.isEmpty()) this.table[position] = undefined;
return true;
}
}
return false;
};
HashTable.prototype._djb2Hash = function hashTable_djb2Hash(key) {
var hash = 5381;
for (var i = 0, max = key.length; i < max; i++) {
hash = hash * 33 + key.charCodeAt(i);
}
return hash % 1013;
};
/**
* createHashTable
*
* @static
* @method createQueue
* @returns {Queue} return hashTable instance that use djb2 hash function.
* @example
* var HashTable = aid.createHashTable(); // use put, get, remove methods
*/
aid.createHashTable = function createHashTable(hashFunc) {
return new HashTable(hashFunc);
};
// Dictionary
function Dictionary() {
this.items = {};
}
Dictionary.prototype.has = function(key) {
return _hasOwnProperty.call(this.items, key);
};
Dictionary.prototype.get = function(key) {
return this.has(key) ? this.items[key] : undefined;
};
Dictionary.prototype.set = function(key, value) {
this.items[key] = value;
};
Dictionary.prototype.remove = function(key) {
if (this.has(key)) {
delete this.items[key];
return true;
}
return false;
};
Dictionary.prototype.clear = function() {
this.items = {};
};
Dictionary.prototype.keys = function() {
return Object.keys(this.items);
};
Dictionary.prototype.values = function() {
var values = [];
for (var key in this.items) {
if (this.has(key)) values.push(this.items[key]);
}
return values;
};
Dictionary.prototype.size = function() {
return Object.keys(this.items).length;
};
Dictionary.prototype.getItems = function() {
return this.items;
};
/**
* createDictionary
*
* @static
* @method createDictionary
* @returns {Dictionary} return Dictionary instance
* @example
* var dictionary = aid.createDictionary(); // use has, get, set, remove, clear, keys, values, size, getItems methods
*/
aid.createDictionary = function createDictionary() {
return new Dictionary();
};
// BinarySearchTree node
var BinarySearchTreeNode = function(data) {
this.data = data;
this.left = null;
this.right = null;
};
// BinarySearchTree node
var BinarySearchTree = function() {
this.root = null;
};
BinarySearchTree.prototype._insertNode = function(node, newNode) {
if (!aid.isDefined(node) || !aid.isDefined(newNode))
throw new TypeError('[BinarySearchTree.prototype._insertNode] node and newNode parameters must be defined.');
// duplicated value is not allowed in this BinarySearchTree
if (node.data === newNode.data) return;
if (newNode.data < node.data) {
aid.isDefined(node.left) ? this._insertNode(node.left, newNode) : (node.left = newNode);
} else {
aid.isDefined(node.right) ? this._insertNode(node.right, newNode) : (node.right = newNode);
}
};
BinarySearchTree.prototype._searchNode = function(node, data) {
if (!aid.isDefined(node)) return false;
if (!aid.isDefined(data))
throw new TypeError('[BinarySearchTree.prototype._searchNode] data parameters must be defined.');
if (data < node.data) {
return this._searchNode(node.left, data);
} else if (data > node.data) {
return this._searchNode(node.right, data);
} else {
return true;
}
};
BinarySearchTree.prototype._findMinNode = function(node) {
if (!aid.isDefined(node)) return null;
while (node && aid.isDefined(node.left)) {
node = node.left;
}
return node;
};
BinarySearchTree.prototype._removeNode = function(node, data) {
if (!aid.isDefined(data))
throw new TypeError('[BinarySearchTree.prototype._removeNode] data parameters must be defined.');
if (!aid.isDefined(node)) return null;
if (data < node.data) {
node.left = this._removeNode(node.left, data);
return node;
} else if (data > node.data) {
node.right = this._removeNode(node.right, data);
return node;
} else {
// case: remove leaf node
if (!aid.isDefined(node.left) && !aid.isDefined(node.right)) {
node = null;
return node;
}
// case: remove node has one child node
if (!aid.isDefined(node.left)) {
node = node.right;
return node;
} else if (!aid.isDefined(node.right)) {
node = node.left;
return node;
}
// case: remove node has two children nodes
var aux = this._findMinNode(node.right);
node.data = aux.data;
node.right = this._removeNode(node.right, aux.data);
return node;
}
};
BinarySearchTree.prototype._minNode = function(node) {
if (!aid.isDefined(node)) return null;
while (node && aid.isDefined(node.left)) node = node.left;
return node.data;
};
BinarySearchTree.prototype._maxNode = function(node) {
if (!aid.isDefined(node)) return null;
while (node && aid.isDefined(node.right)) node = node.right;
return node.data;
};
BinarySearchTree.prototype._inOrderTraverseNode = function(node, callback) {
if (!aid.isFunction(callback))
throw new TypeError(
'[BinarySearchTree.prototype._inOrderTraverseNode] Type of callback parameter must be Function.'
);
// visit node.left, node.data, node.right
if (aid.isDefined(node)) {
this._inOrderTraverseNode(node.left, callback);
callback(node.data);
this._inOrderTraverseNode(node.right, callback);
}
};
BinarySearchTree.prototype._preOrderTraverseNode = function(node, callback) {
if (!aid.isFunction(callback))
throw new TypeError(
'[BinarySearchTree.prototype._preOrderTraverseNode] Type of callback parameter must be Function.'
);
// visit node.data, node.left, node.right
if (aid.isDefined(node)) {
callback(node.data);
this._preOrderTraverseNode(node.left, callback);
this._preOrderTraverseNode(node.right, callback);
}
};
BinarySearchTree.prototype._postOrderTraverseNode = function(node, callback) {
if (!aid.isFunction(callback))
throw new TypeError(
'[BinarySearchTree.prototype._postOrderTraverseNode] Type of callback parameter must be Function.'
);
// visit node.left, node.right, node.data
if (aid.isDefined(node)) {
this._postOrderTraverseNode(node.left, callback);
this._postOrderTraverseNode(node.right, callback);
callback(node.data);
}
};
BinarySearchTree.prototype.getRoot = function binarySearchTree_getRoot() {
return this.root;
};
BinarySearchTree.prototype.insert = function binarySearchTree_insert(data) {
if (!aid.isDefined(data))
throw new TypeError('[BinarySearchTree.prototype.insert] data parameter must be defined.');
var newNode = new BinarySearchTreeNode(data);
if (aid.isDefined(this.root)) {
this._insertNode(this.root, newNode);
} else {
this.root = newNode;
}
};
BinarySearchTree.prototype.search = function binarySearchTree_search(data) {
if (!aid.isDefined(data))
throw new TypeError('[BinarySearchTree.prototype.search] data parameter must be defined.');
return this._searchNode(this.root, data);
};
BinarySearchTree.prototype.remove = function binarySearchTree_remove(data) {
if (!aid.isDefined(data))
throw new TypeError('[BinarySearchTree.prototype.remove] data parameter must be defined.');
this.root = this._removeNode(this.root, data);
};
BinarySearchTree.prototype.min = function binarySearchTree_min() {
return this._minNode(this.root);
};
BinarySearchTree.prototype.max = function binarySearchTree_max() {
return this._maxNode(this.root);
};
BinarySearchTree.prototype.inOrderTraverse = function binarySearchTree_inOrderTraverse(callback) {
if (!aid.isFunction(callback))
throw new TypeError('[BinarySearchTree.prototype.inOrderTraverse] Type of callback parameter must be Function.');
this._inOrderTraverseNode(this.root, callback);
};
BinarySearchTree.prototype.preOrderTraverse = function binarySearchTree_preOrderTraverse(callback) {
if (!aid.isFunction(callback))
throw new TypeError('[BinarySearchTree.prototype.preOrderTraverse] Type of callback parameter must be Function.');
this._preOrderTraverseNode(this.root, callback);
};
BinarySearchTree.prototype.postOrderTraverse = function binarySearchTree_postOrderTraverse(callback) {
if (!aid.isFunction(callback))
throw new TypeError(
'[BinarySearchTree.prototype.postOrderTraverse] Type of callback parameter must be Function.'
);
this._postOrderTraverseNode(this.root, callback);
};
/**
* createBinarySearchTree
*
* @static
* @method createBinarySearchTree
* @returns {BinarySearchTree} return BinarySearchTree instance
* @example
* var binarySearchTree = aid.createBinarySearchTree(); // use getRoot, insert, search, remove, min, max, inOrderTraverse, preOrderTraverse, postOrderTraverse methods
*/
aid.createBinarySearchTree = function createBinarySearchTree() {
return new BinarySearchTree();
};
// Graph
function Graph() {
this.vertices = [];
this.adjacencyList = aid.createDictionary();
}
Graph.prototype.addVertex = function(vertex) {
if (array.indexOf(this.vertices, vertex) >= 0)
throw new Error('[Graph.prototype.addVertex] this.vertices already has the same vertex.');
this.vertices.push(vertex);
this.adjacencyList.set(vertex, []);
};
Graph.prototype.addEdge = function(fromVertex, toVertex) {
if (array.indexOf(this.vertices, fromVertex) < 0)
throw new Error('[Graph.prototype.addEdge] this.vertices has not fromVertex.');
if (array.indexOf(this.vertices, toVertex) < 0)
throw new Error('[Graph.prototype.addEdge] this.vertices has not toVertex.');
this.adjacencyList.get(fromVertex).push(toVertex);
this.adjacencyList.get(toVertex).push(fromVertex);
};
Graph.prototype.bfs = function(fromVertex, callback) {
// breadth-first serach
if (array.indexOf(this.vertices, fromVertex) < 0)
throw new Error('[Graph.prototype.bfs] this.vertices has not fromVertex.');
var neighbors = this.adjacencyList.get(fromVertex);
if (!neighbors || neighbors.length <= 0)
throw new Error('[Graph.prototype.bfs] fromVertex is not connected to any vertices.');
if (aid.isDefined(callback) && !aid.isFunction(callback))
throw new TypeError('[Graph.prototype.bfs] Type of callback parameter must be undefined or null or Function.');
// colors has 3 types of vertex color
// 'white' : vertex is not visited
// 'grey' : vertex is visited but hasn't been explored
// 'black' : vertex is visited and has been explored
var colors = {}, // this.initializeVerticesColor(),
distances = {},
predecessors = {},
queue = new aid.createQueue();
this.vertices.forEach(function(v) {
colors[v] = 'white';
distances[v] = 0;
predecessors[v] = null;
});
queue.enqueue(fromVertex);
while (!queue.isEmpty()) {
var v = queue.dequeue(),
neighborVertices = this.adjacencyList.get(v);
colors[v] = 'grey';
neighborVertices.forEach(function(nv) {
if (colors[nv] === 'white') {
colors[nv] = 'grey';
distances[nv] = distances[v] + 1;
predecessors[nv] = v;
queue.enqueue(nv);
}
});
colors[v] = 'black';
if (callback) callback.call(null, v);
}
return {
distances: distances,
predecessors: predecessors,
};
};
Graph.prototype.getBfsPaths = function(fromVertex) {
var neighbors = this.adjacencyList.get(fromVertex);
if (!neighbors || neighbors.length <= 0)
throw new Error('[Graph.prototype.getBfsPaths] fromVertex is not connected to any vertices.');
var bfsPaths = [];
var datasFromBfs = this.bfs(fromVertex);
var toVertices = this.vertices.filter(function(v) {
return v !== fromVertex;
});
toVertices.forEach(function(toVertex) {
var searchPath = aid.createStack();
for (var v = toVertex; v !== fromVertex; v = datasFromBfs.predecessors[v]) {
searchPath.push(v);
}
searchPath.push(fromVertex);
var edgesNum = searchPath.length() - 1;
var path = searchPath.pop();
while (searchPath.length()) {
path += ' - ' + searchPath.pop();
}
bfsPaths.push({
path: path,
edgesNum: edgesNum,
});
});
return bfsPaths;
};
Graph.prototype.dfs = function(fromVertex, callback) {
// depth-first serach
if (array.indexOf(this.vertices, fromVertex) < 0)
throw new Error('[Graph.prototype.dfs] this.vertices has not fromVertex.');
if (aid.isDefined(callback) && !aid.isFunction(callback))
throw new TypeError('[Graph.prototype.dfs] Type of callback parameter must be undefined or null or Function.');
var datas = {
time: 0,
colors: {},
discovered: {},
finished: {},
predecessors: {},
};
this.vertices.forEach(function(v) {
// colors has 3 types of vertex color
// 'white' : vertex is not visited
// 'grey' : vertex is visited but hasn't been explored
// 'black' : vertex is visited and has been explored
datas.colors[v] = 'white';
datas.discovered[v] = 0;
datas.finished[v] = 0;
datas.predecessors[v] = null;
});
// update datas recursively
this._dfsVisit(fromVertex, datas, callback);
return datas;
};
Graph.prototype._dfsVisit = function(vertex, datas, callback) {
var _ = this,
colors = datas.colors,
discovered = datas.discovered,
finished = datas.finished,
predecessors = datas.predecessors;
colors[vertex] = 'grey';
discovered[vertex] = ++datas.time;
if (callback) callback.call(null, vertex);
var neighborVertices = this.adjacencyList.get(vertex);
neighborVertices.forEach(function(nv) {
if (colors[nv] === 'white') {
predecessors[nv] = vertex;
_._dfsVisit(nv, datas, callback);
}
});
colors[vertex] = 'black';
finished[vertex] = ++datas.time;
};
/**
* createGraph
*
* @static
* @method createGraph
* @returns {Graph} return Graph instance
* @example
* var vertices = ['A', 'B', 'C', 'D'];
* var graph = aid.createGraph(); // use addVertex, addEdge, bfs, getBfsPaths, dfs methods
* vertices.forEach(function(v) { graph.addVertex(v); });
* graph.addEdge('A', 'B');
* graph.addEdge('A', 'C');
* graph.addEdge('A', 'D');
* graph.addEdge('B', 'D');
* graph.addEdge('C', 'D');
* console.log( graph.dfs('A') );
*/
aid.createGraph = function createGraph() {
return new Graph();
};
/**
* plus(+) operator
*
* @static
* @method +
* @param {Object} object_a
* @param {Object} object_b
* @returns {Object} return object
* @example
* var plus = aid.operator['+'];
* console.log( plus(1, 2) ); // 3
*/
operator['+'] = function plus(object_a, object_b) {
return object_a + object_b;
};
/**
* identity(===) operator
*
* @static
* @method ===
* @param {Object} object_a
* @param {Object} object_b
* @returns {Boolean} return boolean
* @example
* var identity = aid.operator['==='];
* console.log( identity(1, 1) ); // true
*/
operator['==='] = function identity(object_a, object_b) {
return object_a === object_b;
};
/**
* logical not(!) operator
*
* @static
* @method !
* @param {Object} obj
* @returns {Boolean} return boolean
* @example
* var isNotNaN = aid.compose(aid.operator['!'], isNaN);
* console.log( isNotNaN(0) ); // true
*/
operator['!'] = function(obj) {
return !obj;
};
/**
* is window platform
*
* @static
* @method isWindow
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.platform.isWindow(window.navigator.userAgent) );
*/
platform.isWindow = function isWindow(userAgent) {
return /Windows/i.test(userAgent);
};
/**
* is Macintosh platform
*
* @static
* @method isMac
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.platform.isMac(window.navigator.userAgent) );
*/
platform.isMac = function isMac(userAgent) {
return /Macintosh/i.test(userAgent);
};
/**
* is iOS platform
* http://stackoverflow.com/questions/9038625/detect-if-device-is-ios/9039885#9039885
*
* @static
* @method isIOS
* @param {string} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.platform.isIOS(window.navigator.userAgent) );
*/
platform.isIOS = function isIOS(userAgent) {
return /iPad|iPhone|iPod/.test(userAgent) && !global.MSStream;
};
/**
* is iPhone
*
* @static
* @method isIPhone
* @param {string} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.platform.isIPhone(window.navigator.userAgent) );
*/
platform.isIPhone = function isIPhone(userAgent) {
if (!platform.isIOS(userAgent)) return false;
return /iPhone/.test(userAgent);
};
/**
* is iPad
*
* @static
* @method isIPad
* @param {string} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.platform.isIPad(window.navigator.userAgent) );
*/
platform.isIPad = function isIPad(userAgent) {
if (!platform.isIOS(userAgent)) return false;
return /iPad/.test(userAgent);
};
/**
* is Android platform
*
* @static
* @method isAndroid
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.platform.isAndroid(window.navigator.userAgent) );
*/
platform.isAndroid = function isAndroid(userAgent) {
return /Android/.test(userAgent);
};
/**
* is IE browser
*
* @static
* @method isIE
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isIE(window.navigator.userAgent) );
*/
browser.isIE = function isIE(userAgent) {
return /msie/.test(userAgent) || /trident/.test(userAgent);
};
/**
* is Edge browser
*
* @static
* @method isEdge
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isEdge(window.navigator.userAgent) );
*/
browser.isEdge = function isEdge(userAgent) {
return /Gecko/.test(userAgent) && /Edge/.test(userAgent);
};
/**
* is Firefox browser
*
* @static
* @method isFF
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isFF(window.navigator.userAgent) );
*/
browser.isFF = function isFF(userAgent) {
return /Firefox/.test(userAgent);
};
/**
* is Opera browser
*
* @static
* @method isOpera
* @param {string} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isOpera(window.navigator.userAgent) );
*/
browser.isOpera = function isOpera(userAgent) {
return /Opera/.test(userAgent) || /OPR\//.test(userAgent);
};
/**
* is Chrome browser (desktop)
*
* @static
* @method isChrome
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isChrome(window.navigator.userAgent) );
*/
browser.isChrome = function isChrome(userAgent) {
if (platform.isAndroid(userAgent) || platform.isIOS(userAgent)) return false;
return !browser.isEdge(userAgent) && !browser.isOpera(userAgent) && /Chrome/.test(userAgent);
};
/**
* is mobile Chrome browser (phone)
*
* @static
* @method isChromePhone
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isChromePhone(window.navigator.userAgent) );
*/
browser.isChromePhone = function isChromePhone(userAgent) {
if (platform.isIPhone(userAgent))
return (
!/Chrome/.test(userAgent) && /Safari/.test(userAgent) && /CriOS/.test(userAgent) && /Mobile/.test(userAgent)
);
// https://developer.chrome.com/multidevice/user-agent
if (platform.isAndroid(userAgent)) return /Chrome\/[.0-9]* Mobile/.test(userAgent) && /Safari/i.test(userAgent);
return false;
};
/**
* is mobile Chrome browser (tablet)
*
* @static
* @method isChromeTablet
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isChromeTablet(window.navigator.userAgent) );
*/
browser.isChromeTablet = function isChromeTablet(userAgent) {
if (platform.isIPad(userAgent))
return (
!/Chrome/.test(userAgent) && /Safari/.test(userAgent) && /CriOS/.test(userAgent) && /Mobile/.test(userAgent)
);
// https://developer.chrome.com/multidevice/user-agent
if (platform.isAndroid(userAgent)) {
return /Chrome\/[.0-9]* (?!Mobile)/.test(userAgent) && /Safari/i.test(userAgent);
}
return false;
};
/**
* is Safari browser (desktop)
*
* @static
* @method isSafari
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isSafari(window.navigator.userAgent) );
*/
browser.isSafari = function isSafari(userAgent) {
if (platform.isIOS(userAgent)) return false;
return (
!/Chrome/.test(userAgent) && /Safari/.test(userAgent) && !/CriOS/.test(userAgent) && !/Mobile/.test(userAgent)
);
};
/**
* is mobile Safari browser (phone)
*
* @static
* @method isSafariPhone
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
*/
browser.isSafariPhone = function isSafariPhone(userAgent) {
if (platform.isIPhone(userAgent))
return (
!/Chrome/.test(userAgent) && /Safari/.test(userAgent) && !/CriOS/.test(userAgent) && /Mobile/.test(userAgent)
);
return false;
};
/**
* is mobile Safari browser (tablet)
*
* @static
* @method isSafariTablet
* @param {String} userAgent
* @returns {Boolean} return boolean
* @example
*/
browser.isSafariTablet = function isSafariTablet(userAgent) {
if (platform.isIPad(userAgent))
return (
!/Chrome/.test(userAgent) && /Safari/.test(userAgent) && !/CriOS/.test(userAgent) && /Mobile/.test(userAgent)
);
return false;
};
/**
* get IE version
*
* @static
* @method getIEVersion
* @param {string} userAgent
* @returns {Number} return version float number
* @example
* console.log( aid.browser.getIEVersion(window.navigator.userAgent) );
*/
browser.getIEVersion = function getIEVersion(userAgent) {
if (browser.isIE(userAgent)) {
if (/msie (\d+\.\d+);/i.test(userAgent)) return parseFloat(RegExp.$1, 10);
if (/trident.*rv:(\d+)\.(\d+)/i.test(userAgent)) return parseFloat(RegExp.$1, 10);
}
return -1;
};
/**
* get Firefox version
*
* @static
* @method getFFVersion
* @param {string} userAgent
* @returns {Number} return version float number
* @example
* console.log( aid.browser.getFFVersion(window.navigator.userAgent) );
*/
browser.getFFVersion = function getFFVersion(userAgent) {
if (browser.isFF(userAgent)) {
if (/Firefox[\/\s](\d+\.\d+)/i.test(userAgent)) return parseFloat(RegExp.$1, 10);
}
return -1;
};
/**
* get Opera version
*
* @static
* @method getOperaVersion
* @param {String} userAgent
* @returns {Number} return version float number
* @example
* console.log( aid.browser.getOperaVersion(window.navigator.userAgent) );
*/
browser.getOperaVersion = function getOperaVersion(userAgent) {
if (browser.isOpera(userAgent)) {
if (/Opera/i.test(userAgent)) {
if (/Version[\/\s](\d+\.\d+)/i.test(userAgent)) return parseFloat(RegExp.$1, 10);
if (/Opera[\/\s](\d+\.\d+)/i.test(userAgent)) return parseFloat(RegExp.$1, 10);
}
if (/OPR[\/\s](\d+\.\d+)/i.test(userAgent)) return parseFloat(RegExp.$1, 10);
}
return -1;
};
/**
* get Chrome version
*
* @static
* @method getChromeVersion
* @param {String} userAgent
* @returns {Number} return version float number
* @example
* console.log( aid.browser.getChromeVersion(window.navigator.userAgent) );
*/
browser.getChromeVersion = function getChromeVersion(userAgent) {
if (browser.isChrome(userAgent)) {
if (/Chrome[\/\s](\d+\.\d+)/i.test(userAgent)) return parseFloat(RegExp.$1, 10);
}
return -1;
};
/**
* get Safari version
*
* @static
* @method getSafariVersion
* @param {String} userAgent
* @returns {Number} return version float number
* @example
* console.log( aid.browser.getSafariVersion(window.navigator.userAgent) );
*/
browser.getSafariVersion = function getSafariVersion(userAgent) {
if (browser.isSafari(userAgent)) {
if (/Version[\/\s](\d+\.\d+)/i.test(userAgent)) return parseFloat(RegExp.$1, 10);
}
return -1;
};
/**
* get IE browser Compatibility info object
*
* @static
* @method getIECompatibility
* @param {String} userAgent (optional)
* @returns {Object} return { isIE: Boolean, isCompatibilityMode: Boolean, compatibilityVersion: Number }
* @example
* console.log( aid.browser.getIECompatibility() );
* console.log( aid.browser.getIECompatibility('mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) hrome/39.0.2171.65 safari/537.36') );
*/
browser.getIECompatibility = function getIECompatibility(optionUserAgent) {
var ua = optionUserAgent ? optionUserAgent : global.navigator.userAgent.toLowerCase(),
regex_msie = /msie/i,
regex_msie7 = /msie 7/i,
regex_msie8 = /msie 8/i,
regex_msie9 = /msie 9/i,
regex_msie10 = /msie 10/i,
regex_msie11 = /rv:11[\.0-9]{0,}/,
regex_trident = /trident/i,
regex_trident7 = /trident\/7/;
if (!regex_msie.test(ua) && !regex_msie11.test(ua)) {
// not IE
return {
isIE: false,
isCompatibilityMode: false,
compatibilityVersion: -1,
};
}
if (regex_msie11.test(ua) && regex_trident7.test(ua)) {
// IE11 standard
return {
isIE: true,
isCompatibilityMode: false,
compatibilityVersion: -1,
};
}
if (regex_msie.test(ua) && regex_trident7.test(ua)) {
// IE11 compatibility mode
if (regex_msie7.test(ua))
return {
isIE: true,
isCompatibilityMode: true,
compatibilityVersion: 7,
};
if (regex_msie8.test(ua))
return {
isIE: true,
isCompatibilityMode: true,
compatibilityVersion: 8,
};
if (regex_msie9.test(ua))
return {
isIE: true,
isCompatibilityMode: true,
compatibilityVersion: 9,
};
if (regex_msie10.test(ua))
return {
isIE: true,
isCompatibilityMode: true,
compatibilityVersion: 10,
};
}
if (regex_msie7.test(ua) && regex_trident.test(ua)) {
// IE8 ~ 10 compatibility mode
return {
isIE: true,
isCompatibilityMode: true,
compatibilityVersion: 7,
};
}
// IE8 ~ 10 standard
return {
isIE: true,
isCompatibilityMode: false,
compatibilityVersion: -1,
};
};
/**
* is browser support draggable attribute of element
*
* @static
* @method isSupportDraggable
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isSupportDraggable() );
*/
browser.isSupportDraggable = function isSupportDraggable() {
var div = document.createElement('div');
return 'draggable' in div;
};
/**
* is browser support element methods related drag(ondragstart, ondrop, etc...)
*
* @static
* @method isSupportDragAndDrop
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isSupportDragAndDrop() );
*/
browser.isSupportDragAndDrop = function isSupportDragAndDrop() {
var div = document.createElement('div');
return 'ondragstart' in div && 'ondrop' in div;
};
/**
* is browser support html5 File API
*
* @static
* @method isSupportFileApi
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isSupportFileApi() );
*/
browser.isSupportFileApi = function isSupportFileApi() {
return !!(global.File && global.FileReader && global.FileList && global.Blob);
};
/**
* is chrome extension
*
* @static
* @method isChromeExtension
* @param {string} userAgent
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.isChromeExtension(window.navigator.userAgent) );
*/
browser.isChromeExtension = function isChromeExtension(userAgent) {
if (browser.isChrome(userAgent) && global.chrome) {
if (!global.chrome.cookies) {
if (global.chrome.experimental) global.chrome.cookies = global.chrome.experimental.cookies;
}
if (global.chrome.cookies) return true;
return false;
}
return false;
};
/**
* can use DOM
*
* @static
* @method canUseDOM
* @returns {Boolean} return boolean
* @example
* console.log( aid.browser.canUseDOM() );
*/
browser.canUseDOM = function canUseDOM() {
return aid.truthy(typeof window !== 'undefined' && window.document && window.document.createElement);
};
/**
* get cookie
*
* @static
* @method getCookie
* @param {String} key
* @returns {String} return string
* @example
* console.log( aid.browser.getCookie('sampleCookieKey') );
*/
browser.getCookie = function getCookie(key) {
var cookieArr = document.cookie.split('; '),
splitArr = [],
keyStr = '',
valueStr = '';
for (var i = 0, max = cookieArr.length; i < max; ++i) {
splitArr = cookieArr[i].split('=');
keyStr = splitArr[0];
valueStr = global.decodeURIComponent(splitArr[1]);
if (keyStr === key) return valueStr;
}
return null;
};
/**
* set cookie
*
* @static
* @method setCookie
* @param {String} key
* @param {String} value
* @param {Number} expireSecond
* @param {String} path
* @param {String} domain
* @example
* aid.browser.setCookie('sampleCookieKey', 'sampleValue', 3600, '/samplePath', 'sample.com');
*/
browser.setCookie = function setCookie(key, value, expireSecond, path, domain) {
var expires = '',
pathStr = '; path=' + (path ? path : '/'),
domainStr = domain ? '; domain=' + domain : '';
if (expireSecond) {
var date = new Date();
date.setTime(date.getTime() + expireSecond * 1000);
expires = '; expires=' + date.toGMTString();
}
document.cookie = key + '=' + global.encodeURIComponent(value) + pathStr + domainStr + expires;
};
/**
* trim string
*
* @static
* @method trim
* @param {String} str
* @returns {String} return trimmed string
* @example
* console.log( aid.string.trim(' foo ') ); // 'foo'
*/
string.trim = function trim(str) {
if (!aid.isString(str)) throw new TypeError('[aid.string.trim] Type of str parameter must be String.');
return str.replace(/^\s+/, '').replace(/\s+$/, '');
};
/**
* check string has unique characters
*
* @static
* @method hasUniqueChars
* @param {String} str
* @returns {Boolean} return boolean
* @example
* console.log( aid.string.hasUniqueChars('abcdea') ); // false
*/
string.hasUniqueChars = function hasUniqueChars(str) {
if (!aid.isString(str)) throw new TypeError('[aid.string.hasUniqueChars] Type of str parameter must be String.');
if (!str.length) return true;
var obj = {},
char = '';
for (var i = 0, max = str.length; i < max; i++) {
char = str.charAt(i);
if (obj[char] === true) return false;
obj[char] = true;
}
return true;
};
/**
* get extension(png, jpg ...) string
*
* @static
* @method getFileExtension
* @param {String} fileName
* @returns {String} return string
* @example
* console.log( aid.string.getFileExtension('aid.png') ); // png
*/
string.getFileExtension = function getFileExtension(fileName) {
if (!aid.isString(fileName))
throw new TypeError('[aid.string.getFileExtension] Type of fileName parameter must be String.');
if (fileName.length <= 0) return '';
var lastDotIndex = fileName.lastIndexOf('.'),
extension = fileName.substr(lastDotIndex + 1);
return extension;
};
/**
* check email string
*
* @static
* @method isEmail
* @param {String} email
* @returns {Boolean} return boolean
* @example
* console.log( aid.string.isEmail('dragmove@gmail.com') ); // true
*/
string.isEmail = function isEmail(email) {
if (!aid.isString(email)) throw new TypeError('[aid.string.isEmail] Type of email parameter must be String.');
// html5 form email check regex - https://www.w3.org/TR/html5/forms.html#e-mail-state-(type=email)
var emailRegex = new RegExp(
"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
);
return emailRegex.exec(email) ? true : false;
};
/**
* check iframe string
*
* @static
* @method isIFrame
* @param {String} iframeHtml
* @returns {Boolean} return boolean
* @example
* console.log( aid.string.isIFrame('<iframe src=""></iframe>') ); // true
*/
string.isIFrame = function isIFrame(iframeHtml) {
if (!aid.isString(iframeHtml))
throw new TypeError('[aid.string.isIFrame] Type of iframeHtml parameter must be String.');
return /^(<iframe).*(<\/iframe>|\/>)$/.test(iframeHtml);
};
/**
* get param value from uri
*
* @static
* @method getUriParam
* @param {String} uri
* @param {String} parameterName
* @returns {String} return string
* @example
* console.log( aid.string.getUriParam('http://www.google.com?name=foo&age=99&address=seoul', 'name') ); // 'foo'
*/
string.getUriParam = function getUriParam(uri, parameterName) {
if (!aid.isString(uri) || !aid.isString(parameterName)) {
throw new TypeError('[aid.string.getUriParam] Type of uri, parameterName parameters must be String.');
}
if (uri.length < 1) return '';
uri = uri.split('#')[0];
var tmpArr = uri.split('?');
if (tmpArr.length < 2) return '';
var paramStr = tmpArr[1],
params = paramStr.split('&');
for (var param = '', eqIndex = -1, keyStr = '', valueStr = '', i = 0, max = params.length; i < max; ++i) {
param = params[i];
eqIndex = param.indexOf('=');
if (eqIndex >= 0) {
keyStr = param.substr(0, eqIndex);
valueStr = param.substr(eqIndex + 1);
if (keyStr === parameterName) return valueStr;
}
}
return '';
};
/**
* get param values from uri
*
* @static
* @method getUriParams
* @param {String} uri
* @returns {Object} return object
* @example
* console.log( aid.string.getUriParams('http://www.google.com?name=foo&age=99&address=seoul') ); // {name: 'foo', age: '99', address: 'seoul'}
*/
string.getUriParams = function getUriParams(uri) {
if (!aid.isString(uri)) throw new TypeError('[aid.string.getUriParam] Type of uri parameter must be String.');
if (uri.length < 1) return null;
uri = uri.split('#')[0];
var tmpArr = uri.split('?');
if (tmpArr.length < 2) return null;
var paramStr = tmpArr[1],
params = paramStr.split('&');
if (params.length <= 0) return null;
var obj = {};
for (var param = '', eqIndex = -1, keyStr = '', valueStr = '', i = 0, max = params.length; i < max; ++i) {
param = params[i];
eqIndex = param.indexOf('=');
if (eqIndex >= 0) {
keyStr = param.substr(0, eqIndex);
valueStr = param.substr(eqIndex + 1);
if (keyStr) obj[keyStr] = valueStr;
}
}
if (aid.object.isEmpty(obj)) return null;
return obj;
};
/**
* get uri combined parameters
*
* @static
* @method getUriCombinedParams
* @param {String} uri
* @param {Object} parameters
* @returns {String} return string
* @example
* console.log( aid.string.getUriCombinedParams('http://www.google.com', {name: "foo", age: "99", address: "seoul"}) ) // http://www.google.com?name=foo&age=99&address=seoul
*/
string.getUriCombinedParams = function getUriCombinedParams(uri, parameters) {
if (!aid.isString(uri))
throw new TypeError('[aid.string.getUriCombinedParams] Type of uri parameter must be String.');
if (!aid.isObject(parameters))
throw new TypeError('[aid.string.getUriCombinedParams] Type of parameters parameter must be Object.');
if (!uri) return '';
if (!parameters) return uri;
var str = '';
for (var key in parameters) {
if (_hasOwnProperty.call(parameters, key)) str += '&' + key + '=' + String(parameters[key]);
}
if (str === '') return uri;
var tmpArr = uri.split('#'),
hashStr = aid.isDefined(tmpArr[1]) && tmpArr[1].length ? '#' + tmpArr[1] : '';
uri = tmpArr[0];
uri = (uri.indexOf('?') >= 0 ? uri + str : uri + '?' + str.substr(1)) + hashStr;
return uri;
};
/**
* check youtube video id
*
* @static
* @method isValidYoutubeVideoId
* @param {String} youtubeId
* @returns {Boolean} return boolean
* @example
* console.log( aid.string.isValidYoutubeVideoId('mYIfiQlfaas') ); // true
*/
string.isValidYoutubeVideoId = function isValidYoutubeVideoId(youtubeId) {
if (!aid.isString(youtubeId))
throw new TypeError('[aid.string.isValidYoutubeVideoId] Type of youtubeId parameter must be String.');
var regex = /^(\w|-|_){11}$/;
return regex.exec(youtubeId) ? true : false;
};
/**
* check youtube uri is valid
*
* @static
* @method getObjCheckYoutubeURI
* @param {String} uri
* @returns {Object} return { type:'youtube', uri: String, youtubeId: String, isValidURI: Boolean }
* @example
* console.log( aid.string.getObjCheckYoutubeURI('https://www.youtube.com/watch?v=mYIfiQlfaas') ); // // {type: "youtube", uri: "https://www.youtube.com/watch?v=mYIfiQlfaas", youtubeId: "mYIfiQlfaas", isValidURI: true}
* console.log( aid.string.getObjCheckYoutubeURI('https://youtu.be/mYIfiQlfaas') ); // {type: "youtube", uri: "https://youtu.be/mYIfiQlfaas", youtubeId: "mYIfiQlfaas", isValidURI: true}
* console.log( aid.string.getObjCheckYoutubeURI('https://www.youtube.com/embed/mYIfiQlfaas') ); // {type: "youtube", uri: "https://www.youtube.com/embed/mYIfiQlfaas", youtubeId: "mYIfiQlfaas", isValidURI: true}
*/
string.getObjCheckYoutubeURI = function getObjCheckYoutubeURI(uri) {
if (!aid.isString(uri))
throw new TypeError('[aid.string.getObjCheckYoutubeURI] Type of uri parameter must be String.');
var YOUTUBE_REGEXES = {
watch: /^(?:(?:https?:)?\/\/)?(?:www\.)?youtube\.com\/watch/,
embed: /^(?:(?:https?:)?\/\/)?(?:www\.)?youtube\.com\/(?:embed\/((?:\w|-|_){11}))/,
v: /^(?:(?:https?:)?\/\/)?(?:www\.)?youtube\.com\/(?:v\/((?:\w|-|_){11}))/,
'youtu.be': /^(?:(?:https?:)?\/\/)?(?:www\.)?youtu\.be\/((?:\w|-|_){11})/,
};
var uriType = null;
for (var key in YOUTUBE_REGEXES) {
var val = YOUTUBE_REGEXES[key];
if (!val) continue;
var flag = YOUTUBE_REGEXES[key].exec(uri) ? true : false;
if (flag) {
uriType = key;
break;
}
}
var youtubeId = '',
tmpArr = [];
switch (uriType) {
case 'watch':
youtubeId = string.getUriParam(uri, 'v') || '';
break;
case 'embed':
tmpArr = uri.split('?')[0].split('/');
youtubeId = tmpArr[tmpArr.length - 1] || '';
break;
case 'v':
tmpArr = uri.split('?')[0].split('/');
youtubeId = tmpArr[tmpArr.length - 1] || '';
break;
case 'youtu.be':
tmpArr = uri.split('?')[0].split('/');
youtubeId = tmpArr[tmpArr.length - 1] || '';
break;
default:
}
var isValidURI = youtubeId !== '' && string.isValidYoutubeVideoId(youtubeId);
return {
type: 'youtube',
uri: uri,
youtubeId: youtubeId,
isValidURI: isValidURI,
};
};
/**
* check twitch uri is valid
*
* @static
* @method getObjCheckTwitchURI
* @param {String} uri
* @returns {Object} return { type:'twitch', uri: String, channelName: String, videoId: Boolean, isChatting: Boolean, isValidURI: Boolean }
* @example
* console.log(aid.string.getObjCheckTwitchURI('https://www.twitch.tv/surrenderhs/chat?popout=')); // {channelName: "surrenderhs", isChatting: true, isValidURI: true, type: "twitch", uri: "https://www.twitch.tv/surrenderhs/chat?popout=", videoId: ""}
* console.log(aid.string.getObjCheckTwitchURI('https://www.twitch.tv/surrenderhs')); // {channelName: "surrenderhs", isChatting: false, isValidURI: true, type: "twitch", uri: "https://www.twitch.tv/surrenderhs", videoId: ""}
* console.log(aid.string.getObjCheckTwitchURI('www.twitch.tv/surrenderhs')); // {channelName: "surrenderhs", isChatting: false, isValidURI: true, type: "twitch", uri: "www.twitch.tv/surrenderhs", videoId: ""}
* console.log(aid.string.getObjCheckTwitchURI('twitch.tv/surrenderhs')); // {channelName: "surrenderhs", isChatting: false, isValidURI: true, type: "twitch", uri: "twitch.tv/surrenderhs", videoId: ""}
* console.log(aid.string.getObjCheckTwitchURI('https://player.twitch.tv/?channel=surrenderhs')); // {channelName: "surrenderhs", isChatting: false, isValidURI: true, type: "twitch", uri: "https://player.twitch.tv/?channel=surrenderhs", videoId: ""}
* console.log(aid.string.getObjCheckTwitchURI('player.twitch.tv/?channel=surrenderhs')); // {channelName: "surrenderhs", isChatting: false, isValidURI: true, type: "twitch", uri: "player.twitch.tv/?channel=surrenderhs", videoId: ""}
* console.log(aid.string.getObjCheckTwitchURI('https://www.twitch.tv/surrenderhs/v/56097351')); // {channelName: "surrenderhs", isChatting: false, isValidURI: true, type: "twitch", uri: "https://www.twitch.tv/surrenderhs/v/56097351", videoId: "56097351"}
* console.log(aid.string.getObjCheckTwitchURI('www.twitch.tv/surrenderhs/v/56097351')); // {channelName: "surrenderhs", isChatting: false, isValidURI: true, type: "twitch", uri: "www.twitch.tv/surrenderhs/v/56097351", videoId: "56097351"}
* console.log(aid.string.getObjCheckTwitchURI('https://player.twitch.tv/?video=v56097351')); // {channelName: "surrenderhs", isChatting: false, isValidURI: true, type: "twitch", uri: "https://player.twitch.tv/?video=v56097351", videoId: "56097351"}
* console.log(aid.string.getObjCheckTwitchURI('player.twitch.tv/?video=v56097351')); // {channelName: "surrenderhs", isChatting: false, isValidURI: true, type: "twitch", uri: "player.twitch.tv/?video=v56097351", videoId: "56097351"}
*/
string.getObjCheckTwitchURI = function getObjCheckTwitchURI(uri) {
/*
+ Live Video
https://www.twitch.tv/surrenderhs // channel link
https://player.twitch.tv/?channel=surrenderhs // iframe, Flash player
https://www.twitch.tv/surrenderhs/chat?popout= // iframe chatting
+ Past Video
https://www.twitch.tv/surrenderhs/v/56097351 // channel link
https://player.twitch.tv/?video=v56097351 // iframe, Flash player
+ URI TEST
https://www.twitch.tv/surrenderhs
www.twitch.tv/surrenderhs
twitch.tv/surrenderhs
https://player.twitch.tv/?channel=surrenderhs
player.twitch.tv/?channel=surrenderhs
https://www.twitch.tv/surrenderhs/v/56097351
www.twitch.tv/surrenderhs/v/56097351
https://player.twitch.tv/?video=v56097351
player.twitch.tv/?video=v56097351
*/
if (!aid.isString(uri))
throw new TypeError('[aid.string.getObjCheckTwitchURI] Type of uri parameter must be String.');
var TWITCH_REGEXES = {
liveChannel: /^(?:(?:https?:)?\/\/)?(?:www\.)?twitch\.tv\/([a-zA-Z0-9][\w]{2,24})$/,
liveVideo: /^(?:(?:https?:)?\/\/)?player\.twitch\.tv\/\?channel\=([a-zA-Z0-9][\w]{2,24})$/,
chatting: /^(?:(?:https?:)?\/\/)?(?:www\.)?twitch\.tv\/([a-zA-Z0-9][\w]{2,24})\/chat/,
pastChannel: /^(?:(?:https?:)?\/\/)?(?:www\.)?twitch\.tv\/([a-zA-Z0-9][\w]{2,24})\/v\/(\d+)/,
pastVideo: /^(?:(?:https?:)?\/\/)?player\.twitch\.tv\/\?video\=v(\d+)/,
};
var channelName = '',
videoId = '',
isChatting = false,
isValidURI = false;
var uriType = null;
for (var key in TWITCH_REGEXES) {
var val = TWITCH_REGEXES.hasOwnProperty(key);
if (!val) continue;
var flag = TWITCH_REGEXES[key].exec(uri) ? true : false;
if (flag) {
uriType = key;
isValidURI = true;
break;
}
}
var tmpArr = [];
switch (uriType) {
// https://www.twitch.tv/surrenderhs
case 'liveChannel':
tmpArr = TWITCH_REGEXES[uriType].exec(uri);
channelName = tmpArr[1];
break;
// https://player.twitch.tv/?channel=surrenderhs
case 'liveVideo':
tmpArr = TWITCH_REGEXES[uriType].exec(uri);
channelName = tmpArr[1];
break;
// https://www.twitch.tv/surrenderhs/chat?popout=
case 'chatting':
tmpArr = TWITCH_REGEXES[uriType].exec(uri);
channelName = tmpArr[1];
isChatting = true;
break;
// https://www.twitch.tv/surrenderhs/v/56097351
case 'pastChannel':
tmpArr = TWITCH_REGEXES[uriType].exec(uri);
channelName = tmpArr[1];
videoId = tmpArr[2];
break;
// https://player.twitch.tv/?video=v56097351
case 'pastVideo':
tmpArr = TWITCH_REGEXES[uriType].exec(uri);
videoId = tmpArr[1];
break;
default:
}
isValidURI = channelName || videoId ? true : false;
return {
type: 'twitch',
uri: uri,
channelName: channelName,
videoId: videoId,
isChatting: isChatting,
isValidURI: isValidURI,
};
};
/**
* get prefixed document property string
*
* @static
* @method getDocumentPrefixedProperty
* @param {String} propertyName
* @param {Boolean} isPropertyFirstCharToUpperCase
* @returns {String} return string
* @example
* console.log( aid.string.getDocumentPrefixedProperty('visibilityState', true) ); // return 'visibilityState' or 'webkitVisibilityState' or 'mozVisibilityState' or 'msVisibilityState' or 'oVisibilityState'.
* console.log( aid.string.getDocumentPrefixedProperty('12345', false) ); // if browser doesn't have property, return ''.
*/
string.getDocumentPrefixedProperty = function getDocumentPrefixedProperty(
propertyName,
isPropertyFirstCharToUpperCase
) {
if (!aid.isString(propertyName))
throw new TypeError('[aid.string.getDocumentPrefixedProperty] Type of propertyName parameter must be String.');
if (!aid.isBoolean(isPropertyFirstCharToUpperCase))
throw new TypeError(
'[aid.string.getDocumentPrefixedProperty] Type of isPropertyFirstCharToUpperCase parameter must be Boolean.'
);
if (propertyName in global.document) return propertyName;
var PREFIXES = ['webkit', 'moz', 'ms', 'o'],
isPropFirstCharUppercase = isPropertyFirstCharToUpperCase === true ? true : false;
var prop = '';
for (var i = 0, max = PREFIXES.length; i < max; i++) {
prop = isPropFirstCharUppercase ? propertyName.charAt(0).toUpperCase() + propertyName.slice(1) : propertyName;
prop = PREFIXES[i] + prop;
if (prop in document) return prop;
}
return '';
};
/**
* get prefixed element style property string
*
* @static
* @method getElementPrefixedStyle
* @returns {String} return string
* @example
* console.log( aid.string.getElementPrefixedStyle('transform', true) ); // return 'transform' or 'wekitTransform' or 'mozTransform' or 'msTransform' or 'oTransform'.
* console.log( aid.string.getElementPrefixedStyle('12345', false) ); // if browser doesn't have style property, return ''.
*/
string.getElementPrefixedStyle = function getElementPrefixedStyle(propertyName, isPropertyFirstCharToUpperCase) {
if (!aid.isString(propertyName))
throw new TypeError('[aid.string.getElementPrefixedStyle] Type of propertyName parameter must be String.');
if (!aid.isBoolean(isPropertyFirstCharToUpperCase))
throw new TypeError(
'[aid.string.getElementPrefixedStyle] Type of isPropertyFirstCharToUpperCase parameter must be Boolean.'
);
var style = global.document.createElement('div').style;
if (propertyName in style) return propertyName;
var PREFIXES = ['webkit', 'moz', 'ms', 'o'],
isPropFirstCharUppercase = isPropertyFirstCharToUpperCase === true ? true : false;
var prop = '';
for (var i = 0, max = PREFIXES.length; i < max; i++) {
prop = isPropFirstCharUppercase ? propertyName.charAt(0).toUpperCase() + propertyName.slice(1) : propertyName;
prop = PREFIXES[i] + prop;
if (prop in style) return prop;
}
return '';
};
/**
* get string removed no content elements from html
*
* @static
* @method removeNoContentElements
* @param {String} html
* @param (Boolean) withWhitespaceContent (optional)
* @returns {String} return html string
* @example
* console.log( aid.string.removeNoContentElements('<div><span>foo</span><p></p><em> </em></div>') ); // '<div><span>foo</span><em> </em></div>'
* console.log( aid.string.removeNoContentElements('<div><span>foo</span><p></p><em> </em></div>', true) ); // '<div><span>foo</span></div>'
*/
string.removeNoContentElements = function removeNoContentElements(html, withWhitespaceContent) {
if (!aid.isString(html))
throw new TypeError('[aid.string.removeNoContentElements] Type of html parameter must be String.');
if (aid.isDefined(withWhitespaceContent)) {
// withWhitespaceContent parameter is defined
if (!aid.isBoolean(withWhitespaceContent))
throw new TypeError(
'[aid.string.removeNoContentElements] Type of withWhitespaceContent parameter must be Boolean.'
);
}
if (!html) return '';
var regex = withWhitespaceContent ? /<([^>]+)>\s*<\/\1>/g : /<([^>]+)><\/\1>/g;
return html.replace(regex, '');
};
/**
* get string removed no content elements recursively from html
*
* @static
* @method removeNoContentElementsRecursively
* @param {String} html
* @param (Boolean) withWhitespaceContent (optional)
* @returns {String} return html string
* @example
* console.log( aid.string.removeNoContentElementsRecursively('<div><span>foo</span><p><span><strong><em></em></strong></span></p></div>') ); // '<div><span>foo</span></div>'
* console.log( aid.string.removeNoContentElementsRecursively('<div>foo<span><strong> <em> </em> </strong></span>bar<p> <strong> </strong> </p>baz</div>', true) ); // '<div>foobarbaz</div>'
*/
string.removeNoContentElementsRecursively = function removeNoContentElementsRecursively(html, withWhitespaceContent) {
if (!aid.isString(html))
throw new TypeError('[aid.string.removeNoContentElementsRecursively] Type of html parameter must be String.');
if (aid.isDefined(withWhitespaceContent)) {
// withWhitespaceContent parameter is defined
if (!aid.isBoolean(withWhitespaceContent))
throw new TypeError(
'[aid.string.removeNoContentElementsRecursively] Type of withWhitespaceContent parameter must be Boolean.'
);
}
if (!html) return '';
var regex = withWhitespaceContent ? /<([^>]+)>\s*<\/\1>/g : /<([^>]+)><\/\1>/g;
var htmlRemovedNoContentElements = html,
match = null;
while ((match = regex.exec(htmlRemovedNoContentElements))) {
htmlRemovedNoContentElements = string.removeNoContentElements(
htmlRemovedNoContentElements,
withWhitespaceContent
);
regex.lastIndex = 0;
}
return htmlRemovedNoContentElements;
};
/**
* null, undefined to empty string. if parameter is not null or undefined, return parameter.
*
* @static
* @method absentToEmpty
* @param {String} absentableStr
* @returns {String} return string
* @example
* console.log( aid.string.absentToEmpty(null) ); // null to ''.
* console.log( aid.string.absentToEmpty(undefined) ); // undefined to ''.
* console.log( aid.string.absentToEmpty('javascript') ); // if parameter exists, return parameter.
*/
string.absentToEmpty = function absentToEmpty(absentableStr) {
if (!aid.existy(absentableStr) || !aid.isString(absentableStr)) return '';
return absentableStr;
};
/**
* convert number to string type with commas
* http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
*
* @static
* @method numberWithCommas
* @param {Number} number
* @returns {String} return string
* @example
* console.log( aid.string.numberWithCommas(1000000) ); // '1,000,000'
*/
string.numberWithCommas = function numberWithCommas(number) {
if (!aid.isInteger(number))
throw new TypeError('[aid.string.numberWithCommas] Type of number parameter must be Integer Number.');
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};
/**
* convert 'translate(0px, 0px)' or 'translateX(0px)' or 'translateY(0px)' format string to { x: 0, y: 0 }
*
* @static
* @method getPositionFromTranslateStr
* @param {String} str
* @returns {Object} return object
* @example
* console.log( aid.string.getPositionFromTranslateStr('translate(99px)') ); // {x: 99, y: 0}
* console.log( aid.string.getPositionFromTranslateStr('translate(99px, 999px)') ); // {x: 99, y: 999}
* console.log( aid.string.getPositionFromTranslateStr('translateX(99px)') ); // {x: 99, y: 0}
* console.log( aid.string.getPositionFromTranslateStr('translateY(99px)') ); // {x: 0, y: 99}
*/
string.getPositionFromTranslateStr = function getPositionFromTranslateStr(str) {
if (!aid.isString(str))
throw new TypeError('[aid.string.getPositionFromTranslateStr] Type of str parameter must be String.');
var obj = {
x: 0,
y: 0,
};
if (!aid.isDefined(str)) return obj;
var values = str.match(/[+-]?(\d*\.)?\d+/g);
if (values.length <= 0) return obj;
if (/translateX/g.test(str)) {
obj.x = parseFloat(values[0], 10);
return obj;
}
if (/translateY/g.test(str)) {
obj.y = parseFloat(values[0], 10);
return obj;
}
obj.x = parseFloat(values[0], 10);
if (values.length > 1) obj.y = parseFloat(values[1], 10);
return obj;
};
/**
* get flag string is palindrome
*
* @static
* @method isPalindrome
* @param {String} str
* @returns {Boolean} return boolean
* @example
* console.log( aid.string.isPalindrome('abcdeedcba') ); // true
* console.log( aid.string.isPalindrome('motor') ); // false
*/
string.isPalindrome = function isPalindrome(str) {
if (!aid.isString(str)) throw new TypeError('[aid.string.isPalindrome] Type of str parameter must be String.');
if (str.length <= 1) return true;
if (str.slice(0, 1) !== str.slice(-1)) return false;
return aid.string.isPalindrome(str.slice(-1, 1));
};
/**
* get flag string is decoded
*
* @static
* @method isDecoded
* @param {String} str
* @param {Function} decodeFunc (optional)
* @returns {(Boolean|Error)} return boolean
* @example
* console.log( aid.isError(aid.string.isDecoded('%%%%%%%%%')) ); // true
* console.log( aid.string.isDecoded('エイド') ); // true
* console.log( aid.string.isDecoded(encodeURIComponent('エイド')) ); // false
*/
string.isDecoded = function isDecoded(str, decodeFunc) {
var decodeFn = global.decodeURIComponent;
if (!aid.isString(str)) throw new TypeError('[aid.string.isDecoded] Type of str parameter must be String.');
if (aid.isDefined(decodeFunc)) {
// decodeFunc parameter is defined
if (!aid.isFunction(decodeFunc)) {
throw new TypeError('[aid.string.isDecoded] Type of decodeFunc parameter must be Function.');
}
decodeFn = decodeFunc;
} else {
if (!aid.isFunction(decodeFn)) {
// no decodeURIComponent function and no decodeFunc parameter
throw new TypeError('[aid.string.isDecoded] Type of decodeFunc parameter must be defined.');
}
}
var decodedStr = '';
try {
decodedStr = decodeFn.call(null, str);
} catch (error) {
return error;
}
if (decodedStr === str) return true;
return false;
};
/**
* get decoded string
*
* @static
* @method decodeRecursively
* @param {String} str
* @param {Function} decodeFunc (optional)
* @returns {(String|Error)} return string
* @example
* console.log( aid.string.decodeRecursively(encodeURIComponent(encodeURIComponent('エイド'))) ); // 'エイド'
*/
string.decodeRecursively = function decodeRecursively(str, decodeFunc) {
var decodeFn = global.decodeURIComponent;
if (!aid.isString(str)) throw new TypeError('[aid.string.decodeRecursively] Type of str parameter must be String.');
if (aid.isDefined(decodeFunc)) {
// decodeFunc parameter is defined
if (!aid.isFunction(decodeFunc)) {
throw new TypeError('[aid.string.decodeRecursively] Type of decodeFunc parameter must be Function.');
}
decodeFn = decodeFunc;
} else {
if (!aid.isFunction(decodeFn)) {
// no decodeURIComponent function and no decodeFunc parameter
throw new TypeError('[aid.string.decodeRecursively] Type of decodeFunc parameter must be defined.');
}
}
var decodedStr = '';
try {
decodedStr = decodeFn.call(null, str);
} catch (error) {
return error;
}
if (decodedStr !== str) {
return decodeRecursively(decodedStr, decodeFn);
}
return decodedStr;
};
/**
* get escaped string to use in RegExp constructor
* refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
*
* @static
* @method escapeRegExp
* @param {String} str
* @returns {String} return string
* @example
* console.log( aid.string.escapeRegExp('meta characters: .*+?^${}()|[]\/') );
*/
string.escapeRegExp = function escapeRegExp(str) {
if (!aid.isString(str)) throw new TypeError('[aid.string.escapeRegExp] Type of str parameter must be String.');
// escape RegExp meta characters: ., *, +, ?, ^, $, {, }, (, ), |, [, ], \
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
/**
* get aspectFill content size
*
* @static
* @method getSizeAspectFill
* @param {Number} srcWidth
* @param {Number} srcHeight
* @param {Number} fillWidth
* @param {Number} fillHeight
* @returns {Object} return { width: Number, height: Number }
* @example
* console.log( aid.math.getSizeAspectFill(960, 640, window.innerWidth, window.innerHeight) );
*/
math.getSizeAspectFill = function getSizeAspectFill(srcWidth, srcHeight, fillWidth, fillHeight) {
var isNumber = aid.isNumber;
if (!isNumber(srcWidth) || !isNumber(srcHeight) || !isNumber(fillWidth) || !isNumber(fillHeight)) {
throw new TypeError('[aid.math.getSizeAspectFill] Type of parameters must be Number.');
}
var modifiedSizeW = fillWidth,
modifiedSizeH = Math.ceil((fillWidth / srcWidth) * srcHeight);
if (modifiedSizeH < fillHeight) {
modifiedSizeW = Math.ceil((fillHeight / srcHeight) * srcWidth);
modifiedSizeH = fillHeight;
}
return {
width: modifiedSizeW,
height: modifiedSizeH,
};
};
/**
* get aspectFit content size
*
* @static
* @method getSizeAspectFit
* @param {Number} srcWidth
* @param {Number} srcHeight
* @param {Number} fitWidth
* @param {Number} fitHeight
* @returns {Object} return { width: Number, height: Number }
* @example
* console.log( aid.math.getSizeAspectFit(960, 640, window.innerWidth, window.innerHeight) );
*/
math.getSizeAspectFit = function getSizeAspectFit(srcWidth, srcHeight, fitWidth, fitHeight) {
var isNumber = aid.isNumber;
if (!isNumber(srcWidth) || !isNumber(srcHeight) || !isNumber(fitWidth) || !isNumber(fitHeight)) {
throw new TypeError('[aid.math.getSizeAspectFit] Type of parameters must be Number.');
}
var ratio = Math.min(fitWidth / srcWidth, fitHeight / srcHeight),
modifiedSizeW = Math.ceil(srcWidth * ratio),
modifiedSizeH = Math.ceil(srcHeight * ratio);
return {
width: modifiedSizeW,
height: modifiedSizeH,
};
};
/**
* get widthFit content size
*
* @static
* @method getSizeWidthFit
* @param {Number} srcWidth
* @param {Number} srcHeight
* @param {Number} fitWidth
* @returns {Object} return { width: Number, height: Number }
* @example
* console.log( aid.math.getSizeWidthFit(960, 640, window.innerWidth) );
*/
math.getSizeWidthFit = function getSizeWidthFit(srcWidth, srcHeight, fitWidth) {
var isNumber = aid.isNumber;
if (!isNumber(srcWidth) || !isNumber(srcHeight) || !isNumber(fitWidth)) {
throw new TypeError('[aid.math.getSizeWidthFit] Type of parameters must be Number.');
}
var modifiedSizeW = fitWidth,
modifiedSizeH = Math.ceil((fitWidth / srcWidth) * srcHeight);
return {
width: modifiedSizeW,
height: modifiedSizeH,
};
};
/**
* get flag whether 2 numbers are equal by Number.EPSILON (2.220446049250313e-16)
*
* @static
* @method isEpsilonEqual
* @param {Number} number_a
* @param {Number} number_b
* @returns {Boolean} return boolean
* @example
* console.log( aid.math.isEpsilonEqual(0.1 + 0.2, 0.3) );
*/
math.isEpsilonEqual = function isEpsilonEqual(number_a, number_b) {
if (!aid.isNumber(number_a) || !aid.isNumber(number_b)) {
throw new TypeError('[aid.math.isEpsilonEqual] Type of parameters must be Number.');
}
var epsilon = aid.isDefined(Number.EPSILON) ? Number.EPSILON : 2.220446049250313e-16;
return Math.abs(number_a - number_b) < epsilon;
};
/**
* get flag that looped values in 1 ~ totalLength has searchIndex.
* value increases from startIndex to (firstIndex + loopGap - 1).
* if value is bigger than totalLength, change value to 1.
* if searchIndex in values, return true.
* otherwise, return false.
*
* @static
* @method isIndexInLoop
* @param {Number} totalLength
* @param {Number} loopGap
* @param {Number} firstIndex
* @param {Number} searchIndex
* @returns {Boolean} return boolean
* @example
* console.log( aid.math.isIndexInLoop(8, 5, 6, 3) ); // false
* console.log( aid.math.isIndexInLoop(8, 5, 6, 6) ); // true
*/
math.isIndexInLoop = function isIndexInLoop(totalLength, loopGap, firstIndex, searchIndex) {
if (arguments.length < 4) throw new Error('[aid.math.isIndexInLoop] Requires 4 parameters.');
var isInteger = aid.isInteger;
if (!isInteger(totalLength) || !isInteger(loopGap) || !isInteger(firstIndex) || !isInteger(searchIndex)) {
throw new TypeError('[aid.math.isIndexInLoop] Type of parameters must be Number.');
}
if (totalLength < 1 || firstIndex < 1) {
throw new Error('[aid.math.isIndexInLoop] totalLength, firstIndex parameters cannot smaller than 1.');
}
if (loopGap > totalLength) {
throw new Error('[aid.math.isIndexInLoop] loopGap parameter cannot bigger than totalLength parameter.');
}
var index = firstIndex;
for (var i = 0; i < loopGap; i++) {
if (index === searchIndex) return true;
index = index + 1 > totalLength ? 1 : index + 1;
}
return false;
};
/**
* get looped index from firstIndex in 1 ~ totalLength.
* value increases from firstIndex to (firstIndex + loopGap - 1).
* if value is bigger than totalLength, change value to 1. and, return value.
*
* @static
* @method getLoopedLastIndex
* @param {Number} totalLength
* @param {Number} loopGap
* @param {Number} firstIndex
* @returns {Number} return Int Number
* @example
* console.log( aid.math.getLoopedLastIndex(8, 1, 7) ); // 7
* console.log( aid.math.getLoopedLastIndex(8, 4, 7) ); // 2
*/
math.getLoopedLastIndex = function getLoopedLastIndex(totalLength, loopGap, firstIndex) {
if (arguments.length < 3) throw new Error('[aid.math.getLoopedLastIndex] Requires 3 parameters.');
var isInteger = aid.isInteger;
if (!isInteger(totalLength) || !isInteger(loopGap) || !isInteger(firstIndex)) {
throw new TypeError('[aid.math.getLoopedLastIndex] Type of parameters must be Integer Number.');
}
if (totalLength < 1 || firstIndex < 1) {
throw new Error('[aid.math.getLoopedLastIndex] totalLength, firstIndex parameters cannot smaller than 1.');
}
if (loopGap > totalLength || firstIndex > totalLength) {
throw new Error(
'[aid.math.getLoopedLastIndex] loopGap, firstIndex parameters cannot bigger than totalLength parameter.'
);
}
var index = firstIndex;
for (var i = 0; i < loopGap - 1; i++) {
index = index + 1 > totalLength ? 1 : index + 1;
}
return index;
};
/**
* get reverse looped index from lastIndex in 1 ~ totalLength.
* value decreases from lastIndex to (lastIndex - loopGap - 1).
* if value is smaller than 0, change value to totalLength. and return value.
*
* @static
* @method getReverseLoopedFirstIndex
* @param {Number} totalLength
* @param {Number} loopGap
* @param {Number} firstIndex
* @returns {Number} return Int Number
* @example
* console.log( aid.math.getReverseLoopedFirstIndex(8, 4, 2) ); // 7
* console.log( aid.math.getReverseLoopedFirstIndex(8, 8, 2) ); // 3
*/
math.getReverseLoopedFirstIndex = function getReverseLoopedFirstIndex(totalLength, loopGap, lastIndex) {
if (arguments.length < 3) throw new Error('[aid.math.getReverseLoopedFirstIndex] Requires 3 parameters.');
var isInteger = aid.isInteger;
if (!isInteger(totalLength) || !isInteger(loopGap) || !isInteger(lastIndex)) {
throw new TypeError('[aid.math.getReverseLoopedFirstIndex] Type of parameters must be Integer Number.');
}
if (totalLength < 1 || lastIndex < 1) {
throw new Error('[aid.math.getReverseLoopedFirstIndex] totalLength, lastIndex parameters cannot smaller than 1.');
}
if (loopGap > totalLength || lastIndex > totalLength) {
throw new Error(
'[aid.math.getReverseLoopedFirstIndex] loopGap, lastIndex parameters cannot bigger than totalLength parameter.'
);
}
var index = lastIndex;
for (var i = 0; i < loopGap - 1; i++) {
index = index - 1 < 1 ? totalLength : index - 1;
}
return index;
};
/**
* get factorial value
*
* @static
* @method factorial
* @param {Number} Int number
* @returns {Number} return Int Number
* @example
* console.log( aid.math.factorial(-99) ); // 1
* console.log( aid.math.factorial(5) ); // 5 * 4 * 3 * 2 * 1 = 120
*/
math.factorial = function factorial(number) {
if (!aid.isInteger(number)) throw new TypeError('[aid.math.factorial] Type of parameters must be Integer Number.');
if (number < 1) return 1;
return number * aid.math.factorial(number - 1);
};
/**
* get board pagination info object
*
* @static
* @method getObjForPagination
* @param {Number} totalPostNum
* @param {Number} displayPostNumPerPage
* @param {Number} displayPaginationBtnNum
* @param {Number} pageIndex
* @returns {Object} return object
* @example
* console.log( aid.math.getObjForPagination(39, 10, 5, 1) ); // {totalPostNum: 39, displayPostNumPerPage: 10, displayPaginationBtnNum: 5, pageIndex: 1, totalPageNum: 4, prevPageIndex: 0, firstPageIndex: 1, lastPageIndex: 4, nextPageIndex: 0}
*/
math.getObjForPagination = function getObjForPagination(
totalPostNum,
displayPostNumPerPage,
displayPaginationBtnNum,
pageIndex
) {
if (arguments.length < 4) throw new Error('[aid.math.getObjForPagination] Requires 4 parameters.');
var isInteger = aid.isInteger;
if (
!isInteger(totalPostNum) ||
!isInteger(displayPostNumPerPage) ||
!isInteger(displayPaginationBtnNum) ||
!isInteger(pageIndex)
) {
throw new TypeError('[aid.math.getObjForPagination] Type of parameters must be Integer Number.');
}
if (totalPostNum <= 0 || displayPostNumPerPage <= 0 || displayPaginationBtnNum <= 0 || pageIndex <= 0) {
throw new TypeError('[aid.math.getObjForPagination] Type of parameters must be positive Integer Number.');
}
var totalPageNum = Math.ceil(totalPostNum / displayPostNumPerPage);
if (pageIndex < 1) pageIndex = 1;
if (pageIndex > totalPageNum) pageIndex = totalPageNum;
var paginationBtnGroupIndex = Math.floor((pageIndex - 1) / displayPaginationBtnNum),
prevPageIndex = paginationBtnGroupIndex * displayPaginationBtnNum,
firstPageIndex = paginationBtnGroupIndex * displayPaginationBtnNum + 1,
lastPageIndex = firstPageIndex + displayPaginationBtnNum - 1,
nextPageIndex = lastPageIndex + 1;
if (lastPageIndex > totalPageNum) lastPageIndex = totalPageNum;
if (nextPageIndex > totalPageNum) nextPageIndex = 0;
return {
totalPostNum: totalPostNum,
displayPostNumPerPage: displayPostNumPerPage,
displayPaginationBtnNum: displayPaginationBtnNum,
pageIndex: pageIndex,
totalPageNum: totalPageNum,
prevPageIndex: prevPageIndex,
firstPageIndex: firstPageIndex,
lastPageIndex: lastPageIndex,
nextPageIndex: nextPageIndex,
};
};
/**
* convert degree to radian
*
* @static
* @method degreeToRadian
* @param {Number} degree
* @returns {Number} return number
* @example
* console.log( aid.math.degreeToRadian(90) ); // 1.5707963267948966
*/
math.degreeToRadian = function degreeToRadian(degree) {
if (!aid.isNumber(degree))
throw new TypeError('[aid.math.degreeToRadian] Type of degree parameter must be Number.');
return (degree * Math.PI) / 180;
};
/**
* convert radian to radian
*
* @static
* @method radianToDegree
* @param {Number} radian
* @returns {Number} return number
* @example
* console.log( aid.math.radianToDegree(1.5707963267948966) ); // 90
*/
math.radianToDegree = function radianToDegree(radian) {
if (!aid.isNumber(radian))
throw new TypeError('[aid.math.radianToDegree] Type of degree parameter must be Number.');
return (radian * 180) / Math.PI;
};
/**
* get height of right triangle from base line width, acute angle
*
* @static
* @method getHeightOfRightTriangle
* @param {Number} baseLineWidth
* @param {Number} acuteAngleDegree
* @returns {Number} return number
* @example
* console.log( aid.math.getHeightOfRightTriangle(100, 30) ); // 57.73502691896256
*/
math.getHeightOfRightTriangle = function getHeightOfRightTriangle(baseLineWidth, acuteAngleDegree) {
if (!aid.isNumber(baseLineWidth) || !aid.isNumber(acuteAngleDegree)) {
throw new TypeError('[aid.math.getHeightOfRightTriangle] Type of parameters must be Number.');
}
if (acuteAngleDegree >= 90)
throw new Error(
'[aid.math.getHeightOfRightTriangle] acuteAngleDegree parameter cannot greater than or equal to 90.'
);
return baseLineWidth * Math.tan(math.degreeToRadian(acuteAngleDegree));
};
/**
* get distance between two points
*
* @static
* @method getDistanceBetweenTwoPoints
* @param {Object} point1
* @param {Object} point2
* @returns {Number} return number
* @example
* var point1 = {x: 0, y: 0}
* var point2 = {x: 100, y: 100};
* console.log( aid.math.getDistanceBetweenTwoPoints(point1, point2) ); // 141.4213562373095
*/
math.getDistanceBetweenTwoPoints = function getDistanceBetweenTwoPoints(point1, point2) {
if (!aid.isObject(point1) || !aid.isObject(point2)) {
throw new TypeError('[aid.math.getDistanceBetweenTwoPoints] Type of parameters must be Object.');
}
var isNumber = aid.isNumber;
if (!isNumber(point1.x) || !isNumber(point1.y) || !isNumber(point2.x) || !isNumber(point2.y)) {
throw new TypeError(
'[aid.math.getDistanceBetweenTwoPoints] Type of parameters must be Object that has x, y properties.'
);
}
var distance = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
return distance;
};
/**
* get orthogonal point between line and some point
*
* @static
* @method getOrthogonalPointBetweenLineAndSomePoint
* @param {Object} collinearPoint1
* @param {Object} collinearPoint2
* @param {Object} somePoint
* @returns {Object} return object
* @example
* var collinearPoint1 = {x: 0, y: 0};
* var collinearPoint2 = {x: 100, y: 100};
* var somePoint = {x: 50, y: 100};
* console.log( aid.math.getOrthogonalPointBetweenLineAndSomePoint(collinearPoint1, collinearPoint2, somePoint) ); // {x: 75, y: 75}
*/
math.getOrthogonalPointBetweenLineAndSomePoint = function getOrthogonalPointBetweenLineAndSomePoint(
collinearPoint1,
collinearPoint2,
somePoint
) {
var isObject = aid.isObject;
if (!isObject(collinearPoint1) || !isObject(collinearPoint2) || !isObject(somePoint)) {
throw new TypeError('[aid.math.getOrthogonalPointBetweenLineAndSomePoint] Type of parameters must be Object.');
}
var isNumber = aid.isNumber;
if (
!isNumber(collinearPoint1.x) ||
!isNumber(collinearPoint1.y) ||
!isNumber(collinearPoint2.x) ||
!isNumber(collinearPoint2.y) ||
!isNumber(somePoint.x) ||
!isNumber(somePoint.y)
) {
throw new TypeError(
'[aid.math.getOrthogonalPointBetweenLineAndSomePoint] Type of parameters must be Object that has x, y properties.'
);
}
if (collinearPoint1.x === collinearPoint2.x && collinearPoint1.y === collinearPoint2.y) {
// collinearPoint1 is equal to collinearPoint2. no line.
return null;
}
var diffX = collinearPoint2.x - collinearPoint1.x,
diffY = collinearPoint2.y - collinearPoint1.y;
if (diffX === 0)
return {
x: collinearPoint1.x,
y: somePoint.y,
};
if (diffY === 0)
return {
x: somePoint.x,
y: collinearPoint1.y,
};
var lineSlope = diffY / diffX,
somePointLineSlope = -1 / lineSlope;
var orthogonalPoint = {};
orthogonalPoint.x =
(somePointLineSlope * somePoint.x - lineSlope * collinearPoint1.x + collinearPoint1.y - somePoint.y) /
(somePointLineSlope - lineSlope);
orthogonalPoint.y = somePointLineSlope * (orthogonalPoint.x - somePoint.x) + somePoint.y;
return orthogonalPoint;
};
/**
* getRandomPositiveNegative
*
* @static
* @method getRandomPositiveNegative
* @returns {Number} return Number
* @example
* console.log( aid.math.getRandomPositiveNegative() ); // -1 or 1
*/
math.getRandomPositiveNegative = function getRandomPositiveNegative() {
return Math.round(Math.random()) > 0 ? 1 : -1;
};
/**
* getRandomFloat
*
* @static
* @method getRandomFloat
* @param {Number} min
* @param {Number} max
* @returns {Number} return Number
* @example
* console.log( aid.math.getRandomFloat(-99.999, 99.999) ); // -99.999 <= aid.math.getRandomFloat(-99.999, 99.999) < 99.999
*/
math.getRandomFloat = function getRandomFloat(min, max) {
var isNumber = aid.isNumber;
if (!isNumber(min) || !isNumber(max)) {
throw new TypeError('[aid.math.getRandomFloat] Type of parameters must be Number.');
}
return min + Math.random() * (max - min);
};
/**
* getRandomInt
*
* @static
* @method getRandomInt
* @param {Number} minInt
* @param {Number} maxInt
* @returns {Number} return Number
* @example
* console.log( aid.math.getRandomInt(-99, 99) ); // -99 ~ 99
*/
math.getRandomInt = function getRandomInt(minInt, maxInt) {
var isInteger = aid.isInteger;
if (!isInteger(minInt) || !isInteger(maxInt)) {
throw new TypeError('[aid.math.getRandomInt] Type of parameters must be Integer Number.');
}
return minInt + Math.floor(Math.random() * (maxInt - minInt + 1));
};
/**
* remap
*
* @static
* @method remap
* @param {Number} target
* @param {Number} min
* @param {Number} max
* @param {Number} remapedMin
* @param {Number} remapedMax
* @returns {Number} return Number
* @example
* console.log( aid.math.remap(0.5, 0, 1, 0, 100) ); // 50
*/
math.remap = function remap(target, min, max, remapedMin, remapedMax) {
var isNumber = aid.isNumber;
if (!isNumber(target) || !isNumber(min) || !isNumber(max) || !isNumber(remapedMin) || !isNumber(remapedMax)) {
throw new TypeError('[aid.math.remap] Type of parameters must be Number.');
}
return ((target - min) / (max - min)) * (remapedMax - remapedMin) + remapedMin;
};
/**
* greater than
*
* @static
* @method gt
* @param {Number} number
* @param {Number} number
* @returns {Boolean} return boolean
* @example
* console.log( aid.math.gt(1)(9) ); // true
*/
math.gt = aid.curry2(function(lhs, rhs) {
if (!aid.allOf(aid.isNumber(lhs), aid.isNumber(rhs)))
throw new TypeError('[aid.math.gt] Type of parameters must be Number.');
return lhs > rhs;
});
/**
* less than
*
* @static
* @method lt
* @param {Number} number
* @param {Number} number
* @returns {Boolean} return boolean
* @example
* console.log( aid.math.lt(9)(1) ); // true
*/
math.lt = aid.curry2(function(lhs, rhs) {
if (!aid.allOf(aid.isNumber(lhs), aid.isNumber(rhs)))
throw new TypeError('[aid.math.lt] Type of parameters must be Number.');
return lhs < rhs;
});
/**
* greater than equal
*
* @static
* @method gte
* @param {Number} number
* @param {Number} number
* @returns {Boolean} return boolean
* @example
* console.log( aid.math.gte(1)(1) ); // true
* console.log( aid.math.gte(1)(9) ); // true
*/
math.gte = aid.curry2(function(lhs, rhs) {
if (!aid.allOf(aid.isNumber(lhs), aid.isNumber(rhs)))
throw new TypeError('[aid.math.gte] Type of parameters must be Number.');
return lhs >= rhs;
});
/**
* less than equal
*
* @static
* @method lte
* @param {Number} number
* @param {Number} number
* @returns {Boolean} return boolean
* @example
* console.log( aid.math.lte(1)(1) ); // true
* console.log( aid.math.lte(9)(1) ); // true
*/
math.lte = aid.curry2(function(lhs, rhs) {
if (!aid.allOf(aid.isNumber(lhs), aid.isNumber(rhs)))
throw new TypeError('[aid.math.lte] Type of parameters must be Number.');
return lhs <= rhs;
});
/**
* days name
*
* @static
* @property date.DAYS
* @returns {Array} return array
* @example
*/
date.DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
/**
* months name
*
* @static
* @property date.MONTHS
* @returns {Array} return array
* @example
*/
date.MONTHS = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
/**
* 1 minutes to second unit (60 sec)
*
* @static
* @property date.MIN_TO_SEC
* @returns {Number} return Int number
* @example
*/
date.MIN_TO_SEC = 60;
/**
* 1 hour to second unit (60 * 60 = 3600 sec)
*
* @static
* @property date.HOUR_TO_SEC
* @returns {Number} return Int number
* @example
*/
date.HOUR_TO_SEC = 3600;
/**
* 1 day to second unit (60 * 60 * 24 = 86400 sec)
*
* @static
* @property date.DAY_TO_SEC
* @returns {Number} return Int number
* @example
*/
date.DAY_TO_SEC = 86400;
/**
* equal to Array.indexOf
*
* @static
* @method indexOf
* @param {Array} arr
* @param {Object} target
* @returns {Number} return Int number
* @example
* var arr = ['foo', 1, 'bar', 2, null, 99];
* console.log( aid.array.indexOf(arr, undefined) ); // -1
* console.log( aid.array.indexOf(arr, 99) ); // 5
*/
array.indexOf = function indexOf(arr, target) {
if (!aid.isArray(arr)) throw new TypeError('[aid.math.indexOf] Type of arr parameters must be Array.');
for (var i = 0, max = arr.length; i < max; ++i) {
if (arr[i] === target) return i;
}
return -1;
};
/**
* get index of minimum number in Array
*
* @static
* @method indexOfMin
* @param {Array} arr
* @param {Number} startSearchIndex
* @returns {Number} return Int number
* @example
* var arr = [18, 6, 66, 44, 9, 22, 14];
* console.log( aid.array.indexOfMin(arr, 0) ); // 1
* console.log( aid.array.indexOfMin(arr, 1) ); // 1
* console.log( aid.array.indexOfMin(arr, 2) ); // 4
* console.log( aid.array.indexOfMin(arr, 2) ); // 4
* console.log( aid.array.indexOfMin(arr, 4) ); // 4
* console.log( aid.array.indexOfMin(arr, 5) ); // 6
* console.log( aid.array.indexOfMin(arr, 6) ); // 6
*/
array.indexOfMin = function indexOfMin(arr, startSearchIndex) {
if (!aid.isArray(arr) || !aid.isInteger(startSearchIndex)) return -1;
var startIndex = startSearchIndex > 0 ? startSearchIndex : 0;
if (arr.length <= startIndex) return -1;
var minVal = arr[startIndex],
minIndex = startIndex;
for (var i = minIndex + 1, max = arr.length; i < max; i++) {
if (arr[i] < minVal) {
minVal = arr[i];
minIndex = i;
}
}
return minIndex;
};
/**
* swap two elements in Array
*
* @static
* @method swap
* @param {Array} arr
* @param {Number} firstIndex
* @param {Number} secondIndex
* @returns {Array} return array or null
* @example
* var arr = [18, 6, 66, 44, 9, 22, 14];
* console.log( aid.array.swap(arr, 0, 1) ); // [6, 18, 66, 44, 9, 22, 14]
*/
array.swap = function swap(arr, firstIndex, secondIndex) {
if (arguments.length !== 3) return null;
if (!aid.isArray(arr) || !aid.isInteger(firstIndex) || !aid.isInteger(secondIndex)) return null;
if (firstIndex < 0 || firstIndex >= arr.length || secondIndex < 0 || secondIndex >= arr.length) return null;
var tmp = arr[firstIndex];
arr[firstIndex] = arr[secondIndex];
arr[secondIndex] = tmp;
return arr;
};
/**
* apply selection sort in Array
*
* @static
* @method selectionSort
* @param {Array} arr
* @returns {Array} return array or null
* @example
* var arr = [18, 6, 66, 44, 9, 22, 14];
* console.log( aid.array.selectionSort(arr) ); // [6, 9, 14, 18, 22, 44, 66]
*/
array.selectionSort = function selectionSort(arr) {
if (!aid.isArray(arr) || arr.length <= 0) return null;
var minIndex = 0;
for (var i = 0, max = arr.length - 1; i < max; i++) {
minIndex = array.indexOfMin(arr, i);
array.swap(arr, i, minIndex);
}
return arr;
};
/**
* apply insertion sort in Array
* https://ko.khanacademy.org/computing/computer-science/algorithms/insertion-sort/a/insertion-sort
*
* @static
* @method insertionSort
* @param {Array} arr
* @returns {Array} return array or null
* @example
* var arr = [18, 6, 66, 44, 9, 22, 14];
* console.log( aid.array.insertionSort(arr) ); // [6, 9, 14, 18, 22, 44, 66]
*/
array.insertionSort = function insertionSort(arr) {
if (!aid.isArray(arr) || arr.length <= 0) return null;
var value;
for (var i = 1, max = arr.length; i < max; i++) {
value = arr[i];
for (var j = i - 1; j >= 0 && arr[j] > value; j--) {
arr[j + 1] = arr[j];
}
arr[j + 1] = value;
}
return arr;
};
/**
* merge sort to startIndex ~ middleIndex elements, and middleIndex ~ endIndex elements.
* this function is only used by aid.array.mergeSort function.
*
* @static
* @method merge
* @param {Array} arr
* @param {Number} startIndex
* @param {Number} middleIndex
* @param {Number} endIndex
* @returns {Array} return array or null
* @example
*/
array.merge = function merge(arr, startIndex, middleIndex, endIndex) {
if (!aid.isArray(arr) || arr.length <= 0) return null;
if (startIndex > middleIndex || middleIndex > endIndex) return null;
var i = startIndex,
j = middleIndex + 1,
k = startIndex,
tmpArr = [];
// compare startIndex ~ middleIndex datas, and middleIndex + 1 ~ endIndex datas.
while (i <= middleIndex && j <= endIndex) {
if (arr[i] <= arr[j]) {
tmpArr[k++] = arr[i++];
} else {
tmpArr[k++] = arr[j++];
}
}
if (i > middleIndex) {
// set rest right array
for (var r = j; r <= endIndex; r++) {
tmpArr[k] = arr[r];
k++;
}
} else {
// set rest left array
for (var l = i; l <= middleIndex; l++) {
tmpArr[k] = arr[l];
k++;
}
}
// set sorted values to arr.
for (var n = startIndex; n <= endIndex; n++) {
arr[n] = tmpArr[n];
}
return arr;
};
/**
* apply merge sort in Array
* https://ko.khanacademy.org/computing/computer-science/algorithms/merge-sort/a/analysis-of-merge-sort
*
* @static
* @method mergeSort
* @param {Array} arr
* @param {Number} startIndex
* @param {Number} endIndex
* @returns {Array} return array or null
* @example
* var arr = [18, 6, 66, 44, 9, 22, 14];
* console.log( aid.array.mergeSort(arr, 0, arr.length - 1) ); // [6, 9, 14, 18, 22, 44, 66]
*/
array.mergeSort = function mergeSort(arr, startIndex, endIndex) {
if (!aid.isArray(arr) || arr.length <= 0) return null;
if (arr.length <= 1 || startIndex === endIndex) return null;
if (startIndex + 1 === endIndex) {
if (arr[startIndex] > arr[endIndex]) {
aid.array.swap(arr, startIndex, endIndex);
}
return arr;
}
var middleIndex = Math.floor((startIndex + endIndex) / 2);
aid.array.mergeSort(arr, startIndex, middleIndex);
aid.array.mergeSort(arr, middleIndex + 1, endIndex);
aid.array.merge(arr, startIndex, middleIndex, endIndex);
return arr;
};
/**
* quick sort with use pivot value.
* this function is only used by aid.array.quickSort function.
*
* @static
* @method getPivotIndexAfterPartition
* @param {Array} arr
* @param {Number} startIndex
* @param {Number} endIndex
* @returns {Number} return index of pivot (between group that less or equal to the pivot, and group that greater than the pivot). Int Number.
* @example
* var arr = [18, 6, 66, 44, 9, 22, 14];
* console.log( aid.array.getPivotIndexAfterPartition(arr, 0, arr.length - 1) ); // [6, 9, 14, 44, 18, 22, 66]
*/
array.getPivotIndexAfterPartition = function getPivotIndexAfterPartition(arr, startIndex, endIndex) {
if (!aid.isArray(arr) || arr.length <= 0) return -1;
if (startIndex > endIndex) return -1;
if (arr.length <= 1) return 0;
var pivot = arr[endIndex],
swapIndex = startIndex;
for (var i = startIndex; i < endIndex; i++) {
if (arr[i] <= pivot) {
aid.array.swap(arr, i, swapIndex);
swapIndex++;
}
}
if (endIndex !== swapIndex) aid.array.swap(arr, endIndex, swapIndex);
return swapIndex;
};
/**
* apply quick sort in Array
* https://ko.khanacademy.org/computing/computer-science/algorithms/quick-sort/a/overview-of-quicksort
*
* @static
* @method quickSort
* @param {Array} arr
* @param {Number} startIndex
* @param {Number} endIndex
* @returns {Array} return array or null
* @example
* var arr = [18, 6, 66, 44, 9, 22, 14];
* console.log( aid.array.quickSort(arr, 0, arr.length - 1) ); // [6, 9, 14, 18, 22, 44, 66]
*/
array.quickSort = function quickSort(arr, startIndex, endIndex) {
if (!aid.isArray(arr) || arr.length <= 0) return null;
if (arr.length <= 1 || startIndex === endIndex) return null;
if (startIndex < endIndex) {
var pivotIndex = aid.array.getPivotIndexAfterPartition(arr, startIndex, endIndex);
aid.array.quickSort(arr, startIndex, pivotIndex - 1);
aid.array.quickSort(arr, pivotIndex + 1, endIndex);
}
return arr;
};
/**
* remove element in Array, and return cloned Array
*
* @static
* @method remove
* @param {Array} arr
* @param {Object} target
* @returns {Array} return array
* @example
* var arr = [1, 2, 3, 4, 5, 99];
* console.log( aid.array.remove(arr, 99) ); // [1, 2, 3, 4, 5]
*/
array.remove = function remove(arr, target) {
if (!aid.isArray(arr)) throw new TypeError('[aid.array.remove] Type of arr parameter must be Array.');
var clonedArr = arr.slice(0),
index = array.indexOf(clonedArr, target);
if (index >= 0) clonedArr.splice(index, 1);
return clonedArr;
};
/**
* get 2 x 2 Matrix Array
*
* @static
* @method getMatrixArr
* @param {Number} rowNum
* @param {Number} columnNum
* @param {Object} initialVal
* @returns {Array} return array
* @example
* console.log( aid.array.getMatrixArr(2, 2, 0) ); // [ [0, 0], [0, 0] ]
*/
array.getMatrixArr = function getMatrixArr(rowNum, columnNum, initialVal) {
if (!aid.isInteger(rowNum) || !aid.isInteger(columnNum)) {
throw new TypeError('[aid.array.getMatrixArr] Type of rowNum, columnNum parameters must be Integer Number.');
}
if (arguments.length !== 3) throw Error('[aid.array.getMatrixArr] Requires 3 parameters.');
var arr = [],
columns = [];
for (var i = 0; i < rowNum; ++i) {
columns = [];
for (var j = 0; j < columnNum; ++j) {
columns[j] = initialVal;
}
arr[i] = columns;
}
return arr;
};
/**
* binary search version of Array.indexOf
*
* @static
* @method binaryIndexOf
* @param {Array} sortedArray
* @param {Object} target
* @returns {Number} return Int number
* @example
* var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
* console.log( aid.array.binaryIndexOf(arr, 7) ); // 7
*/
array.binaryIndexOf = function binaryIndexOf(sortedArray, target) {
if (!aid.isArray(sortedArray) || sortedArray.length <= 0) return -1;
var first = 0,
last = sortedArray.length - 1,
middle;
while (first <= last) {
middle = Math.floor((first + last) / 2);
if (target === sortedArray[middle]) {
return middle;
} else {
if (target < sortedArray[middle]) {
last = middle - 1;
} else {
first = middle + 1;
}
}
}
return -1;
};
/**
* get object has property in array.
* this object is first matched element in array by value or regex.
*
* @static
* @method getFirstObjectHasProperty
* @param {Array} arrayHasObjects
* @param {String} propertyKey
* @param {Object} findPropertyValueOrRegex
* @returns {Object} return object
* @example
* var arrayHasObjects = [{ no: 11 }, { no: 22 }];
* console.log( aid.array.getFirstObjectHasProperty(arrayHasObjects, 'index', 11) ); // null
* console.log( aid.array.getFirstObjectHasProperty(arrayHasObjects, 'no', 11) ); // {no: 11}
* console.log( aid.array.getFirstObjectHasProperty(arrayHasObjects, 'no', new RegExp('^1')) ); // {no: 11}
* console.log( aid.array.getFirstObjectHasProperty(arrayHasObjects, 'no', /^(1)\d/) ); // {no: 11}
*/
array.getFirstObjectHasProperty = function getFirstObjectHasProperty(
arrayHasObjects,
propertyKey,
findPropertyValueOrRegex
) {
if (!aid.isArray(arrayHasObjects) || arrayHasObjects.length <= 0) return null;
if (!aid.isString(propertyKey)) return null;
var obj,
result = null;
for (var i = 0, max = arrayHasObjects.length; i < max; ++i) {
obj = arrayHasObjects[i];
if (!obj.hasOwnProperty(propertyKey)) continue;
if (aid.isRegExp(findPropertyValueOrRegex)) {
if (findPropertyValueOrRegex.test(obj[propertyKey])) {
result = obj;
break;
}
} else {
if (obj[propertyKey] === findPropertyValueOrRegex) {
result = obj;
break;
}
}
}
return result;
};
/**
* get index of object has property in array.
* this object is first matched element in array by value or regex.
*
* @static
* @method getIndexOfObjectHasProperty
* @param {Array} arrayHasObjects
* @param {String} propertyKey
* @param {Object} findPropertyValueOrRegex
* @returns {Object} return object
* @example
* TODO:
*/
array.getIndexOfObjectHasProperty = function getIndexOfObjectHasProperty(
arrayHasObjects,
propertyKey,
findPropertyValueOrRegex
) {
if (!aid.isArray(arrayHasObjects) || arrayHasObjects.length <= 0) return null;
if (!aid.isString(propertyKey)) return null;
var obj,
result = null,
i = 0;
for (var max = arrayHasObjects.length; i < max; ++i) {
obj = arrayHasObjects[i];
if (!obj.hasOwnProperty(propertyKey)) continue;
if (aid.isRegExp(findPropertyValueOrRegex)) {
if (findPropertyValueOrRegex.test(obj[propertyKey])) {
result = obj;
break;
}
} else {
if (obj[propertyKey] === findPropertyValueOrRegex) {
result = obj;
break;
}
}
}
if (result) return i;
return -1;
};
/**
* apply overlapped condition sort in array
*
* @static
* @method overlappedConditionSortByProperty
* @param {Array} arrayHasObjects
* @param {Array} sortConditions. condition is object has property, func property
* @returns {Array} return array
* @example
* var arrayHasObjects = [{group: 3, level: 1, date: '2017-02-04T00:00:00.000Z'}, {group: 1, level: 1, date: '2017-01-01T00:00:00.000Z'}, {group: 2, level: 3, date: '2017-01-01T00:00:00.000Z'}, {group: 3, level: 1, date: '2017-01-03T00:00:00.000Z'}, {group: 2, level: 1, date: '2017-01-01T00:00:00.000Z'}, {group: 4, level: 1, date: '2017-01-01T00:00:00.000Z'}, {group: 3, level: 2, date: '2017-01-03T00:00:00.000Z'}, {group: 1, level: 2, date: '2017-01-01T00:00:00.000Z'}, {group: 3, level: 2, date: '2017-02-04T00:00:00.000Z'}, {group: 2, level: 2, date: '2017-01-01T00:00:00.000Z'}];
* var result = array.overlappedConditionSortByProperty(arr, [
* { property: 'group', func: function (a, b) { return a.group - b.group; } },
* { property: 'level', func: function (a, b) { return a.level - b.level; } },
* { property: 'date', func: function (a, b) { return new Date(a.date).getTime() - new Date(b.date).getTime(); } }
* ]);
* console.log(result); // [{group: 1, level: 1, date: '2017-01-01T00:00:00.000Z'}, {group: 1, level: 2, date: '2017-01-01T00:00:00.000Z'}, {group: 2, level: 1, date: '2017-01-01T00:00:00.000Z'}, {group: 2, level: 2, date: '2017-01-01T00:00:00.000Z'}, {group: 2, level: 3, date: '2017-01-01T00:00:00.000Z'}, {group: 3, level: 1, date: '2017-01-03T00:00:00.000Z'}, {group: 3, level: 1, date: '2017-02-04T00:00:00.000Z'}, {group: 3, level: 2, date: '2017-01-03T00:00:00.000Z'}, {group: 3, level: 2, date: '2017-02-04T00:00:00.000Z'}, {group: 4, level: 1, date: '2017-01-01T00:00:00.000Z'}]
*/
array.overlappedConditionSortByProperty = function overlappedConditionSortByProperty(
arrayHasObjects,
sortConditions
) {
if (!aid.isArray(arrayHasObjects)) return null;
var datas = _slice.call(arrayHasObjects);
if (datas.length <= 1) return datas;
if (!aid.isDefined(sortConditions) || !aid.isArray(sortConditions) || sortConditions.length <= 0) return datas;
var restArgs = aid.rest(_slice.call(arguments), 2),
conditionIndex = restArgs.length >= 1 ? restArgs[0] : 0,
condition = sortConditions[conditionIndex];
if (conditionIndex <= 0) datas.sort(condition.func);
if (sortConditions.length <= 1) return datas;
// overlapped conditional sort
if (conditionIndex < sortConditions.length - 1) {
var prevProperty = condition.property,
nextConditionIndex = conditionIndex + 1,
nextCondition = sortConditions[nextConditionIndex];
var memoObj = {},
memoArr = [],
arr;
var obj, prop;
for (var i = 0, max = datas.length; i < max; i++) {
obj = datas[i];
prop = String(obj[prevProperty]);
if (!memoObj[prop]) {
memoObj[prop] = [];
memoArr.push(memoObj[prop]);
}
arr = memoObj[prop];
arr.push(obj);
}
for (var j = 0, len = memoArr.length; j < len; j++) {
arr = memoArr[j];
arr.sort(nextCondition.func);
memoArr[j] = array.overlappedConditionSortByProperty(arr, sortConditions, nextConditionIndex);
}
return memoArr.reduce(function(acc, curVal) {
return acc.concat(curVal);
});
}
return datas;
};
/**
* check object has properties
*
* @static
* @method isEmpty
* @param {Object} obj
* @returns {Boolean} return boolean
* @example
*/
object.isEmpty = function isEmpty(obj) {
if (!aid.isObject(obj)) throw new TypeError('[aid.object.isEmpty] Type of obj parameter must be Object.');
for (var key in obj) {
if (obj.hasOwnProperty(key)) return false;
}
return true;
};
/**
* Object.keys polyfill
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys#Polyfill
*
* @static
* @method keys
* @param {Object} obj
* @returns {Boolean} return boolean
* @example
* var obj = {name: 'foo', job: 'programmer', works: [{id: 1, year: 1999}]};
* console.log( aid.object.keys(obj) ); // ['name', 'job', 'works']
*/
object.keys = function keys(obj) {
var hasDontEnumBug = !{ toString: null }.propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor',
],
dontEnumsLength = dontEnums.length;
if (aid.not(aid.isFunction)(obj) && (aid.not(aid.isObject)(obj) || obj === null)) {
throw new TypeError('[aid.object.keys] Called on non-object.');
}
var result = [];
for (var prop in obj) {
if (_hasOwnProperty.call(obj, prop)) result.push(prop);
}
if (hasDontEnumBug) {
for (var i = 0; i < dontEnumsLength; i++) {
if (_hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
}
}
return result;
};
/**
* Object.shallowEqual
* // https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/using_should_component_update.html
*
* @static
* @method keys
* @param {Object} objA
* @param {Object} objB
* @returns {Boolean} return boolean
* @example
*/
object.shallowEqual = function shallowEqual(objA, objB) {
var not = aid.not,
eq = aid.eq,
isObject = aid.isObject,
isNotObject = not(isObject),
isNull = eq(null);
if (eq(objA)(objB)) return true;
if (isNotObject(objA) || isNull(objA) || isNotObject(objB) || isNull(objB)) return false;
var keysA = object.keys(objA),
keysB = object.keys(objB);
if (not(eq(keysA.length))(keysB.length)) return false;
// Test for A's keys different from B.
var bHasOwnProperty = _hasOwnProperty.bind(objB);
for (var i = 0, max = keysA.length; i < max; i++) {
if (!bHasOwnProperty(keysA[i]) || not(eq(objA[keysA[i]]))(objB[keysA[i]])) return false;
}
return true;
};
/**
* check element is in viewport entirely
*
* @static
* @method isEntirelyInViewport
* @param {Element} ele
* @returns {Boolean} return boolean
* @example
*/
element.isEntirelyInViewport = function isEntirelyInViewport(ele) {
if (typeof jQuery === 'function' && ele instanceof jQuery) ele = ele.get(0);
if (!ele) return false;
var rect = ele.getBoundingClientRect();
// if parent element is invisible, left, top, right, bottom, width, height, x, y properties are zero.
var eqZero = aid.eq(0);
if (
aid.allOf(
eqZero(rect.top),
eqZero(rect.left),
eqZero(rect.bottom),
eqZero(rect.right),
eqZero(rect.width),
eqZero(rect.height),
eqZero(rect.x),
eqZero(rect.y)
)
)
return false;
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (global.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (global.innerWidth || document.documentElement.clientWidth)
);
};
/**
* check element is in viewport partially
*
* @static
* @method isPartiallyInViewport
* @param {Element} ele
* @returns {Boolean} return boolean
* @example
*/
element.isPartiallyInViewport = function isPartiallyInViewport(ele) {
if (typeof jQuery === 'function' && ele instanceof jQuery) ele = ele.get(0);
if (!ele) return false;
var rect = ele.getBoundingClientRect();
// if parent element is invisible, left, top, right, bottom, width, height, x, y properties are zero.
var eqZero = aid.eq(0);
if (
aid.allOf(
eqZero(rect.top),
eqZero(rect.left),
eqZero(rect.bottom),
eqZero(rect.right),
eqZero(rect.width),
eqZero(rect.height),
eqZero(rect.x),
eqZero(rect.y)
)
)
return false;
var windowHeight = global.innerHeight || document.documentElement.clientHeight,
windowWidth = global.innerWidth || document.documentElement.clientWidth;
// http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
var verticallyInView = rect.top <= windowHeight && rect.top + rect.height >= 0,
horizontallyInView = rect.left <= windowWidth && rect.left + rect.width >= 0;
return verticallyInView && horizontallyInView;
};
/**
* create script file, and append to target element
*
* @static
* @method appendScriptFile
* @param {String} fileUrl
* @param {Element} targetElementToAppendFile
* @param {Function} loadCompleteCallback
* @example
* aid.file.appendScriptFile('https://apis.google.com/js/client.js', document.head, function() { console.log('load script file completely'); });
*/
file.appendScriptFile = function appendScriptFile(fileUrl, targetElementToAppend, loadCompleteCallback) {
// document.head, document.body
var script = document.createElement('script');
script.type = 'text/javascript';
if (typeof loadCompleteCallback === 'function') {
if (typeof script.onreadystatechange === 'undefined') {
script.onload = loadCompleteCallback;
} else {
script.onreadystatechange = function() {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
script.onreadystatechange = null;
loadCompleteCallback();
}
};
}
}
script.src = fileUrl;
var ele = targetElementToAppend;
if (typeof jQuery === 'function' && ele instanceof jQuery) ele = ele.get(0);
if (ele) {
ele.appendChild(script);
} else {
var firstScript = document.getElementsByTagName('script')[0];
firstScript.parentNode.insertBefore(script, firstScript);
}
};
/**
* writes text to the system clipboard
*
* @static
* @method copyText
* @param {String} str
* @param {Function} successCallback
* @param {Function} errorCallback
* @example
* aid.clipboard.copyText('aid.js', function(str) { console.log(str); // 'aid.js' }, function(error) { console.log(error); });
*/
clipboard.copyText = function copyText(str, successCallback, errorCallback) {
if (!aid.isString(str)) throw new TypeError('[aid.clipboard.copyText] Type of str parameter must be String.');
if (aid.isDefined(successCallback) && !aid.isFunction(successCallback))
throw new TypeError(
'[aid.clipboard.copyText] Type of successCallback parameter must be undefined or null or Function.'
);
if (aid.isDefined(errorCallback) && !aid.isFunction(errorCallback))
throw new TypeError(
'[aid.clipboard.copyText] Type of errorCallback parameter must be undefined or null or Function.'
);
if (!navigator.clipboard) {
// fallback. no support Clipboard API
var textArea = document.createElement('textarea');
textArea.style.position = 'fixed';
textArea.style.top = '-9999px';
textArea.style.left = '-9999px';
textArea.style.width = '2em'; // setting to 1px / 1em doesn't work as this gives a negative w/h on some browsers.
textArea.style.height = '2em';
textArea.value = str;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
// https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
var isSuccessCopy = document.execCommand('copy');
if (isSuccessCopy) {
if (successCallback) successCallback.call(null, str);
return;
}
if (errorCallback)
errorCallback.call(
null,
new Error('[aid.clipboard.copyText] Cannot copy string with using document.execCommand("copy").')
);
} catch (error) {
if (errorCallback) errorCallback.call(null, error);
}
document.body.removeChild(textArea);
} else {
// https://developer.mozilla.org/en-US/docs/Web/API/Clipboard
navigator.clipboard.writeText(str).then(
function() {
if (successCallback) successCallback.call(null, str);
},
function(error) {
if (errorCallback) errorCallback.call(null, error);
}
);
}
};
/**
* Identity monad
*
* @static
* @class Identity
* @param {Object} value
* @example
* TODO:
*/
var Identity = function(value) {
this._value = value;
};
Identity.prototype.bind = function(func) {
return func(this._value);
};
Identity.prototype.toString = function() {
return 'Identity (' + this._value + ')';
};
monad.Identity = Identity;
/**
* Empty monad
*
* @class Empty
* @example
* TODO:
*/
var Empty = function() {};
Empty.prototype.map = function(/*func*/) {
return this;
};
Empty.prototype.flatmap = function(/*_*/) {
return new Empty();
};
Empty.prototype.toString = function() {
return 'Empty ()';
};
monad.Empty = Empty;
/**
* Wrapper monad
*
* @class Wrapper
* @param {Object} value
* @example
* TODO:
*/
var Wrapper = function(value) {
this._value = value;
};
Wrapper.of = function(value) {
return new Wrapper(value);
};
Wrapper.prototype.map = function(func) {
return Wrapper.of(func(this._value));
};
Wrapper.prototype.join = function() {
if (!(this._value instanceof Wrapper)) return this;
return this._value.join();
};
Wrapper.prototype.get = function() {
return this._value;
};
Wrapper.prototype.toString = function() {
return 'Wrapper (' + this._value + ')';
};
monad.Wrapper = Wrapper;
/**
* Nothing monad
*
* @class Nothing
* @example
* TODO:
*/
var Nothing = function() {};
Nothing.prototype.value = function(/*value*/) {
throw new TypeError('[aid.monad.Nothing] Cannot extract the value.');
};
Nothing.prototype.map = function(/*func*/) {
return this;
};
Nothing.prototype.getOrElse = function(other) {
return other;
};
Nothing.prototype.filter = function(/*func*/) {
return this._value;
};
Nothing.prototype.chain = function(/*func*/) {
return this;
};
Nothing.prototype.toString = function() {
return 'Maybe.Nothing';
};
monad.Nothing = Nothing;
/**
* Just monad
*
* @class Just
* @param {Object} value
* @example
* TODO:
*/
var Just = function(value) {
this._value = value;
};
Just.prototype.value = function(/*value*/) {
throw this._value;
};
Just.prototype.map = function(func) {
return Maybe.fromNullable(func(this._value));
};
Just.prototype.getOrElse = function() {
return this._value;
};
Just.prototype.filter = function(func) {
Maybe.fromNullable(func(this._value) ? this._value : null);
};
Just.prototype.chain = function(func) {
return func(this._value);
};
Just.prototype.toString = function() {
return 'Maybe.Just (' + this._value + ')';
};
monad.Just = Just;
/**
* Maybe monad
*
* @static
* @method Maybe
* @example
* var uri = 'http://www.google.com'; // this uri has no parameters
* var safeUriParams = aid.curry(function(uri) { return aid.monad.Maybe.fromNullable(aid.string.getUriParams(uri)); });
* var safeName = safeUriParams(uri).map(function(params) { return params.name; });
* var name = safeName.getOrElse('no parameter'); // 'no parameter'
*/
var Maybe = function() {};
Maybe.just = function(value) {
return new Just(value);
};
Maybe.nothing = function() {
return new Nothing();
};
Maybe.of = function(value) {
return Maybe.just(value);
};
Maybe.fromNullable = function(value) {
return aid.isDefined(value) ? Maybe.just(value) : Maybe.nothing();
};
monad.Maybe = Maybe;
/**
* Left monad
*
* @class Left
* @param {Object} value
* @example
* TODO:
*/
var Left = function(value) {
this._value = value;
};
Left.prototype.map = function(/*_*/) {
return this;
};
Left.prototype.value = function() {
throw new TypeError('[aid.monad.Left] Cannot extract the value.');
};
Left.prototype.getOrElse = function(other) {
return other;
};
Left.prototype.orElse = function(func) {
return func(this._value);
};
Left.prototype.chain = function(/*func*/) {
return this;
};
Left.prototype.getOrElseThrow = function(errorMessage) {
throw new Error(errorMessage);
};
Left.prototype.filter = function(/*func*/) {
return this;
};
Left.prototype.toString = function() {
return 'Either.Left (' + this._value + ')';
};
monad.Left = Left;
/**
* Right monad
*
* @class Right
* @param {Object} value
* @example
* TODO:
*/
var Right = function(value) {
this._value = value;
};
Right.prototype.map = function(func) {
return Either.of(func(this._value));
};
Right.prototype.getOrElse = function(/*other*/) {
return this._value;
};
Right.prototype.orElse = function() {
return this;
};
Right.prototype.chain = function(func) {
return func(this._value);
};
Right.prototype.getOrElseThrow = function(/*_*/) {
return this._value;
};
Right.prototype.filter = function(func) {
return Either.fromNullable(func(this._value) ? this._value : null);
};
Right.prototype.toString = function() {
return 'Either.Right (' + this._value + ')';
};
monad.Right = Right;
/**
* Either monad
*
* @class Either
* @param {Object} value
* @example
* var decode = function(url) {
* try {
* var result = window.decodeURIComponent(url);
* return Either.of(result);
* } catch (error) {
* return Either.left(error);
* }
* };
* var trim = function(str) { return str.trim(); };
* var split = function(str) { return str.split(''); };
* var debug = aid.tab(function(value) { console.log('[debug] :', value)});
*
* var splitDecodedStr = function(str) {
* var result = Maybe.fromNullable(str)
*
* // change Maybe(Just, Nothing) monad to Either(Left, Right) monad.
* .map(trim).map(debug)
* .chain(decode).map(debug).map(split)
*
* // get success result or else value
* .getOrElse([]); // get successed value or replacement value // useful to get some value.
* // .orElse(func) // if fail, func(value of Either.left) // useful to do something.
*
* return result;
* };
*
* console.log(splitDecodedStrUseMonad(' % '));
* console.log(splitDecodedStrUseMonad(' https%3A%2F%2Fgithub.com%2F '));
*/
var Either = function(value) {
this._value = value;
};
Either.left = function(value) {
return new monad.Left(value);
};
Either.right = function(value) {
return new monad.Right(value);
};
Either.fromNullable = function(value) {
return aid.isDefined(value) ? Either.right(value) : Either.left(value);
};
Either.of = function(value) {
return Either.right(value);
};
Either.prototype.value = function() {
return this._value;
};
monad.Either = Either;
/**
* IO monad
*
* @class IO
* @param {Function} effect
* @example
* var read = function(document, selector) {
* return function() {
* return 'aid.js'; // return document.querySelector(selector).innerHTML;
* };
* };
* var write = function(document, selector) {
* return function(value) {
* console.log('write :', value); // document.querySelector(selector).innerHTML = value;
* return value;
* };
* };
* var uppercase = function(str) { return str.toUpperCase(); };
* var readDOM = aid.partial(read, document),
* writeDOM = aid.partial(write, document);
*
* var readAndWriteUppercase = IO.from(readDOM('#dummy')).map(uppercase).map(writeDOM('#dummy'));
* readAndWriteUppercase.run();
*/
var IO = function(effect) {
if (!aid.isFunction(effect)) throw new TypeError('[aid.monad.IO] Type of effect parameter must be Function.');
this.effect = effect;
};
IO.of = function(value) {
return new IO(function() {
return value;
});
};
IO.from = function(func) {
return new IO(func);
};
IO.prototype.map = function(func) {
var self = this;
return new IO(function() {
return func(self.effect());
});
};
IO.prototype.chain = function(func) {
return func(this.effect());
};
IO.prototype.run = function() {
return this.effect();
};
monad.IO = IO;
/*
* export
*/
aid.operator = operator;
aid.platform = platform;
aid.browser = browser;
aid.string = string;
aid.math = math;
aid.date = date;
aid.array = array;
aid.object = object;
aid.element = element;
aid.file = file;
aid.clipboard = clipboard;
aid.monad = monad;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = aid;
}
} else if (typeof define === 'function' && define.amd) {
define('aid', [], function() {
return aid;
});
} else {
global.aid = aid;
}
})();