From cc7218273763cd0214f6d1505d649314ba430100 Mon Sep 17 00:00:00 2001 From: MaDnh Date: Tue, 18 Apr 2017 09:02:45 +0700 Subject: [PATCH] First commit --- .gitignore | 4 + README.md | 11 + bower.json | 24 + dist/khoai.js | 1205 +++++++++++++++++++++++++++++++++++++++++ dist/khoai.js.map | 1 + dist/khoai.min.js | 2 + dist/khoai.min.js.map | 1 + docs/.gitkeep | 0 gulpfile.js | 43 ++ package.json | 34 ++ src/khoai.js | 71 +++ src/util.js | 1133 ++++++++++++++++++++++++++++++++++++++ test/index.html | 27 + test/test.js | 598 ++++++++++++++++++++ 14 files changed, 3154 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 bower.json create mode 100644 dist/khoai.js create mode 100644 dist/khoai.js.map create mode 100644 dist/khoai.min.js create mode 100644 dist/khoai.min.js.map create mode 100644 docs/.gitkeep create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 src/khoai.js create mode 100644 src/util.js create mode 100644 test/index.html create mode 100644 test/test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6169a3f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +bower_components/ +node_modules/ +/yarn.lock diff --git a/README.md b/README.md new file mode 100644 index 0000000..4474a7c --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +![http://i.imgur.com/RmD50NR.jpg](http://i.imgur.com/RmD50NR.jpg) + +# KhoaiJS + +## Install + +**Bower** + +```bash +bower install --save khoaijs +``` diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..c03377f --- /dev/null +++ b/bower.json @@ -0,0 +1,24 @@ +{ + "name": "khoaijs", + "description": "", + "main": "khoaijs.js", + "authors": [ + "MaDnh " + ], + "license": "MIT", + "homepage": "https://github.com/madnh", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test" + ], + "devDependencies": { + "mocha": "^3.0.2", + "chai": "^3.5.0", + "expect.js": "^0.3.1" + }, + "dependencies": { + "lodash": "^4.17.4" + } +} diff --git a/dist/khoai.js b/dist/khoai.js new file mode 100644 index 0000000..79b3316 --- /dev/null +++ b/dist/khoai.js @@ -0,0 +1,1205 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define([], function(){ + return (root.Khoai = factory()); + }); + } else { + // Browser globals + root.Khoai = factory(); + } +}(this, function () { + "use strict"; + + var Khoai = {}; + var proxyCounter = 0; + + /** + * Khoai's version + * @constant {string} VERSION + * @default + */ + var version = '0.0.1'; + + + Object.defineProperty(Khoai, 'VERSION', { + value: version + }); + + var BaseClass = function () { + this.proxiedMethods = {} + }; + + + /** + * + * @return {number} + */ + Khoai.getProxyCounter = function () { + return proxyCounter; + }; + + BaseClass.prototype.dispose = function () { + for (var key in this.proxiedMethods) { + if (this.proxiedMethods.hasOwnProperty(key)) { + this.proxiedMethods[key] = null + } + } + + this.proxiedMethods = {} + }; + + /* + * Creates a proxied method reference or returns an existing proxied method. + */ + BaseClass.prototype.proxy = function (method) { + if (method.khoaiProxyId === undefined) { + proxyCounter++; + method.khoaiProxyId = proxyCounter + } + + if (this.proxiedMethods[method.khoaiProxyId] !== undefined) + return this.proxiedMethods[method.khoaiProxyId]; + + this.proxiedMethods[method.khoaiProxyId] = method.bind(this); + + return this.proxiedMethods[method.khoaiProxyId] + }; + + Khoai.baseClass = BaseClass; + + return Khoai; +})); +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define(['lodash', 'khoai'], function (_, Khoai) { + factory(_, Khoai); + }); + } else { + factory(root._, root.Khoai) + } +}(this, function (_, Khoai) { + /* + |-------------------------------------------------------------------------- + | Type Definitions + |-------------------------------------------------------------------------- + */ + + /** + * Loop callback. Useful in _.each, _.map, _.omit,... + * @callback loopCallback + * @param value Item value + * @param key Item name/index + * @param object object of items + * + * @see http://underscorejs.org/#each + * @see http://underscorejs.org/#map + * @see http://underscorejs.org/#omit + * + * @example Alerts each number value in turn... + * _.each([1, 2, 3], alert); + * _.each({one: 1, two: 2, three: 3}, alert); + * @example Log to console each number value in turn... + * var logger = function(item, key, object){ + * console.log(key, '=>', item, '<------ Object', object); + * }; + * _.each([1, 2, 3], logger); + * _.each({one: 1, two: 2, three: 3}, logger); + * + */ + + /* + |-------------------------------------------------------------------------- + | Core + |-------------------------------------------------------------------------- + */ + + + var Util = {}; + var slice = Array.prototype.slice; + + /** + * Slice arguments of a function as array + * @param args + * @param {Number} [start] + * @param {Number} [end] + * @return {*} + */ + Util.sliceArguments = function (args, start, end) { + return slice.apply(args, slice.call(arguments, 1)); + }; + + Util.beNumber = function (value, default_value) { + value = parseFloat(value); + + if (Util.isNumeric(value)) { + return value; + } + if (_.isUndefined(default_value)) { + return 0; + } + + return Util.isNumeric(default_value) ? parseFloat(default_value) : Util.beNumber(default_value, 0); + }; + + /** + * Make sure first argument is object or arguments are name and value of object + * @param {*} [name] + * @param {*} [value] + * @returns {*} + * @example + * Khoai.util.beObject(); //{} + * Khoai.util.beObject(['foo', 'bar', 123]); //{0: "a", 1: 'bar', 2: 123} + * Khoai.util.beObject('yahoo'); //{0: "yahoo"} + * Khoai.util.beObject(235); //{0: 235} + * Khoai.util.beObject('yahoo', 123); //{yahoo: 123} + * Khoai.util.beObject({yahoo: 123, goooo:'ASDWd'}); //{yahoo: 123, goooo:'ASDWd'} + * + */ + Util.beObject = function (name, value) { + switch (true) { + case arguments.length == 1: + if (_.isObject(name)) { + return name; + } else if (_.isArray(name) || _.isArguments(name)) { + return _.zipObject(Object.keys(name), name); + } + + return {0: name}; + break; + + case arguments.length >= 2: + if (_.isObject(name)) { + return name; + } + var obj = {}; + + obj[name] = value; + + return obj; + } + + return {}; + }; + + var cast_types = {}; + + cast_types['string'] = function (value) { + return value + ''; + }; + cast_types['boolean'] = function (value) { + return Boolean(value); + }; + + cast_types['number'] = function (value) { + return Util.beNumber(value); + }; + cast_types['integer'] = function (value) { + return Math.floor(Util.beNumber(value)); + }; + cast_types['array'] = _.castArray; + cast_types['object'] = function (value) { + return Util.beObject(value); + }; + + /** + * Convert array|object item to other type. Support types: + * - string + * - boolean + * - number + * - integer + * - array + * - object + * + * @param object + * @param type + * @return {Array|object} + * @throws Error when cast type is unsupported + */ + Util.castItemsType = function (object, type) { + if (!cast_types.hasOwnProperty(type)) { + throw new Error('Invalid cast type. Available types are: string, number, integer, array and object'); + } + if (_.isArray(object)) { + return _.map(_.clone(object), cast_types[type]); + } + + return _.mapObject(_.clone(object), cast_types[type]); + }; + + /** + * Loop over array or object like _.each but breakable + * @param {object|Array} obj Loop object + * @param {loopCallback} callback callback apply on every item, return break value to break the loop + * @param {string} [break_on=break] Value of callback result that break the loop, default is 'break' + * @returns {*} + * @example + * Khoai.util.loop([1,2,3,4,5], function(item){ + * console.log('Number', item); + * if(item > 3){ + * return 'break'; + * } + * }); + * //Console will log: + * // Number 1 + * // Number 2 + * // Number 3 + * @example + * Khoai.util.loop([1,2,3,4,5], function(item){ + * console.log('Number', item); + * if(item > 3){ + * return 'yahoo'; + * } + * }, 'yahoo'); + * //Console will log: + * // Number 1 + * // Number 2 + * // Number 3 + * + */ + Util.loop = function (obj, callback, break_on) { + var i, length; + if (_.isUndefined(break_on)) { + break_on = 'break'; + } + if (_.isArray(obj)) { + for (i = 0, length = obj.length; i < length; i++) { + if (callback(obj[i], i, obj) === break_on) { + break; + } + } + } else { + var keys = _.keys(obj); + for (i = 0, length = keys.length; i < length; i++) { + if (callback(obj[keys[i]], keys[i], obj) === break_on) { + break; + } + } + } + return obj; + }; + + var unique_id_current_status = {}; + + /** + * Return Next ID of type, start from 1 + * @param {string} [type="unique_id"] Type of ID + * @param {boolean} [type_as_prefix = true] Use type as prefix of return ID + * @returns {string|number} + * @example Default type + * Khoai.util.nextID(); //unique_id_1 + * Khoai.util.nextID(); //unique_id_2 + * Khoai.util.nextID(null, false); //3 + * Khoai.util.nextID('superman'); //superman_1 + * Khoai.util.nextID('superman'); //superman_2 + * Khoai.util.nextID(); //unique_id_4 + * Khoai.util.nextID('superman', false); //3 + * + */ + Util.nextID = function (type, type_as_prefix) { + if (_.isEmpty(type)) { + type = 'unique_id'; + } + if (!unique_id_current_status.hasOwnProperty(type)) { + unique_id_current_status[type] = 1; + } else { + unique_id_current_status[type]++; + } + + return get_unique_id_with_prefix(type, unique_id_current_status[type], type_as_prefix); + }; + + /** + * Return current ID of type + * @param {string} [type="unique_id"] Type of ID + * @param {boolean} [type_as_prefix = true] Use type as prefix of return ID + * @returns {boolean|string|number} + * @example + * Khoai.util.currentID(); //false + * Khoai.util.nextID(); //unique_id_0 + * Khoai.util.nextID(); //unique_id_1 + * Khoai.util.currentID(); //unique_id_1 + * Khoai.util.currentID(null, false); //1 + * Khoai.util.currentID('superman'); //false + * Khoai.util.nextID('superman'); //superman_0 + * Khoai.util.nextID('superman'); //superman_1 + * Khoai.util.currentID('superman'); //superman_1 + * Khoai.util.currentID('superman', false); //1 + * Khoai.util.nextID(); //2 + * Khoai.util.currentID(); //unique_id_2 + */ + Util.currentID = function (type, type_as_prefix) { + if (_.isEmpty(type)) { + type = 'unique_id'; + } + + if (!unique_id_current_status.hasOwnProperty(type)) { + return false; + } + + return get_unique_id_with_prefix(type, unique_id_current_status[type], type_as_prefix); + }; + + /** + * + * @param {string} [type] a type, do not require existed + * @param {number} [value] + * @returns {number|*} + */ + Util.resetID = function (type, value) { + if (!arguments.length) { + type = 'unique_id'; + } + + type = String(type); + + if (_.isUndefined(value)) { + delete unique_id_current_status[type]; + } else { + value = arguments.length > 1 ? Util.beNumber(value) : 0; + value = Math.max(value, 0); + unique_id_current_status[type] = value; + } + + return value; + }; + + function get_unique_id_with_prefix(type, id, type_as_prefix) { + if (type_as_prefix || _.isUndefined(type_as_prefix)) { + return type + '_' + id; + } + + return id; + } + + + /** + * Get constructor name of object + * @param obj + * @param {boolean} [constructor_only = false] Return object's constructor name only + * @returns {string} + * @example + * Khoai.util.className(Khoai.App); //"[object Object]" + * Khoai.util.className(Khoai.App, true); //App + * Khoai.util.className(new Khoai.EventEmitter(), true); //EventEmitter + */ + Util.className = function (obj, constructor_only) { + if (constructor_only) { + return obj.constructor.name; + } + return Object.prototype.toString.call(obj); + }; + + /** + * Get type of content. If is object then return constructor name + * @param content + * @returns {string} + * @example + * Khoai.util.contentType(123); //number + * Khoai.util.contentType('123'); //string + * Khoai.util.contentType('Yahooooooo'); //string + * Khoai.util.contentType(true); //boolean + * Khoai.util.contentType(true); //boolean + * Khoai.util.contentType([1,2]); //Array + * Khoai.util.contentType({}); //Object + * Khoai.util.contentType(_.App); //App + */ + Util.contentType = function (content) { + var type = typeof content; + + if (type === 'object') { + var class_name = Util.className(content, true); + + if (class_name) { + return class_name; + } + } + + return type; + }; + + /** + * Check object is an instance of a constructor type + * @param {*} obj + * @param {string} class_name Name of class + * @returns {boolean} + * @example + * Khoai.util.isInstanceOf(Khoai.App, 'App');//true + * Khoai.util.isInstanceOf(123, 'Object'); //false + * Khoai.util.isInstanceOf(123, 'Number'); //true + * Khoai.util.isInstanceOf('123', 'String'); //true + * Khoai.util.isInstanceOf(new Khoai.util.EventEmitter(), 'EventEmitter'); //true + */ + Util.isInstanceOf = function (obj, class_name) { + return Util.className(obj, true) === class_name; + }; + + /** + * Check if object is primitive type: null, string, number, boolean + * @param value + * @returns {boolean} + * @example + * Khoai.util.isPrimitiveType(123); //true + * Khoai.util.isPrimitiveType('123'); //true + * Khoai.util.isPrimitiveType(null); //true + * Khoai.util.isPrimitiveType(); //true + * Khoai.util.isPrimitiveType(_.App); //false + */ + Util.isPrimitiveType = function (value) { + if (_.isObject(value)) { + return false; + } + + var type = typeof value; + return value == null || type === 'string' || type === 'number' || type === 'boolean'; + }; + + Util.mergeObject = function () { + var next_index = 0; + + for (var i = 0, length = arguments.length; i < length; i++) { + if (_.isArray(arguments[i]) || !_.isObject(arguments[i])) { + arguments[i] = _.castArray(arguments[i]); + arguments[i] = _.zipObject(_.range(next_index, next_index += arguments[i].length), arguments[i]); + } + } + + return _.extend.apply(_, arguments); + }; + + function is_diff_strict_cb(value_1, value_2) { + return value_1 !== value_2; + } + + function is_diff_loose_cb(value_1, value_2) { + return value_1 != value_2; + } + + /** + * Get dirty of object with others object + * @param {function} cb Callback return true if 2 item is difference + * @param object + * @param [others...] + * @return {{}} + */ + function diff_object(cb, object, others) { + if (arguments.length < 2) { + return {}; + } + + var result = {}; + + if (!_.isFunction(cb)) { + cb = cb ? is_diff_strict_cb : is_diff_loose_cb; + } + + others = Util.mergeObject.apply(Util, slice.call(arguments, 2)); + + _.each(object, function (value, key) { + if (!others.hasOwnProperty(key)) { + result[key] = value; + } else { + if (cb(value, others[key])) { + result[key] = value; + } + } + }); + + return result; + } + + /** + * Get dirty of object with others object. Strict comparison + * @param {object} object + * @param {object} others + * @return {*} + * @example + * Khoai.util.diffObject({a: 0, b: 1}, {a: '0', b: 1}); //{a: 0} + */ + Util.diffObject = function (object, others) { + var args = _.flatten(_.toArray(arguments)); + + args.unshift(is_diff_strict_cb); + + return diff_object.apply(null, args); + }; + + /** + * Get dirty of object with others object. Loose comparison + * @param {object} object + * @param {object} others + * @return {*} + * @example + * Khoai.util.diffObjectLoose({a: 0, b: 1}, {a: '0', b: 2}); //{b: 1} + */ + Util.diffObjectLoose = function (object, others) { + var args = _.flatten(_.toArray(arguments)); + + args.unshift(is_diff_loose_cb); + + return diff_object.apply(null, args); + }; + + /** + * Get dirty of object with others object, use callback + * @param {function} callback Callback with 2 parameters: base value, other object value. Return true when difference + * @param {object} object + * @param {object} others + * @return {*} + */ + Util.diffObjectWith = function (callback, object, others) { + return diff_object.apply(null, slice.apply(arguments)) + }; + + + /** + * Get random string + * @param {number} [length] + * @param {string} [chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'] + * @returns {string} + * @example + * Khoai.util.randomString(10); //'mYJeC1xBcl' + * Khoai.util.randomString(10, 'ABCDEF'); //'CDABBEFADE' + */ + Util.randomString = function (length, chars) { + var result = '', chars_length, i; + if (_.isUndefined(chars) || !chars.toString().length) { + chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + } + chars_length = chars.length; + + length = Util.beNumber(length, 10); + + for (i = length; i > 0; --i) { + result += chars[Math.round(Math.random() * (chars_length - 1))]; + } + return result; + }; + + + /** + * Setup a object with field name and value or object of fields + * @param {Object} object host object + * @param {(string|Object)} option field name of object of fields + * @param {*} value value of field when option param is field name + * @returns {{}} + * @example + * var obj = {a: 'A', b: 'B'} + * Khoai.util.setup(obj, 'a', '123'); //obj -> {a: '123', b: 'B'} + * Khoai.util.setup(obj, {b: 'Yahoo', c: 'ASD'}); //obj -> {a: 123, b: 'Yahoo', c: 'ASD'} + */ + Util.setup = function (object, option, value) { + if (!_.isObject(object)) { + object = {}; + } + if (_.isObject(option)) { + _.each(option, function (val, path) { + _.set(object, path, val); + }); + } else { + _.set(object, option, value); + } + + return object; + }; + + /** + * Get all of valid keys that exists in object. + * + * @param {object} object + * @param {Array} keys + * @return {Array} + */ + Util.validKeys = function (object, keys) { + var result = []; + + keys = _.castArray(keys); + for (var i = 0, length = keys.length; i < length; i++) { + if (object.hasOwnProperty(keys[i])) { + result.push(keys[i]); + } + } + + return result; + }; + + + /** + * Like _.pairs but array item is an object with field is "key", "value" + * @param {{}} object + * @param {string} [key = 'key'] + * @param {string} [value = 'value'] + * @returns {Array} + * @example + * Khoai.util.pairsAsObject({one: 1, two: 2, three: 3}); + * => [{key: 'one', value: 1},{key: 'two', value: 2},{key: 'three', value: 3}] + */ + Util.pairsAsObject = function (object, key, value) { + var result = [], + field_key = key || 'key', + field_value = value || 'value'; + + _.each(object, function (value, key) { + var item = {}; + + item[field_key] = key; + item[field_value] = value; + + result.push(item); + }); + + return result; + }; + + /** + * A convenient version of what is perhaps the most common use-case for map: extracting a list of property values, with a column as key. + * @param {Array} collection + * @param {string} key_field If key field not found then use as "undefined" + * @param {string} value_field If value field not found then use as "undefined" + * @returns {{}} + * @example + * var stooges = [{name: 'moe', id: 1, age: 40}, {name: 'larry', id: 2, age: 50}, {name: 'curly', id: 4, age: 60}]; + * Khoai.util.pluckBy(stooges, 'id', 'name'); + * => {1: 'moe', 2: 'larry', 3: 'curly'} + */ + Util.pluckBy = function (collection, key_field, value_field) { + var result = {}; + + _.each(collection, function (object) { + if (object.hasOwnProperty(key_field)) { + result[object[key_field]] = object.hasOwnProperty(value_field) ? object[value_field] : undefined; + } + }); + + return result; + }; + + /** + * Check value is numeric + * @param value + * @returns {boolean} + * + * @example + * Khoai.util.isNumeric(123); //true + * Khoai.util.isNumeric(123.5); //true + * Khoai.util.isNumeric('123.5'); //true + */ + Util.isNumeric = function (value) { + return !_.isArray(value) && (value - parseFloat(value) + 1) >= 0; + }; + + /** + * Make sure value is in array + * @param {*} value + * @param {Array} values + * @param {*} [default_value] Default value if not found value in values + * @returns {*} If found then return value itself, else, return default_value or first item of array + * @example + * var items = [1,2,3,'a']; + * Khoai.util.oneOf(1, items); // 1 + * Khoai.util.oneOf(0, items); // 1 + * Khoai.util.oneOf('a', items); // 'a' + * Khoai.util.oneOf('b', items, 'C'); // 'C' + */ + Util.oneOf = function (value, values, default_value) { + if (-1 !== values.indexOf(value)) { + return value; + } + + if (arguments.length >= 3) { + return default_value; + } + + return _.first(values); + }; + + /** + * Escape URL + * @param {string} url + * @param {boolean} [param = false] Include param? + * @returns {string} + */ + Util.escapeURL = function (url, param) { + return param ? encodeURIComponent(url) : encodeURI(url); + }; + + /** + * Unescape URL + * @param {string} url + * @param {boolean} [param = false] Include param? + * @returns {string} + */ + Util.unescapeURL = function (url, param) { + return param ? decodeURI(url) : decodeURIComponent(url); + }; + + + /** + * Split array to n of chunks + * @param {Array} array + * @param {number} chunks Number of chunks + * @return {Array} + * @example + * var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + * Khoai.util.chunks(arr, 3) + * => [ + * [0, 1, 2, 3], + * [4, 5, 6, 7], + * [8, 9] + * ] + */ + Util.chunks = function (array, chunks) { + return _.chunk(array, Math.ceil(array.length / chunks)); + }; + + /** + * Toggle array's elements + * @param {Array} array + * @param {Array} elements + * @param {boolean} status If this param is boolean then add/remove base on it value. By default it is undefined - + * add if none exists, remove if existed + * @returns {Array} + * @example + * var arr = ['A', 'B', 'C', 'D']; + * Khoai.util.toggle(arr, ['A', 'V']) => ['B', 'C', 'D', 'V'] + * Khoai.util.toggle(arr, ['A', 'V'], true) => ['A', 'B', 'C', 'D', 'V'] + * Khoai.util.toggle(arr, ['A', 'V'], false) => ['B', 'C', 'D'] + */ + Util.toggle = function (array, elements, status) { + elements = _.uniq(_.castArray(elements)); + if (_.isUndefined(status)) { + var exclude = _.intersection(array, elements); + var include = _.difference(elements, array); + + array = _.union(_.difference(array, exclude), include); + } else { + if (status) { + array = _.union(array, elements); + } else { + array = _.difference(array, elements); + } + } + return array; + }; + + /** + * Create, define and return a object with properties. Object's properties are fixed and enumerable + * @param {{}} properties + * @returns {{}} + * @example + * var obj = Khoai.util.defineObject({name: 'Manh', old: 123, hello: function(){ + * alert('Hello ' + this.name); + * }}); + * + * obj.old = 10; + * console.log(obj); //{name: 'Manh', old: 123} + * _.each(obj, function(value, key){ + * console.log(key, '=>', value); + * }); + * //name => Manh + * //old => 123 + */ + Util.defineObject = function (properties) { + var obj = {}; + _.each(properties, function (value, key) { + key = key.trim(); + if (!_.has(obj, key)) { + Object.defineProperty(obj, key, { + enumerable: !_.isFunction(value), + value: value + }); + } + }); + return obj; + }; + + /** + * Define a MaDnh constant + * @param {object} target + * @param {(string|Object)} name + * @param {*} [value = undefined] + * @example + * Khoai.util.defineConstant(obj, 'TEST', 123) => obj.TEST = 123 + */ + Util.defineConstant = function (target, name, value) { + var obj = {}; + + if (_.isObject(name)) { + obj = name; + value = undefined; + } else { + obj[name] = value; + } + _.each(obj, function (val, key) { + key = key.trim().toUpperCase(); + + if (!target.hasOwnProperty(key)) { + Object.defineProperty(target, key, { + enumerable: true, + value: val + }); + } + }); + }; + + /** + * Inherit constructor prototype + * @param {function} subclass_constructor Destination constructor + * @param {function} base_class_constructor Source constructor + * @param {boolean} [addSuper = true] Add property to destination prototype that reference back to source prototype + * + * @see https://github.com/Olical/Heir + * + * @example + * function MyEE(){ + * M.EventEmitter.call(this); + * } + * + * M.inherit(MyEE, M.EventEmitter); + */ + Util.inherit = function (subclass_constructor, base_class_constructor, addSuper) { + var proto = subclass_constructor.prototype = Object.create(base_class_constructor.prototype); + proto.constructor = subclass_constructor; + + if (addSuper || _.isUndefined(addSuper)) { + proto._super = base_class_constructor.prototype; + } + }; + + /** + * Call callback with arguments + * @param {string|function|Array} callback + * @param {*} [args] Callback arguments, if only one argument as array passed then it must be wrapped by array, eg: + * [users] + * @param {Object|null} context context of "this" keyword + * @returns {*} + * + * @example + * Khoai.util.callFunc(null, alert, 123); + * Khoai.util.callFunc(null, function(name, old){ + * alert('My name is ' + name + ', ' + old + 'years old'); + * }, ['Manh', 10]); + * + * var obj = {name: 'Manh', old: 10}; + * Khoai.util.callFunc(obj, function(say_hi){ + * alert((say_hi ? 'Hi' : 'Hello') + '! my name is ' + this.name + ', ' + this.old + ' years old'); + * }, true); + */ + Util.callFunc = function (callback, args, context) { + if (arguments.length >= 2) { + args = _.castArray(args); + } else { + args = []; + } + + if (callback) { + if (_.isFunction(callback)) { + return callback.apply(context || null, args); + } else if (_.isString(callback)) { + if (window.hasOwnProperty(callback) && _.isFunction(window[callback])) { + return window[callback].apply(context || null, args); + } + + throw new Error('Invalid callback!'); + } else if (_.isArray(callback)) { + var result = [], + this_func = arguments.callee; + + _.each(callback, function (tmpFunc) { + result.push(this_func(tmpFunc, args, context)); + }); + + return result; + } + } + + return undefined; + }; + + /** + * Call callback asynchronous. Similar to Khoai.util.callFunc + * + * @param {(string|function|Array)} callback + * @param {*} [args] Callback arguments, if only one argument as array passed then it must be wrapped by array, eg: + * [users] + * @param {number} [delay=1] Delay milliseconds + * @param {Object|null} context context of "this" keyword + * @see callFunc + */ + Util.async = function (callback, args, delay, context) { + delay = parseInt(delay); + if (_.isNaN(delay)) { + delay = 1; + } + + return setTimeout(function () { + Util.callFunc(callback, args, context || null); + }, Math.max(1, delay)); + }; + + function createConsoleCB(name, description) { + return function () { + console[name].apply(console, (description ? [description] : []).concat(slice.apply(arguments))); + } + } + + /** + * Like console.log with dynamic arguments + * @example + * var args = [1,2,3,4]; + * Khoai.util.logArgs('a',123); + * Khoai.util.logArgs.apply(null, args); + */ + Util.logArgs = function () { + console.log.apply(console, slice.apply(arguments)); + }; + + /** + * Create console log callback with description as first arguments + * @param {string} description + * @returns {*} + * @example + * var cb = Khoai.util.logCb('Test 1'); + * cb(1,2,3); // Console log: 'Test 1' 1 2 3 + */ + Util.logCb = function (description) { + return createConsoleCB.apply(null, ['log'].concat(slice.apply(arguments))); + }; + + /** + * Like console.warn with dynamic arguments + * @example + * var args = [1,2,3,4]; + * Khoai.util.warnArgs('a',123); + * Khoai.util.warnArgs.apply(null, args); + */ + Util.warnArgs = function () { + console.warn.apply(console, slice.apply(arguments)); + }; + + /** + * Create console waring callback with description as first arguments + * @param {string} description + * @returns {*} + * @example + * var cb = Khoai.util.warnCb('Test 1'); + * cb(1,2,3); // Console warn as: 'Test 1' 1 2 3 + */ + Util.warnCb = function (description) { + return createConsoleCB.apply(null, ['warn'].concat(slice.apply(arguments))); + }; + + /** + * Like console.error with dynamic arguments + * @example + * var args = [1,2,3,4]; + * Khoai.util.errorArgs('a',123); + * Khoai.util.errorArgs.apply(null, args); + */ + Util.errorArgs = function () { + console.error.apply(console, slice.apply(arguments)); + }; + + /** + * Create console error callback with description as first arguments + * @param {string} description + * @returns {*} + * @example + * var cb = Khoai.util.errorCb('Test 1'); + * cb(1,2,3); // Console error as: 'Test 1' 1 2 3 + */ + Util.errorCb = function (description) { + return createConsoleCB.apply(null, ['error'].concat(slice.apply(arguments))); + }; + + + var debug_types_status = {}, + all_debugging = false; + + /** + * + * @param type + * @returns {boolean} + */ + Util.isDebugging = function (type) { + if (all_debugging || _.isEmpty(type)) { + return all_debugging; + } + + return debug_types_status.hasOwnProperty(type) && debug_types_status[type]; + + }; + /** + * + * @param [type] default is all debug type + */ + Util.debugging = function (type) { + if (_.isEmpty(type)) { + all_debugging = true; + return; + } + + debug_types_status[type] = true; + }; + /** + * + * @param [type] default is all debug type + */ + Util.debugComplete = function (type) { + if (_.isEmpty(type)) { + all_debugging = false; + debug_types_status = {}; + + return; + } + + delete debug_types_status[type]; + }; + + /** + * Run callback if is in debugging of a type + * @param type null - all debug type + * @param {function} callback + */ + Util.onDebugging = function (type, callback) { + if (Util.isDebugging(type)) { + Util.callFunc(callback); + } + }; + + /** + * Get json string of an array + * @param {array} details + * @param {string} [glue="\n"] + * @returns {string} + */ + Util.getDebugString = function (details, glue) { + var result = []; + + _.each(_.castArray(details), function (item) { + result.push(JSON.stringify(item)); + }); + + return result.join(glue || "\n"); + }; + + /** + * + * @param args + * @param order + * @param rules + * @returns {{}} + * @example + * var order = ['int', 'bool', 'str'], + * rules = {int: 'number', bool: 'boolean', str: 'string'}; + * + * Khoai.util.optionalArgs([1, true, 'A'], order, rules); //{int: 1, bool: true, str: "A"} + * Khoai.util.optionalArgs([true, 'A'], order, rules);//{bool: true, str: "A"} + * Khoai.util.optionalArgs(['A'], order, rules); //{str: "A"} + * Khoai.util.optionalArgs(['A', 'V'], order, rules); //{int: "A", bool: "V"} + * Khoai.util.optionalArgs([1, []], order, rules); //{int: 1, bool: []} + * Khoai.util.optionalArgs([true, []], order, rules); //{int: true, bool: []} + * Khoai.util.optionalArgs(['A', []], order, rules); //{int: "A", bool: []} + * Khoai.util.optionalArgs([[], []], order, rules); //{int: Array[0], bool: []} + * + * Khoai.util.optionalArgs(['A', 'V'], ['int', 'bool', 'str', 'str2'], {int: 'number', bool: 'boolean', str: 'string', str2: 'string'}); + * //=> {str: "A", str2: "V"} + */ + Util.optionalArgs = function (args, order, rules) { + var result = {}, + arg, index = 0, last_index, missing_rules, type, args_cloned, args_with_type, found; + + missing_rules = _.difference(order, Object.keys(rules)); + missing_rules.forEach(function (missing) { + rules[missing] = true; + }); + + args_with_type = order.map(function (arg_name) { + return rules[arg_name]; + }); + + if (_.isEmpty(args)) { + return result; + } + if (args.length >= order.length) { + result = _.zipObject(order, args.slice(0, order.length)); + } else { + args_cloned = args.slice(0); + + while (arg = args_cloned.shift()) { + type = Util.contentType(arg); + found = false; + last_index = index; + + Util.loop(args_with_type.slice(index), (function (tmp_arg, tmp_type) { + return function (types) { + if (types === true || tmp_type === types + || (_.isArray(types) && -1 != types.indexOf(tmp_type)) + || (_.isFunction(types) && types(tmp_arg))) { + found = true; + + return 'break'; + } + + index++; + } + })(arg, type)); + + if (!found) { + result = _.zipObject(order.slice(0, args.length), args); + break; + } + + result[order[index++]] = arg; + } + } + + return result; + }; + + /** + * Sort number asc + */ + function sortNumberCallback(a, b) { + return a - b; + } + + /** + * Sort number desc + */ + function sortNumberDescCallback(a, b) { + return b - a; + } + + function is_equal_strict(a, b) { + return a === b; + } + + function is_equal_loose(a, b) { + return a == b; + } + + + Util.defineConstant(Util, { + /** + * Array sort compare function. Sort number + * @constant + * @example + * var scores = [1, 10, 2, 21]; + * scores.sort(); // [1, 10, 2, 21] + * scores.sort(Khoai.util.SORT_NUMBER); // [1, 2, 10, 21] + */ + SORT_NUMBER: sortNumberCallback, + /** + * Array sort compare function. Sort number desc + * @constant + * @example + * var scores = [1, 10, 2, 21]; + * scores.sort(Khoai.util.SORT_NUMBER_DESC); // [21, 10, 2, 1] + */ + SORT_NUMBER_DESC: sortNumberDescCallback + }); + + + Khoai.util = Util; +})); +//# sourceMappingURL=khoai.js.map diff --git a/dist/khoai.js.map b/dist/khoai.js.map new file mode 100644 index 0000000..c452587 --- /dev/null +++ b/dist/khoai.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["khoai.js","util.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACtEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"khoai.js","sourcesContent":["(function (root, factory) {\r\n if (typeof define === 'function' && define.amd) {\r\n define([], function(){\r\n return (root.Khoai = factory());\r\n });\r\n } else {\r\n // Browser globals\r\n root.Khoai = factory();\r\n }\r\n}(this, function () {\r\n \"use strict\";\r\n\r\n var Khoai = {};\r\n var proxyCounter = 0;\r\n\r\n /**\r\n * Khoai's version\r\n * @constant {string} VERSION\r\n * @default\r\n */\r\n var version = '0.0.1';\r\n\r\n\r\n Object.defineProperty(Khoai, 'VERSION', {\r\n value: version\r\n });\r\n\r\n var BaseClass = function () {\r\n this.proxiedMethods = {}\r\n };\r\n\r\n\r\n /**\r\n *\r\n * @return {number}\r\n */\r\n Khoai.getProxyCounter = function () {\r\n return proxyCounter;\r\n };\r\n\r\n BaseClass.prototype.dispose = function () {\r\n for (var key in this.proxiedMethods) {\r\n if (this.proxiedMethods.hasOwnProperty(key)) {\r\n this.proxiedMethods[key] = null\r\n }\r\n }\r\n\r\n this.proxiedMethods = {}\r\n };\r\n\r\n /*\r\n * Creates a proxied method reference or returns an existing proxied method.\r\n */\r\n BaseClass.prototype.proxy = function (method) {\r\n if (method.khoaiProxyId === undefined) {\r\n proxyCounter++;\r\n method.khoaiProxyId = proxyCounter\r\n }\r\n\r\n if (this.proxiedMethods[method.khoaiProxyId] !== undefined)\r\n return this.proxiedMethods[method.khoaiProxyId];\r\n\r\n this.proxiedMethods[method.khoaiProxyId] = method.bind(this);\r\n\r\n return this.proxiedMethods[method.khoaiProxyId]\r\n };\r\n\r\n Khoai.baseClass = BaseClass;\r\n\r\n return Khoai;\r\n}));","(function (root, factory) {\r\n if (typeof define === 'function' && define.amd) {\r\n define(['lodash', 'khoai'], function (_, Khoai) {\r\n factory(_, Khoai);\r\n });\r\n } else {\r\n factory(root._, root.Khoai)\r\n }\r\n}(this, function (_, Khoai) {\r\n /*\r\n |--------------------------------------------------------------------------\r\n | Type Definitions\r\n |--------------------------------------------------------------------------\r\n */\r\n\r\n /**\r\n * Loop callback. Useful in _.each, _.map, _.omit,...\r\n * @callback loopCallback\r\n * @param value Item value\r\n * @param key Item name/index\r\n * @param object object of items\r\n *\r\n * @see http://underscorejs.org/#each\r\n * @see http://underscorejs.org/#map\r\n * @see http://underscorejs.org/#omit\r\n *\r\n * @example Alerts each number value in turn...\r\n * _.each([1, 2, 3], alert);\r\n * _.each({one: 1, two: 2, three: 3}, alert);\r\n * @example Log to console each number value in turn...\r\n * var logger = function(item, key, object){\r\n * console.log(key, '=>', item, '<------ Object', object);\r\n * };\r\n * _.each([1, 2, 3], logger);\r\n * _.each({one: 1, two: 2, three: 3}, logger);\r\n *\r\n */\r\n\r\n /*\r\n |--------------------------------------------------------------------------\r\n | Core\r\n |--------------------------------------------------------------------------\r\n */\r\n\r\n\r\n var Util = {};\r\n var slice = Array.prototype.slice;\r\n\r\n /**\r\n * Slice arguments of a function as array\r\n * @param args\r\n * @param {Number} [start]\r\n * @param {Number} [end]\r\n * @return {*}\r\n */\r\n Util.sliceArguments = function (args, start, end) {\r\n return slice.apply(args, slice.call(arguments, 1));\r\n };\r\n\r\n Util.beNumber = function (value, default_value) {\r\n value = parseFloat(value);\r\n\r\n if (Util.isNumeric(value)) {\r\n return value;\r\n }\r\n if (_.isUndefined(default_value)) {\r\n return 0;\r\n }\r\n\r\n return Util.isNumeric(default_value) ? parseFloat(default_value) : Util.beNumber(default_value, 0);\r\n };\r\n\r\n /**\r\n * Make sure first argument is object or arguments are name and value of object\r\n * @param {*} [name]\r\n * @param {*} [value]\r\n * @returns {*}\r\n * @example\r\n * Khoai.util.beObject(); //{}\r\n * Khoai.util.beObject(['foo', 'bar', 123]); //{0: \"a\", 1: 'bar', 2: 123}\r\n * Khoai.util.beObject('yahoo'); //{0: \"yahoo\"}\r\n * Khoai.util.beObject(235); //{0: 235}\r\n * Khoai.util.beObject('yahoo', 123); //{yahoo: 123}\r\n * Khoai.util.beObject({yahoo: 123, goooo:'ASDWd'}); //{yahoo: 123, goooo:'ASDWd'}\r\n *\r\n */\r\n Util.beObject = function (name, value) {\r\n switch (true) {\r\n case arguments.length == 1:\r\n if (_.isObject(name)) {\r\n return name;\r\n } else if (_.isArray(name) || _.isArguments(name)) {\r\n return _.zipObject(Object.keys(name), name);\r\n }\r\n\r\n return {0: name};\r\n break;\r\n\r\n case arguments.length >= 2:\r\n if (_.isObject(name)) {\r\n return name;\r\n }\r\n var obj = {};\r\n\r\n obj[name] = value;\r\n\r\n return obj;\r\n }\r\n\r\n return {};\r\n };\r\n\r\n var cast_types = {};\r\n\r\n cast_types['string'] = function (value) {\r\n return value + '';\r\n };\r\n cast_types['boolean'] = function (value) {\r\n return Boolean(value);\r\n };\r\n\r\n cast_types['number'] = function (value) {\r\n return Util.beNumber(value);\r\n };\r\n cast_types['integer'] = function (value) {\r\n return Math.floor(Util.beNumber(value));\r\n };\r\n cast_types['array'] = _.castArray;\r\n cast_types['object'] = function (value) {\r\n return Util.beObject(value);\r\n };\r\n\r\n /**\r\n * Convert array|object item to other type. Support types:\r\n * - string\r\n * - boolean\r\n * - number\r\n * - integer\r\n * - array\r\n * - object\r\n *\r\n * @param object\r\n * @param type\r\n * @return {Array|object}\r\n * @throws Error when cast type is unsupported\r\n */\r\n Util.castItemsType = function (object, type) {\r\n if (!cast_types.hasOwnProperty(type)) {\r\n throw new Error('Invalid cast type. Available types are: string, number, integer, array and object');\r\n }\r\n if (_.isArray(object)) {\r\n return _.map(_.clone(object), cast_types[type]);\r\n }\r\n\r\n return _.mapObject(_.clone(object), cast_types[type]);\r\n };\r\n\r\n /**\r\n * Loop over array or object like _.each but breakable\r\n * @param {object|Array} obj Loop object\r\n * @param {loopCallback} callback callback apply on every item, return break value to break the loop\r\n * @param {string} [break_on=break] Value of callback result that break the loop, default is 'break'\r\n * @returns {*}\r\n * @example\r\n * Khoai.util.loop([1,2,3,4,5], function(item){\r\n * console.log('Number', item);\r\n * if(item > 3){\r\n * return 'break';\r\n * }\r\n * });\r\n * //Console will log:\r\n * // Number 1\r\n * // Number 2\r\n * // Number 3\r\n * @example\r\n * Khoai.util.loop([1,2,3,4,5], function(item){\r\n * console.log('Number', item);\r\n * if(item > 3){\r\n * return 'yahoo';\r\n * }\r\n * }, 'yahoo');\r\n * //Console will log:\r\n * // Number 1\r\n * // Number 2\r\n * // Number 3\r\n *\r\n */\r\n Util.loop = function (obj, callback, break_on) {\r\n var i, length;\r\n if (_.isUndefined(break_on)) {\r\n break_on = 'break';\r\n }\r\n if (_.isArray(obj)) {\r\n for (i = 0, length = obj.length; i < length; i++) {\r\n if (callback(obj[i], i, obj) === break_on) {\r\n break;\r\n }\r\n }\r\n } else {\r\n var keys = _.keys(obj);\r\n for (i = 0, length = keys.length; i < length; i++) {\r\n if (callback(obj[keys[i]], keys[i], obj) === break_on) {\r\n break;\r\n }\r\n }\r\n }\r\n return obj;\r\n };\r\n\r\n var unique_id_current_status = {};\r\n\r\n /**\r\n * Return Next ID of type, start from 1\r\n * @param {string} [type=\"unique_id\"] Type of ID\r\n * @param {boolean} [type_as_prefix = true] Use type as prefix of return ID\r\n * @returns {string|number}\r\n * @example Default type\r\n * Khoai.util.nextID(); //unique_id_1\r\n * Khoai.util.nextID(); //unique_id_2\r\n * Khoai.util.nextID(null, false); //3\r\n * Khoai.util.nextID('superman'); //superman_1\r\n * Khoai.util.nextID('superman'); //superman_2\r\n * Khoai.util.nextID(); //unique_id_4\r\n * Khoai.util.nextID('superman', false); //3\r\n *\r\n */\r\n Util.nextID = function (type, type_as_prefix) {\r\n if (_.isEmpty(type)) {\r\n type = 'unique_id';\r\n }\r\n if (!unique_id_current_status.hasOwnProperty(type)) {\r\n unique_id_current_status[type] = 1;\r\n } else {\r\n unique_id_current_status[type]++;\r\n }\r\n\r\n return get_unique_id_with_prefix(type, unique_id_current_status[type], type_as_prefix);\r\n };\r\n\r\n /**\r\n * Return current ID of type\r\n * @param {string} [type=\"unique_id\"] Type of ID\r\n * @param {boolean} [type_as_prefix = true] Use type as prefix of return ID\r\n * @returns {boolean|string|number}\r\n * @example\r\n * Khoai.util.currentID(); //false\r\n * Khoai.util.nextID(); //unique_id_0\r\n * Khoai.util.nextID(); //unique_id_1\r\n * Khoai.util.currentID(); //unique_id_1\r\n * Khoai.util.currentID(null, false); //1\r\n * Khoai.util.currentID('superman'); //false\r\n * Khoai.util.nextID('superman'); //superman_0\r\n * Khoai.util.nextID('superman'); //superman_1\r\n * Khoai.util.currentID('superman'); //superman_1\r\n * Khoai.util.currentID('superman', false); //1\r\n * Khoai.util.nextID(); //2\r\n * Khoai.util.currentID(); //unique_id_2\r\n */\r\n Util.currentID = function (type, type_as_prefix) {\r\n if (_.isEmpty(type)) {\r\n type = 'unique_id';\r\n }\r\n\r\n if (!unique_id_current_status.hasOwnProperty(type)) {\r\n return false;\r\n }\r\n\r\n return get_unique_id_with_prefix(type, unique_id_current_status[type], type_as_prefix);\r\n };\r\n\r\n /**\r\n *\r\n * @param {string} [type] a type, do not require existed\r\n * @param {number} [value]\r\n * @returns {number|*}\r\n */\r\n Util.resetID = function (type, value) {\r\n if (!arguments.length) {\r\n type = 'unique_id';\r\n }\r\n\r\n type = String(type);\r\n\r\n if (_.isUndefined(value)) {\r\n delete unique_id_current_status[type];\r\n } else {\r\n value = arguments.length > 1 ? Util.beNumber(value) : 0;\r\n value = Math.max(value, 0);\r\n unique_id_current_status[type] = value;\r\n }\r\n\r\n return value;\r\n };\r\n\r\n function get_unique_id_with_prefix(type, id, type_as_prefix) {\r\n if (type_as_prefix || _.isUndefined(type_as_prefix)) {\r\n return type + '_' + id;\r\n }\r\n\r\n return id;\r\n }\r\n\r\n\r\n /**\r\n * Get constructor name of object\r\n * @param obj\r\n * @param {boolean} [constructor_only = false] Return object's constructor name only\r\n * @returns {string}\r\n * @example\r\n * Khoai.util.className(Khoai.App); //\"[object Object]\"\r\n * Khoai.util.className(Khoai.App, true); //App\r\n * Khoai.util.className(new Khoai.EventEmitter(), true); //EventEmitter\r\n */\r\n Util.className = function (obj, constructor_only) {\r\n if (constructor_only) {\r\n return obj.constructor.name;\r\n }\r\n return Object.prototype.toString.call(obj);\r\n };\r\n\r\n /**\r\n * Get type of content. If is object then return constructor name\r\n * @param content\r\n * @returns {string}\r\n * @example\r\n * Khoai.util.contentType(123); //number\r\n * Khoai.util.contentType('123'); //string\r\n * Khoai.util.contentType('Yahooooooo'); //string\r\n * Khoai.util.contentType(true); //boolean\r\n * Khoai.util.contentType(true); //boolean\r\n * Khoai.util.contentType([1,2]); //Array\r\n * Khoai.util.contentType({}); //Object\r\n * Khoai.util.contentType(_.App); //App\r\n */\r\n Util.contentType = function (content) {\r\n var type = typeof content;\r\n\r\n if (type === 'object') {\r\n var class_name = Util.className(content, true);\r\n\r\n if (class_name) {\r\n return class_name;\r\n }\r\n }\r\n\r\n return type;\r\n };\r\n\r\n /**\r\n * Check object is an instance of a constructor type\r\n * @param {*} obj\r\n * @param {string} class_name Name of class\r\n * @returns {boolean}\r\n * @example\r\n * Khoai.util.isInstanceOf(Khoai.App, 'App');//true\r\n * Khoai.util.isInstanceOf(123, 'Object'); //false\r\n * Khoai.util.isInstanceOf(123, 'Number'); //true\r\n * Khoai.util.isInstanceOf('123', 'String'); //true\r\n * Khoai.util.isInstanceOf(new Khoai.util.EventEmitter(), 'EventEmitter'); //true\r\n */\r\n Util.isInstanceOf = function (obj, class_name) {\r\n return Util.className(obj, true) === class_name;\r\n };\r\n\r\n /**\r\n * Check if object is primitive type: null, string, number, boolean\r\n * @param value\r\n * @returns {boolean}\r\n * @example\r\n * Khoai.util.isPrimitiveType(123); //true\r\n * Khoai.util.isPrimitiveType('123'); //true\r\n * Khoai.util.isPrimitiveType(null); //true\r\n * Khoai.util.isPrimitiveType(); //true\r\n * Khoai.util.isPrimitiveType(_.App); //false\r\n */\r\n Util.isPrimitiveType = function (value) {\r\n if (_.isObject(value)) {\r\n return false;\r\n }\r\n\r\n var type = typeof value;\r\n return value == null || type === 'string' || type === 'number' || type === 'boolean';\r\n };\r\n\r\n Util.mergeObject = function () {\r\n var next_index = 0;\r\n\r\n for (var i = 0, length = arguments.length; i < length; i++) {\r\n if (_.isArray(arguments[i]) || !_.isObject(arguments[i])) {\r\n arguments[i] = _.castArray(arguments[i]);\r\n arguments[i] = _.zipObject(_.range(next_index, next_index += arguments[i].length), arguments[i]);\r\n }\r\n }\r\n\r\n return _.extend.apply(_, arguments);\r\n };\r\n\r\n function is_diff_strict_cb(value_1, value_2) {\r\n return value_1 !== value_2;\r\n }\r\n\r\n function is_diff_loose_cb(value_1, value_2) {\r\n return value_1 != value_2;\r\n }\r\n\r\n /**\r\n * Get dirty of object with others object\r\n * @param {function} cb Callback return true if 2 item is difference\r\n * @param object\r\n * @param [others...]\r\n * @return {{}}\r\n */\r\n function diff_object(cb, object, others) {\r\n if (arguments.length < 2) {\r\n return {};\r\n }\r\n\r\n var result = {};\r\n\r\n if (!_.isFunction(cb)) {\r\n cb = cb ? is_diff_strict_cb : is_diff_loose_cb;\r\n }\r\n\r\n others = Util.mergeObject.apply(Util, slice.call(arguments, 2));\r\n\r\n _.each(object, function (value, key) {\r\n if (!others.hasOwnProperty(key)) {\r\n result[key] = value;\r\n } else {\r\n if (cb(value, others[key])) {\r\n result[key] = value;\r\n }\r\n }\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Get dirty of object with others object. Strict comparison\r\n * @param {object} object\r\n * @param {object} others\r\n * @return {*}\r\n * @example\r\n * Khoai.util.diffObject({a: 0, b: 1}, {a: '0', b: 1}); //{a: 0}\r\n */\r\n Util.diffObject = function (object, others) {\r\n var args = _.flatten(_.toArray(arguments));\r\n\r\n args.unshift(is_diff_strict_cb);\r\n\r\n return diff_object.apply(null, args);\r\n };\r\n\r\n /**\r\n * Get dirty of object with others object. Loose comparison\r\n * @param {object} object\r\n * @param {object} others\r\n * @return {*}\r\n * @example\r\n * Khoai.util.diffObjectLoose({a: 0, b: 1}, {a: '0', b: 2}); //{b: 1}\r\n */\r\n Util.diffObjectLoose = function (object, others) {\r\n var args = _.flatten(_.toArray(arguments));\r\n\r\n args.unshift(is_diff_loose_cb);\r\n\r\n return diff_object.apply(null, args);\r\n };\r\n\r\n /**\r\n * Get dirty of object with others object, use callback\r\n * @param {function} callback Callback with 2 parameters: base value, other object value. Return true when difference\r\n * @param {object} object\r\n * @param {object} others\r\n * @return {*}\r\n */\r\n Util.diffObjectWith = function (callback, object, others) {\r\n return diff_object.apply(null, slice.apply(arguments))\r\n };\r\n\r\n\r\n /**\r\n * Get random string\r\n * @param {number} [length]\r\n * @param {string} [chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ']\r\n * @returns {string}\r\n * @example\r\n * Khoai.util.randomString(10); //'mYJeC1xBcl'\r\n * Khoai.util.randomString(10, 'ABCDEF'); //'CDABBEFADE'\r\n */\r\n Util.randomString = function (length, chars) {\r\n var result = '', chars_length, i;\r\n if (_.isUndefined(chars) || !chars.toString().length) {\r\n chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\r\n }\r\n chars_length = chars.length;\r\n\r\n length = Util.beNumber(length, 10);\r\n\r\n for (i = length; i > 0; --i) {\r\n result += chars[Math.round(Math.random() * (chars_length - 1))];\r\n }\r\n return result;\r\n };\r\n\r\n\r\n /**\r\n * Setup a object with field name and value or object of fields\r\n * @param {Object} object host object\r\n * @param {(string|Object)} option field name of object of fields\r\n * @param {*} value value of field when option param is field name\r\n * @returns {{}}\r\n * @example\r\n * var obj = {a: 'A', b: 'B'}\r\n * Khoai.util.setup(obj, 'a', '123'); //obj -> {a: '123', b: 'B'}\r\n * Khoai.util.setup(obj, {b: 'Yahoo', c: 'ASD'}); //obj -> {a: 123, b: 'Yahoo', c: 'ASD'}\r\n */\r\n Util.setup = function (object, option, value) {\r\n if (!_.isObject(object)) {\r\n object = {};\r\n }\r\n if (_.isObject(option)) {\r\n _.each(option, function (val, path) {\r\n _.set(object, path, val);\r\n });\r\n } else {\r\n _.set(object, option, value);\r\n }\r\n\r\n return object;\r\n };\r\n\r\n /**\r\n * Get all of valid keys that exists in object.\r\n *\r\n * @param {object} object\r\n * @param {Array} keys\r\n * @return {Array}\r\n */\r\n Util.validKeys = function (object, keys) {\r\n var result = [];\r\n\r\n keys = _.castArray(keys);\r\n for (var i = 0, length = keys.length; i < length; i++) {\r\n if (object.hasOwnProperty(keys[i])) {\r\n result.push(keys[i]);\r\n }\r\n }\r\n\r\n return result;\r\n };\r\n\r\n\r\n /**\r\n * Like _.pairs but array item is an object with field is \"key\", \"value\"\r\n * @param {{}} object\r\n * @param {string} [key = 'key']\r\n * @param {string} [value = 'value']\r\n * @returns {Array}\r\n * @example\r\n * Khoai.util.pairsAsObject({one: 1, two: 2, three: 3});\r\n * => [{key: 'one', value: 1},{key: 'two', value: 2},{key: 'three', value: 3}]\r\n */\r\n Util.pairsAsObject = function (object, key, value) {\r\n var result = [],\r\n field_key = key || 'key',\r\n field_value = value || 'value';\r\n\r\n _.each(object, function (value, key) {\r\n var item = {};\r\n\r\n item[field_key] = key;\r\n item[field_value] = value;\r\n\r\n result.push(item);\r\n });\r\n\r\n return result;\r\n };\r\n\r\n /**\r\n * A convenient version of what is perhaps the most common use-case for map: extracting a list of property values, with a column as key.\r\n * @param {Array} collection\r\n * @param {string} key_field If key field not found then use as \"undefined\"\r\n * @param {string} value_field If value field not found then use as \"undefined\"\r\n * @returns {{}}\r\n * @example\r\n * var stooges = [{name: 'moe', id: 1, age: 40}, {name: 'larry', id: 2, age: 50}, {name: 'curly', id: 4, age: 60}];\r\n * Khoai.util.pluckBy(stooges, 'id', 'name');\r\n * => {1: 'moe', 2: 'larry', 3: 'curly'}\r\n */\r\n Util.pluckBy = function (collection, key_field, value_field) {\r\n var result = {};\r\n\r\n _.each(collection, function (object) {\r\n if (object.hasOwnProperty(key_field)) {\r\n result[object[key_field]] = object.hasOwnProperty(value_field) ? object[value_field] : undefined;\r\n }\r\n });\r\n\r\n return result;\r\n };\r\n\r\n /**\r\n * Check value is numeric\r\n * @param value\r\n * @returns {boolean}\r\n *\r\n * @example\r\n * Khoai.util.isNumeric(123); //true\r\n * Khoai.util.isNumeric(123.5); //true\r\n * Khoai.util.isNumeric('123.5'); //true\r\n */\r\n Util.isNumeric = function (value) {\r\n return !_.isArray(value) && (value - parseFloat(value) + 1) >= 0;\r\n };\r\n\r\n /**\r\n * Make sure value is in array\r\n * @param {*} value\r\n * @param {Array} values\r\n * @param {*} [default_value] Default value if not found value in values\r\n * @returns {*} If found then return value itself, else, return default_value or first item of array\r\n * @example\r\n * var items = [1,2,3,'a'];\r\n * Khoai.util.oneOf(1, items); // 1\r\n * Khoai.util.oneOf(0, items); // 1\r\n * Khoai.util.oneOf('a', items); // 'a'\r\n * Khoai.util.oneOf('b', items, 'C'); // 'C'\r\n */\r\n Util.oneOf = function (value, values, default_value) {\r\n if (-1 !== values.indexOf(value)) {\r\n return value;\r\n }\r\n\r\n if (arguments.length >= 3) {\r\n return default_value;\r\n }\r\n\r\n return _.first(values);\r\n };\r\n\r\n /**\r\n * Escape URL\r\n * @param {string} url\r\n * @param {boolean} [param = false] Include param?\r\n * @returns {string}\r\n */\r\n Util.escapeURL = function (url, param) {\r\n return param ? encodeURIComponent(url) : encodeURI(url);\r\n };\r\n\r\n /**\r\n * Unescape URL\r\n * @param {string} url\r\n * @param {boolean} [param = false] Include param?\r\n * @returns {string}\r\n */\r\n Util.unescapeURL = function (url, param) {\r\n return param ? decodeURI(url) : decodeURIComponent(url);\r\n };\r\n\r\n\r\n /**\r\n * Split array to n of chunks\r\n * @param {Array} array\r\n * @param {number} chunks Number of chunks\r\n * @return {Array}\r\n * @example\r\n * var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];\r\n * Khoai.util.chunks(arr, 3)\r\n * => [\r\n * [0, 1, 2, 3],\r\n * [4, 5, 6, 7],\r\n * [8, 9]\r\n * ]\r\n */\r\n Util.chunks = function (array, chunks) {\r\n return _.chunk(array, Math.ceil(array.length / chunks));\r\n };\r\n\r\n /**\r\n * Toggle array's elements\r\n * @param {Array} array\r\n * @param {Array} elements\r\n * @param {boolean} status If this param is boolean then add/remove base on it value. By default it is undefined -\r\n * add if none exists, remove if existed\r\n * @returns {Array}\r\n * @example\r\n * var arr = ['A', 'B', 'C', 'D'];\r\n * Khoai.util.toggle(arr, ['A', 'V']) => ['B', 'C', 'D', 'V']\r\n * Khoai.util.toggle(arr, ['A', 'V'], true) => ['A', 'B', 'C', 'D', 'V']\r\n * Khoai.util.toggle(arr, ['A', 'V'], false) => ['B', 'C', 'D']\r\n */\r\n Util.toggle = function (array, elements, status) {\r\n elements = _.uniq(_.castArray(elements));\r\n if (_.isUndefined(status)) {\r\n var exclude = _.intersection(array, elements);\r\n var include = _.difference(elements, array);\r\n\r\n array = _.union(_.difference(array, exclude), include);\r\n } else {\r\n if (status) {\r\n array = _.union(array, elements);\r\n } else {\r\n array = _.difference(array, elements);\r\n }\r\n }\r\n return array;\r\n };\r\n\r\n /**\r\n * Create, define and return a object with properties. Object's properties are fixed and enumerable\r\n * @param {{}} properties\r\n * @returns {{}}\r\n * @example\r\n * var obj = Khoai.util.defineObject({name: 'Manh', old: 123, hello: function(){\r\n * alert('Hello ' + this.name);\r\n * }});\r\n *\r\n * obj.old = 10;\r\n * console.log(obj); //{name: 'Manh', old: 123}\r\n * _.each(obj, function(value, key){\r\n * console.log(key, '=>', value);\r\n * });\r\n * //name => Manh\r\n * //old => 123\r\n */\r\n Util.defineObject = function (properties) {\r\n var obj = {};\r\n _.each(properties, function (value, key) {\r\n key = key.trim();\r\n if (!_.has(obj, key)) {\r\n Object.defineProperty(obj, key, {\r\n enumerable: !_.isFunction(value),\r\n value: value\r\n });\r\n }\r\n });\r\n return obj;\r\n };\r\n\r\n /**\r\n * Define a MaDnh constant\r\n * @param {object} target\r\n * @param {(string|Object)} name\r\n * @param {*} [value = undefined]\r\n * @example\r\n * Khoai.util.defineConstant(obj, 'TEST', 123) => obj.TEST = 123\r\n */\r\n Util.defineConstant = function (target, name, value) {\r\n var obj = {};\r\n\r\n if (_.isObject(name)) {\r\n obj = name;\r\n value = undefined;\r\n } else {\r\n obj[name] = value;\r\n }\r\n _.each(obj, function (val, key) {\r\n key = key.trim().toUpperCase();\r\n\r\n if (!target.hasOwnProperty(key)) {\r\n Object.defineProperty(target, key, {\r\n enumerable: true,\r\n value: val\r\n });\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * Inherit constructor prototype\r\n * @param {function} subclass_constructor Destination constructor\r\n * @param {function} base_class_constructor Source constructor\r\n * @param {boolean} [addSuper = true] Add property to destination prototype that reference back to source prototype\r\n *\r\n * @see https://github.com/Olical/Heir\r\n *\r\n * @example\r\n * function MyEE(){\r\n * M.EventEmitter.call(this);\r\n * }\r\n *\r\n * M.inherit(MyEE, M.EventEmitter);\r\n */\r\n Util.inherit = function (subclass_constructor, base_class_constructor, addSuper) {\r\n var proto = subclass_constructor.prototype = Object.create(base_class_constructor.prototype);\r\n proto.constructor = subclass_constructor;\r\n\r\n if (addSuper || _.isUndefined(addSuper)) {\r\n proto._super = base_class_constructor.prototype;\r\n }\r\n };\r\n\r\n /**\r\n * Call callback with arguments\r\n * @param {string|function|Array} callback\r\n * @param {*} [args] Callback arguments, if only one argument as array passed then it must be wrapped by array, eg:\r\n * [users]\r\n * @param {Object|null} context context of \"this\" keyword\r\n * @returns {*}\r\n *\r\n * @example\r\n * Khoai.util.callFunc(null, alert, 123);\r\n * Khoai.util.callFunc(null, function(name, old){\r\n * alert('My name is ' + name + ', ' + old + 'years old');\r\n * }, ['Manh', 10]);\r\n *\r\n * var obj = {name: 'Manh', old: 10};\r\n * Khoai.util.callFunc(obj, function(say_hi){\r\n * alert((say_hi ? 'Hi' : 'Hello') + '! my name is ' + this.name + ', ' + this.old + ' years old');\r\n * }, true);\r\n */\r\n Util.callFunc = function (callback, args, context) {\r\n if (arguments.length >= 2) {\r\n args = _.castArray(args);\r\n } else {\r\n args = [];\r\n }\r\n\r\n if (callback) {\r\n if (_.isFunction(callback)) {\r\n return callback.apply(context || null, args);\r\n } else if (_.isString(callback)) {\r\n if (window.hasOwnProperty(callback) && _.isFunction(window[callback])) {\r\n return window[callback].apply(context || null, args);\r\n }\r\n\r\n throw new Error('Invalid callback!');\r\n } else if (_.isArray(callback)) {\r\n var result = [],\r\n this_func = arguments.callee;\r\n\r\n _.each(callback, function (tmpFunc) {\r\n result.push(this_func(tmpFunc, args, context));\r\n });\r\n\r\n return result;\r\n }\r\n }\r\n\r\n return undefined;\r\n };\r\n\r\n /**\r\n * Call callback asynchronous. Similar to Khoai.util.callFunc\r\n *\r\n * @param {(string|function|Array)} callback\r\n * @param {*} [args] Callback arguments, if only one argument as array passed then it must be wrapped by array, eg:\r\n * [users]\r\n * @param {number} [delay=1] Delay milliseconds\r\n * @param {Object|null} context context of \"this\" keyword\r\n * @see callFunc\r\n */\r\n Util.async = function (callback, args, delay, context) {\r\n delay = parseInt(delay);\r\n if (_.isNaN(delay)) {\r\n delay = 1;\r\n }\r\n\r\n return setTimeout(function () {\r\n Util.callFunc(callback, args, context || null);\r\n }, Math.max(1, delay));\r\n };\r\n\r\n function createConsoleCB(name, description) {\r\n return function () {\r\n console[name].apply(console, (description ? [description] : []).concat(slice.apply(arguments)));\r\n }\r\n }\r\n\r\n /**\r\n * Like console.log with dynamic arguments\r\n * @example\r\n * var args = [1,2,3,4];\r\n * Khoai.util.logArgs('a',123);\r\n * Khoai.util.logArgs.apply(null, args);\r\n */\r\n Util.logArgs = function () {\r\n console.log.apply(console, slice.apply(arguments));\r\n };\r\n\r\n /**\r\n * Create console log callback with description as first arguments\r\n * @param {string} description\r\n * @returns {*}\r\n * @example\r\n * var cb = Khoai.util.logCb('Test 1');\r\n * cb(1,2,3); // Console log: 'Test 1' 1 2 3\r\n */\r\n Util.logCb = function (description) {\r\n return createConsoleCB.apply(null, ['log'].concat(slice.apply(arguments)));\r\n };\r\n\r\n /**\r\n * Like console.warn with dynamic arguments\r\n * @example\r\n * var args = [1,2,3,4];\r\n * Khoai.util.warnArgs('a',123);\r\n * Khoai.util.warnArgs.apply(null, args);\r\n */\r\n Util.warnArgs = function () {\r\n console.warn.apply(console, slice.apply(arguments));\r\n };\r\n\r\n /**\r\n * Create console waring callback with description as first arguments\r\n * @param {string} description\r\n * @returns {*}\r\n * @example\r\n * var cb = Khoai.util.warnCb('Test 1');\r\n * cb(1,2,3); // Console warn as: 'Test 1' 1 2 3\r\n */\r\n Util.warnCb = function (description) {\r\n return createConsoleCB.apply(null, ['warn'].concat(slice.apply(arguments)));\r\n };\r\n\r\n /**\r\n * Like console.error with dynamic arguments\r\n * @example\r\n * var args = [1,2,3,4];\r\n * Khoai.util.errorArgs('a',123);\r\n * Khoai.util.errorArgs.apply(null, args);\r\n */\r\n Util.errorArgs = function () {\r\n console.error.apply(console, slice.apply(arguments));\r\n };\r\n\r\n /**\r\n * Create console error callback with description as first arguments\r\n * @param {string} description\r\n * @returns {*}\r\n * @example\r\n * var cb = Khoai.util.errorCb('Test 1');\r\n * cb(1,2,3); // Console error as: 'Test 1' 1 2 3\r\n */\r\n Util.errorCb = function (description) {\r\n return createConsoleCB.apply(null, ['error'].concat(slice.apply(arguments)));\r\n };\r\n\r\n\r\n var debug_types_status = {},\r\n all_debugging = false;\r\n\r\n /**\r\n *\r\n * @param type\r\n * @returns {boolean}\r\n */\r\n Util.isDebugging = function (type) {\r\n if (all_debugging || _.isEmpty(type)) {\r\n return all_debugging;\r\n }\r\n\r\n return debug_types_status.hasOwnProperty(type) && debug_types_status[type];\r\n\r\n };\r\n /**\r\n *\r\n * @param [type] default is all debug type\r\n */\r\n Util.debugging = function (type) {\r\n if (_.isEmpty(type)) {\r\n all_debugging = true;\r\n return;\r\n }\r\n\r\n debug_types_status[type] = true;\r\n };\r\n /**\r\n *\r\n * @param [type] default is all debug type\r\n */\r\n Util.debugComplete = function (type) {\r\n if (_.isEmpty(type)) {\r\n all_debugging = false;\r\n debug_types_status = {};\r\n\r\n return;\r\n }\r\n\r\n delete debug_types_status[type];\r\n };\r\n\r\n /**\r\n * Run callback if is in debugging of a type\r\n * @param type null - all debug type\r\n * @param {function} callback\r\n */\r\n Util.onDebugging = function (type, callback) {\r\n if (Util.isDebugging(type)) {\r\n Util.callFunc(callback);\r\n }\r\n };\r\n\r\n /**\r\n * Get json string of an array\r\n * @param {array} details\r\n * @param {string} [glue=\"\\n\"]\r\n * @returns {string}\r\n */\r\n Util.getDebugString = function (details, glue) {\r\n var result = [];\r\n\r\n _.each(_.castArray(details), function (item) {\r\n result.push(JSON.stringify(item));\r\n });\r\n\r\n return result.join(glue || \"\\n\");\r\n };\r\n\r\n /**\r\n *\r\n * @param args\r\n * @param order\r\n * @param rules\r\n * @returns {{}}\r\n * @example\r\n * var order = ['int', 'bool', 'str'],\r\n * rules = {int: 'number', bool: 'boolean', str: 'string'};\r\n *\r\n * Khoai.util.optionalArgs([1, true, 'A'], order, rules); //{int: 1, bool: true, str: \"A\"}\r\n * Khoai.util.optionalArgs([true, 'A'], order, rules);//{bool: true, str: \"A\"}\r\n * Khoai.util.optionalArgs(['A'], order, rules); //{str: \"A\"}\r\n * Khoai.util.optionalArgs(['A', 'V'], order, rules); //{int: \"A\", bool: \"V\"}\r\n * Khoai.util.optionalArgs([1, []], order, rules); //{int: 1, bool: []}\r\n * Khoai.util.optionalArgs([true, []], order, rules); //{int: true, bool: []}\r\n * Khoai.util.optionalArgs(['A', []], order, rules); //{int: \"A\", bool: []}\r\n * Khoai.util.optionalArgs([[], []], order, rules); //{int: Array[0], bool: []}\r\n *\r\n * Khoai.util.optionalArgs(['A', 'V'], ['int', 'bool', 'str', 'str2'], {int: 'number', bool: 'boolean', str: 'string', str2: 'string'});\r\n * //=> {str: \"A\", str2: \"V\"}\r\n */\r\n Util.optionalArgs = function (args, order, rules) {\r\n var result = {},\r\n arg, index = 0, last_index, missing_rules, type, args_cloned, args_with_type, found;\r\n\r\n missing_rules = _.difference(order, Object.keys(rules));\r\n missing_rules.forEach(function (missing) {\r\n rules[missing] = true;\r\n });\r\n\r\n args_with_type = order.map(function (arg_name) {\r\n return rules[arg_name];\r\n });\r\n\r\n if (_.isEmpty(args)) {\r\n return result;\r\n }\r\n if (args.length >= order.length) {\r\n result = _.zipObject(order, args.slice(0, order.length));\r\n } else {\r\n args_cloned = args.slice(0);\r\n\r\n while (arg = args_cloned.shift()) {\r\n type = Util.contentType(arg);\r\n found = false;\r\n last_index = index;\r\n\r\n Util.loop(args_with_type.slice(index), (function (tmp_arg, tmp_type) {\r\n return function (types) {\r\n if (types === true || tmp_type === types\r\n || (_.isArray(types) && -1 != types.indexOf(tmp_type))\r\n || (_.isFunction(types) && types(tmp_arg))) {\r\n found = true;\r\n\r\n return 'break';\r\n }\r\n\r\n index++;\r\n }\r\n })(arg, type));\r\n\r\n if (!found) {\r\n result = _.zipObject(order.slice(0, args.length), args);\r\n break;\r\n }\r\n\r\n result[order[index++]] = arg;\r\n }\r\n }\r\n\r\n return result;\r\n };\r\n\r\n /**\r\n * Sort number asc\r\n */\r\n function sortNumberCallback(a, b) {\r\n return a - b;\r\n }\r\n\r\n /**\r\n * Sort number desc\r\n */\r\n function sortNumberDescCallback(a, b) {\r\n return b - a;\r\n }\r\n\r\n function is_equal_strict(a, b) {\r\n return a === b;\r\n }\r\n\r\n function is_equal_loose(a, b) {\r\n return a == b;\r\n }\r\n\r\n\r\n Util.defineConstant(Util, {\r\n /**\r\n * Array sort compare function. Sort number\r\n * @constant\r\n * @example\r\n * var scores = [1, 10, 2, 21];\r\n * scores.sort(); // [1, 10, 2, 21]\r\n * scores.sort(Khoai.util.SORT_NUMBER); // [1, 2, 10, 21]\r\n */\r\n SORT_NUMBER: sortNumberCallback,\r\n /**\r\n * Array sort compare function. Sort number desc\r\n * @constant\r\n * @example\r\n * var scores = [1, 10, 2, 21];\r\n * scores.sort(Khoai.util.SORT_NUMBER_DESC); // [21, 10, 2, 1]\r\n */\r\n SORT_NUMBER_DESC: sortNumberDescCallback\r\n });\r\n\r\n\r\n Khoai.util = Util;\r\n}));"]} \ No newline at end of file diff --git a/dist/khoai.min.js b/dist/khoai.min.js new file mode 100644 index 0000000..7d4b7eb --- /dev/null +++ b/dist/khoai.min.js @@ -0,0 +1,2 @@ +!function(n,e){"function"==typeof define&&define.amd?define([],function(){return n.Khoai=e()}):n.Khoai=e()}(this,function(){"use strict";var n={},e=0;Object.defineProperty(n,"VERSION",{value:"0.0.1"});var t=function(){this.proxiedMethods={}};return n.getProxyCounter=function(){return e},t.prototype.dispose=function(){for(var n in this.proxiedMethods)this.proxiedMethods.hasOwnProperty(n)&&(this.proxiedMethods[n]=null);this.proxiedMethods={}},t.prototype.proxy=function(n){return void 0===n.khoaiProxyId&&(e++,n.khoaiProxyId=e),void 0!==this.proxiedMethods[n.khoaiProxyId]?this.proxiedMethods[n.khoaiProxyId]:(this.proxiedMethods[n.khoaiProxyId]=n.bind(this),this.proxiedMethods[n.khoaiProxyId])},n.baseClass=t,n}),function(n,e){"function"==typeof define&&define.amd?define(["lodash","khoai"],function(n,t){e(n,t)}):e(n._,n.Khoai)}(this,function(n,e){function t(e,t,r){return r||n.isUndefined(r)?e+"_"+t:t}function r(n,e){return n!==e}function i(n,e){return n!=e}function o(e,t,o){if(arguments.length<2)return{};var u={};return n.isFunction(e)||(e=e?r:i),o=s.mergeObject.apply(s,f.call(arguments,2)),n.each(t,function(n,t){o.hasOwnProperty(t)?e(n,o[t])&&(u[t]=n):u[t]=n}),u}function u(n,e){return function(){console[n].apply(console,(e?[e]:[]).concat(f.apply(arguments)))}}function c(n,e){return n-e}function a(n,e){return e-n}var s={},f=Array.prototype.slice;s.sliceArguments=function(n,e,t){return f.apply(n,f.call(arguments,1))},s.beNumber=function(e,t){return e=parseFloat(e),s.isNumeric(e)?e:n.isUndefined(t)?0:s.isNumeric(t)?parseFloat(t):s.beNumber(t,0)},s.beObject=function(e,t){switch(!0){case 1==arguments.length:return n.isObject(e)?e:n.isArray(e)||n.isArguments(e)?n.zipObject(Object.keys(e),e):{0:e};case arguments.length>=2:if(n.isObject(e))return e;var r={};return r[e]=t,r}return{}};var l={};l.string=function(n){return n+""},l.boolean=function(n){return Boolean(n)},l.number=function(n){return s.beNumber(n)},l.integer=function(n){return Math.floor(s.beNumber(n))},l.array=n.castArray,l.object=function(n){return s.beObject(n)},s.castItemsType=function(e,t){if(!l.hasOwnProperty(t))throw new Error("Invalid cast type. Available types are: string, number, integer, array and object");return n.isArray(e)?n.map(n.clone(e),l[t]):n.mapObject(n.clone(e),l[t])},s.loop=function(e,t,r){var i,o;if(n.isUndefined(r)&&(r="break"),n.isArray(e))for(i=0,o=e.length;i1?s.beNumber(t):0,t=Math.max(t,0),p[e]=t),t},s.className=function(n,e){return e?n.constructor.name:Object.prototype.toString.call(n)},s.contentType=function(n){var e=typeof n;if("object"===e){var t=s.className(n,!0);if(t)return t}return e},s.isInstanceOf=function(n,e){return s.className(n,!0)===e},s.isPrimitiveType=function(e){if(n.isObject(e))return!1;var t=typeof e;return null==e||"string"===t||"number"===t||"boolean"===t},s.mergeObject=function(){for(var e=0,t=0,r=arguments.length;t0;--i)o+=t[Math.round(Math.random()*(r-1))];return o},s.setup=function(e,t,r){return n.isObject(e)||(e={}),n.isObject(t)?n.each(t,function(t,r){n.set(e,r,t)}):n.set(e,t,r),e},s.validKeys=function(e,t){var r=[];t=n.castArray(t);for(var i=0,o=t.length;i=0},s.oneOf=function(e,t,r){return-1!==t.indexOf(e)?e:arguments.length>=3?r:n.first(t)},s.escapeURL=function(n,e){return e?encodeURIComponent(n):encodeURI(n)},s.unescapeURL=function(n,e){return e?decodeURI(n):decodeURIComponent(n)},s.chunks=function(e,t){return n.chunk(e,Math.ceil(e.length/t))},s.toggle=function(e,t,r){if(t=n.uniq(n.castArray(t)),n.isUndefined(r)){var i=n.intersection(e,t),o=n.difference(t,e);e=n.union(n.difference(e,i),o)}else e=r?n.union(e,t):n.difference(e,t);return e},s.defineObject=function(e){var t={};return n.each(e,function(e,r){r=r.trim(),n.has(t,r)||Object.defineProperty(t,r,{enumerable:!n.isFunction(e),value:e})}),t},s.defineConstant=function(e,t,r){var i={};n.isObject(t)?(i=t,r=void 0):i[t]=r,n.each(i,function(n,t){t=t.trim().toUpperCase(),e.hasOwnProperty(t)||Object.defineProperty(e,t,{enumerable:!0,value:n})})},s.inherit=function(e,t,r){var i=e.prototype=Object.create(t.prototype);i.constructor=e,(r||n.isUndefined(r))&&(i._super=t.prototype)},s.callFunc=function(e,t,r){if(t=arguments.length>=2?n.castArray(t):[],e){if(n.isFunction(e))return e.apply(r||null,t);if(n.isString(e)){if(window.hasOwnProperty(e)&&n.isFunction(window[e]))return window[e].apply(r||null,t);throw new Error("Invalid callback!")}if(n.isArray(e)){var i=[],o=arguments.callee;return n.each(e,function(n){i.push(o(n,t,r))}),i}}},s.async=function(e,t,r,i){return r=parseInt(r),n.isNaN(r)&&(r=1),setTimeout(function(){s.callFunc(e,t,i||null)},Math.max(1,r))},s.logArgs=function(){console.log.apply(console,f.apply(arguments))},s.logCb=function(n){return u.apply(null,["log"].concat(f.apply(arguments)))},s.warnArgs=function(){console.warn.apply(console,f.apply(arguments))},s.warnCb=function(n){return u.apply(null,["warn"].concat(f.apply(arguments)))},s.errorArgs=function(){console.error.apply(console,f.apply(arguments))},s.errorCb=function(n){return u.apply(null,["error"].concat(f.apply(arguments)))};var d={},y=!1;s.isDebugging=function(e){return y||n.isEmpty(e)?y:d.hasOwnProperty(e)&&d[e]},s.debugging=function(e){if(n.isEmpty(e))return void(y=!0);d[e]=!0},s.debugComplete=function(e){if(n.isEmpty(e))return y=!1,void(d={});delete d[e]},s.onDebugging=function(n,e){s.isDebugging(n)&&s.callFunc(e)},s.getDebugString=function(e,t){var r=[];return n.each(n.castArray(e),function(n){r.push(JSON.stringify(n))}),r.join(t||"\n")},s.optionalArgs=function(e,t,r){var i,o,u,c,a,f,l={},p=0;if(o=n.difference(t,Object.keys(r)),o.forEach(function(n){r[n]=!0}),a=t.map(function(n){return r[n]}),n.isEmpty(e))return l;if(e.length>=t.length)l=n.zipObject(t,e.slice(0,t.length));else for(c=e.slice(0);i=c.shift();){if(u=s.contentType(i),f=!1,p,s.loop(a.slice(p),function(e,t){return function(r){if(!0===r||t===r||n.isArray(r)&&-1!=r.indexOf(t)||n.isFunction(r)&&r(e))return f=!0,"break";p++}}(i,u)),!f){l=n.zipObject(t.slice(0,e.length),e);break}l[t[p++]]=i}return l},s.defineConstant(s,{SORT_NUMBER:c,SORT_NUMBER_DESC:a}),e.util=s}); +//# sourceMappingURL=khoai.min.js.map diff --git a/dist/khoai.min.js.map b/dist/khoai.min.js.map new file mode 100644 index 0000000..30a90e7 --- /dev/null +++ b/dist/khoai.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["khoai.min.js"],"names":["root","factory","define","amd","Khoai","this","proxyCounter","Object","defineProperty","value","BaseClass","proxiedMethods","getProxyCounter","prototype","dispose","key","hasOwnProperty","proxy","method","undefined","khoaiProxyId","bind","baseClass","_","get_unique_id_with_prefix","type","id","type_as_prefix","isUndefined","is_diff_strict_cb","value_1","value_2","is_diff_loose_cb","diff_object","cb","object","others","arguments","length","result","isFunction","Util","mergeObject","apply","slice","call","each","createConsoleCB","name","description","console","concat","sortNumberCallback","a","b","sortNumberDescCallback","Array","sliceArguments","args","start","end","beNumber","default_value","parseFloat","isNumeric","beObject","isObject","isArray","isArguments","zipObject","keys","0","obj","cast_types","Boolean","Math","floor","castArray","castItemsType","Error","map","clone","mapObject","loop","callback","break_on","i","unique_id_current_status","nextID","isEmpty","currentID","resetID","String","max","className","constructor_only","constructor","toString","contentType","content","class_name","isInstanceOf","isPrimitiveType","next_index","range","extend","diffObject","flatten","toArray","unshift","diffObjectLoose","diffObjectWith","randomString","chars","chars_length","round","random","setup","option","val","path","set","validKeys","push","pairsAsObject","field_key","field_value","item","pluckBy","collection","key_field","value_field","oneOf","values","indexOf","first","escapeURL","url","param","encodeURIComponent","encodeURI","unescapeURL","decodeURI","decodeURIComponent","chunks","array","chunk","ceil","toggle","elements","status","uniq","exclude","intersection","include","difference","union","defineObject","properties","trim","has","enumerable","defineConstant","target","toUpperCase","inherit","subclass_constructor","base_class_constructor","addSuper","proto","create","_super","callFunc","context","isString","window","this_func","callee","tmpFunc","async","delay","parseInt","isNaN","setTimeout","logArgs","log","logCb","warnArgs","warn","warnCb","errorArgs","error","errorCb","debug_types_status","all_debugging","isDebugging","debugging","debugComplete","onDebugging","getDebugString","details","glue","JSON","stringify","join","optionalArgs","order","rules","arg","missing_rules","args_cloned","args_with_type","found","index","forEach","missing","arg_name","shift","tmp_arg","tmp_type","types","SORT_NUMBER","SORT_NUMBER_DESC","util"],"mappings":"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","file":"khoai.min.js","sourcesContent":["(function (root, factory) {\r\n if (typeof define === 'function' && define.amd) {\r\n define([], function(){\r\n return (root.Khoai = factory());\r\n });\r\n } else {\r\n // Browser globals\r\n root.Khoai = factory();\r\n }\r\n}(this, function () {\r\n \"use strict\";\r\n\r\n var Khoai = {};\r\n var proxyCounter = 0;\r\n\r\n /**\r\n * Khoai's version\r\n * @constant {string} VERSION\r\n * @default\r\n */\r\n var version = '0.0.1';\r\n\r\n\r\n Object.defineProperty(Khoai, 'VERSION', {\r\n value: version\r\n });\r\n\r\n var BaseClass = function () {\r\n this.proxiedMethods = {}\r\n };\r\n\r\n\r\n /**\r\n *\r\n * @return {number}\r\n */\r\n Khoai.getProxyCounter = function () {\r\n return proxyCounter;\r\n };\r\n\r\n BaseClass.prototype.dispose = function () {\r\n for (var key in this.proxiedMethods) {\r\n if (this.proxiedMethods.hasOwnProperty(key)) {\r\n this.proxiedMethods[key] = null\r\n }\r\n }\r\n\r\n this.proxiedMethods = {}\r\n };\r\n\r\n /*\r\n * Creates a proxied method reference or returns an existing proxied method.\r\n */\r\n BaseClass.prototype.proxy = function (method) {\r\n if (method.khoaiProxyId === undefined) {\r\n proxyCounter++;\r\n method.khoaiProxyId = proxyCounter\r\n }\r\n\r\n if (this.proxiedMethods[method.khoaiProxyId] !== undefined)\r\n return this.proxiedMethods[method.khoaiProxyId];\r\n\r\n this.proxiedMethods[method.khoaiProxyId] = method.bind(this);\r\n\r\n return this.proxiedMethods[method.khoaiProxyId]\r\n };\r\n\r\n Khoai.baseClass = BaseClass;\r\n\r\n return Khoai;\r\n}));\n(function (root, factory) {\r\n if (typeof define === 'function' && define.amd) {\r\n define(['lodash', 'khoai'], function (_, Khoai) {\r\n factory(_, Khoai);\r\n });\r\n } else {\r\n factory(root._, root.Khoai)\r\n }\r\n}(this, function (_, Khoai) {\r\n /*\r\n |--------------------------------------------------------------------------\r\n | Type Definitions\r\n |--------------------------------------------------------------------------\r\n */\r\n\r\n /**\r\n * Loop callback. Useful in _.each, _.map, _.omit,...\r\n * @callback loopCallback\r\n * @param value Item value\r\n * @param key Item name/index\r\n * @param object object of items\r\n *\r\n * @see http://underscorejs.org/#each\r\n * @see http://underscorejs.org/#map\r\n * @see http://underscorejs.org/#omit\r\n *\r\n * @example Alerts each number value in turn...\r\n * _.each([1, 2, 3], alert);\r\n * _.each({one: 1, two: 2, three: 3}, alert);\r\n * @example Log to console each number value in turn...\r\n * var logger = function(item, key, object){\r\n * console.log(key, '=>', item, '<------ Object', object);\r\n * };\r\n * _.each([1, 2, 3], logger);\r\n * _.each({one: 1, two: 2, three: 3}, logger);\r\n *\r\n */\r\n\r\n /*\r\n |--------------------------------------------------------------------------\r\n | Core\r\n |--------------------------------------------------------------------------\r\n */\r\n\r\n\r\n var Util = {};\r\n var slice = Array.prototype.slice;\r\n\r\n /**\r\n * Slice arguments of a function as array\r\n * @param args\r\n * @param {Number} [start]\r\n * @param {Number} [end]\r\n * @return {*}\r\n */\r\n Util.sliceArguments = function (args, start, end) {\r\n return slice.apply(args, slice.call(arguments, 1));\r\n };\r\n\r\n Util.beNumber = function (value, default_value) {\r\n value = parseFloat(value);\r\n\r\n if (Util.isNumeric(value)) {\r\n return value;\r\n }\r\n if (_.isUndefined(default_value)) {\r\n return 0;\r\n }\r\n\r\n return Util.isNumeric(default_value) ? parseFloat(default_value) : Util.beNumber(default_value, 0);\r\n };\r\n\r\n /**\r\n * Make sure first argument is object or arguments are name and value of object\r\n * @param {*} [name]\r\n * @param {*} [value]\r\n * @returns {*}\r\n * @example\r\n * Khoai.util.beObject(); //{}\r\n * Khoai.util.beObject(['foo', 'bar', 123]); //{0: \"a\", 1: 'bar', 2: 123}\r\n * Khoai.util.beObject('yahoo'); //{0: \"yahoo\"}\r\n * Khoai.util.beObject(235); //{0: 235}\r\n * Khoai.util.beObject('yahoo', 123); //{yahoo: 123}\r\n * Khoai.util.beObject({yahoo: 123, goooo:'ASDWd'}); //{yahoo: 123, goooo:'ASDWd'}\r\n *\r\n */\r\n Util.beObject = function (name, value) {\r\n switch (true) {\r\n case arguments.length == 1:\r\n if (_.isObject(name)) {\r\n return name;\r\n } else if (_.isArray(name) || _.isArguments(name)) {\r\n return _.zipObject(Object.keys(name), name);\r\n }\r\n\r\n return {0: name};\r\n break;\r\n\r\n case arguments.length >= 2:\r\n if (_.isObject(name)) {\r\n return name;\r\n }\r\n var obj = {};\r\n\r\n obj[name] = value;\r\n\r\n return obj;\r\n }\r\n\r\n return {};\r\n };\r\n\r\n var cast_types = {};\r\n\r\n cast_types['string'] = function (value) {\r\n return value + '';\r\n };\r\n cast_types['boolean'] = function (value) {\r\n return Boolean(value);\r\n };\r\n\r\n cast_types['number'] = function (value) {\r\n return Util.beNumber(value);\r\n };\r\n cast_types['integer'] = function (value) {\r\n return Math.floor(Util.beNumber(value));\r\n };\r\n cast_types['array'] = _.castArray;\r\n cast_types['object'] = function (value) {\r\n return Util.beObject(value);\r\n };\r\n\r\n /**\r\n * Convert array|object item to other type. Support types:\r\n * - string\r\n * - boolean\r\n * - number\r\n * - integer\r\n * - array\r\n * - object\r\n *\r\n * @param object\r\n * @param type\r\n * @return {Array|object}\r\n * @throws Error when cast type is unsupported\r\n */\r\n Util.castItemsType = function (object, type) {\r\n if (!cast_types.hasOwnProperty(type)) {\r\n throw new Error('Invalid cast type. Available types are: string, number, integer, array and object');\r\n }\r\n if (_.isArray(object)) {\r\n return _.map(_.clone(object), cast_types[type]);\r\n }\r\n\r\n return _.mapObject(_.clone(object), cast_types[type]);\r\n };\r\n\r\n /**\r\n * Loop over array or object like _.each but breakable\r\n * @param {object|Array} obj Loop object\r\n * @param {loopCallback} callback callback apply on every item, return break value to break the loop\r\n * @param {string} [break_on=break] Value of callback result that break the loop, default is 'break'\r\n * @returns {*}\r\n * @example\r\n * Khoai.util.loop([1,2,3,4,5], function(item){\r\n * console.log('Number', item);\r\n * if(item > 3){\r\n * return 'break';\r\n * }\r\n * });\r\n * //Console will log:\r\n * // Number 1\r\n * // Number 2\r\n * // Number 3\r\n * @example\r\n * Khoai.util.loop([1,2,3,4,5], function(item){\r\n * console.log('Number', item);\r\n * if(item > 3){\r\n * return 'yahoo';\r\n * }\r\n * }, 'yahoo');\r\n * //Console will log:\r\n * // Number 1\r\n * // Number 2\r\n * // Number 3\r\n *\r\n */\r\n Util.loop = function (obj, callback, break_on) {\r\n var i, length;\r\n if (_.isUndefined(break_on)) {\r\n break_on = 'break';\r\n }\r\n if (_.isArray(obj)) {\r\n for (i = 0, length = obj.length; i < length; i++) {\r\n if (callback(obj[i], i, obj) === break_on) {\r\n break;\r\n }\r\n }\r\n } else {\r\n var keys = _.keys(obj);\r\n for (i = 0, length = keys.length; i < length; i++) {\r\n if (callback(obj[keys[i]], keys[i], obj) === break_on) {\r\n break;\r\n }\r\n }\r\n }\r\n return obj;\r\n };\r\n\r\n var unique_id_current_status = {};\r\n\r\n /**\r\n * Return Next ID of type, start from 1\r\n * @param {string} [type=\"unique_id\"] Type of ID\r\n * @param {boolean} [type_as_prefix = true] Use type as prefix of return ID\r\n * @returns {string|number}\r\n * @example Default type\r\n * Khoai.util.nextID(); //unique_id_1\r\n * Khoai.util.nextID(); //unique_id_2\r\n * Khoai.util.nextID(null, false); //3\r\n * Khoai.util.nextID('superman'); //superman_1\r\n * Khoai.util.nextID('superman'); //superman_2\r\n * Khoai.util.nextID(); //unique_id_4\r\n * Khoai.util.nextID('superman', false); //3\r\n *\r\n */\r\n Util.nextID = function (type, type_as_prefix) {\r\n if (_.isEmpty(type)) {\r\n type = 'unique_id';\r\n }\r\n if (!unique_id_current_status.hasOwnProperty(type)) {\r\n unique_id_current_status[type] = 1;\r\n } else {\r\n unique_id_current_status[type]++;\r\n }\r\n\r\n return get_unique_id_with_prefix(type, unique_id_current_status[type], type_as_prefix);\r\n };\r\n\r\n /**\r\n * Return current ID of type\r\n * @param {string} [type=\"unique_id\"] Type of ID\r\n * @param {boolean} [type_as_prefix = true] Use type as prefix of return ID\r\n * @returns {boolean|string|number}\r\n * @example\r\n * Khoai.util.currentID(); //false\r\n * Khoai.util.nextID(); //unique_id_0\r\n * Khoai.util.nextID(); //unique_id_1\r\n * Khoai.util.currentID(); //unique_id_1\r\n * Khoai.util.currentID(null, false); //1\r\n * Khoai.util.currentID('superman'); //false\r\n * Khoai.util.nextID('superman'); //superman_0\r\n * Khoai.util.nextID('superman'); //superman_1\r\n * Khoai.util.currentID('superman'); //superman_1\r\n * Khoai.util.currentID('superman', false); //1\r\n * Khoai.util.nextID(); //2\r\n * Khoai.util.currentID(); //unique_id_2\r\n */\r\n Util.currentID = function (type, type_as_prefix) {\r\n if (_.isEmpty(type)) {\r\n type = 'unique_id';\r\n }\r\n\r\n if (!unique_id_current_status.hasOwnProperty(type)) {\r\n return false;\r\n }\r\n\r\n return get_unique_id_with_prefix(type, unique_id_current_status[type], type_as_prefix);\r\n };\r\n\r\n /**\r\n *\r\n * @param {string} [type] a type, do not require existed\r\n * @param {number} [value]\r\n * @returns {number|*}\r\n */\r\n Util.resetID = function (type, value) {\r\n if (!arguments.length) {\r\n type = 'unique_id';\r\n }\r\n\r\n type = String(type);\r\n\r\n if (_.isUndefined(value)) {\r\n delete unique_id_current_status[type];\r\n } else {\r\n value = arguments.length > 1 ? Util.beNumber(value) : 0;\r\n value = Math.max(value, 0);\r\n unique_id_current_status[type] = value;\r\n }\r\n\r\n return value;\r\n };\r\n\r\n function get_unique_id_with_prefix(type, id, type_as_prefix) {\r\n if (type_as_prefix || _.isUndefined(type_as_prefix)) {\r\n return type + '_' + id;\r\n }\r\n\r\n return id;\r\n }\r\n\r\n\r\n /**\r\n * Get constructor name of object\r\n * @param obj\r\n * @param {boolean} [constructor_only = false] Return object's constructor name only\r\n * @returns {string}\r\n * @example\r\n * Khoai.util.className(Khoai.App); //\"[object Object]\"\r\n * Khoai.util.className(Khoai.App, true); //App\r\n * Khoai.util.className(new Khoai.EventEmitter(), true); //EventEmitter\r\n */\r\n Util.className = function (obj, constructor_only) {\r\n if (constructor_only) {\r\n return obj.constructor.name;\r\n }\r\n return Object.prototype.toString.call(obj);\r\n };\r\n\r\n /**\r\n * Get type of content. If is object then return constructor name\r\n * @param content\r\n * @returns {string}\r\n * @example\r\n * Khoai.util.contentType(123); //number\r\n * Khoai.util.contentType('123'); //string\r\n * Khoai.util.contentType('Yahooooooo'); //string\r\n * Khoai.util.contentType(true); //boolean\r\n * Khoai.util.contentType(true); //boolean\r\n * Khoai.util.contentType([1,2]); //Array\r\n * Khoai.util.contentType({}); //Object\r\n * Khoai.util.contentType(_.App); //App\r\n */\r\n Util.contentType = function (content) {\r\n var type = typeof content;\r\n\r\n if (type === 'object') {\r\n var class_name = Util.className(content, true);\r\n\r\n if (class_name) {\r\n return class_name;\r\n }\r\n }\r\n\r\n return type;\r\n };\r\n\r\n /**\r\n * Check object is an instance of a constructor type\r\n * @param {*} obj\r\n * @param {string} class_name Name of class\r\n * @returns {boolean}\r\n * @example\r\n * Khoai.util.isInstanceOf(Khoai.App, 'App');//true\r\n * Khoai.util.isInstanceOf(123, 'Object'); //false\r\n * Khoai.util.isInstanceOf(123, 'Number'); //true\r\n * Khoai.util.isInstanceOf('123', 'String'); //true\r\n * Khoai.util.isInstanceOf(new Khoai.util.EventEmitter(), 'EventEmitter'); //true\r\n */\r\n Util.isInstanceOf = function (obj, class_name) {\r\n return Util.className(obj, true) === class_name;\r\n };\r\n\r\n /**\r\n * Check if object is primitive type: null, string, number, boolean\r\n * @param value\r\n * @returns {boolean}\r\n * @example\r\n * Khoai.util.isPrimitiveType(123); //true\r\n * Khoai.util.isPrimitiveType('123'); //true\r\n * Khoai.util.isPrimitiveType(null); //true\r\n * Khoai.util.isPrimitiveType(); //true\r\n * Khoai.util.isPrimitiveType(_.App); //false\r\n */\r\n Util.isPrimitiveType = function (value) {\r\n if (_.isObject(value)) {\r\n return false;\r\n }\r\n\r\n var type = typeof value;\r\n return value == null || type === 'string' || type === 'number' || type === 'boolean';\r\n };\r\n\r\n Util.mergeObject = function () {\r\n var next_index = 0;\r\n\r\n for (var i = 0, length = arguments.length; i < length; i++) {\r\n if (_.isArray(arguments[i]) || !_.isObject(arguments[i])) {\r\n arguments[i] = _.castArray(arguments[i]);\r\n arguments[i] = _.zipObject(_.range(next_index, next_index += arguments[i].length), arguments[i]);\r\n }\r\n }\r\n\r\n return _.extend.apply(_, arguments);\r\n };\r\n\r\n function is_diff_strict_cb(value_1, value_2) {\r\n return value_1 !== value_2;\r\n }\r\n\r\n function is_diff_loose_cb(value_1, value_2) {\r\n return value_1 != value_2;\r\n }\r\n\r\n /**\r\n * Get dirty of object with others object\r\n * @param {function} cb Callback return true if 2 item is difference\r\n * @param object\r\n * @param [others...]\r\n * @return {{}}\r\n */\r\n function diff_object(cb, object, others) {\r\n if (arguments.length < 2) {\r\n return {};\r\n }\r\n\r\n var result = {};\r\n\r\n if (!_.isFunction(cb)) {\r\n cb = cb ? is_diff_strict_cb : is_diff_loose_cb;\r\n }\r\n\r\n others = Util.mergeObject.apply(Util, slice.call(arguments, 2));\r\n\r\n _.each(object, function (value, key) {\r\n if (!others.hasOwnProperty(key)) {\r\n result[key] = value;\r\n } else {\r\n if (cb(value, others[key])) {\r\n result[key] = value;\r\n }\r\n }\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Get dirty of object with others object. Strict comparison\r\n * @param {object} object\r\n * @param {object} others\r\n * @return {*}\r\n * @example\r\n * Khoai.util.diffObject({a: 0, b: 1}, {a: '0', b: 1}); //{a: 0}\r\n */\r\n Util.diffObject = function (object, others) {\r\n var args = _.flatten(_.toArray(arguments));\r\n\r\n args.unshift(is_diff_strict_cb);\r\n\r\n return diff_object.apply(null, args);\r\n };\r\n\r\n /**\r\n * Get dirty of object with others object. Loose comparison\r\n * @param {object} object\r\n * @param {object} others\r\n * @return {*}\r\n * @example\r\n * Khoai.util.diffObjectLoose({a: 0, b: 1}, {a: '0', b: 2}); //{b: 1}\r\n */\r\n Util.diffObjectLoose = function (object, others) {\r\n var args = _.flatten(_.toArray(arguments));\r\n\r\n args.unshift(is_diff_loose_cb);\r\n\r\n return diff_object.apply(null, args);\r\n };\r\n\r\n /**\r\n * Get dirty of object with others object, use callback\r\n * @param {function} callback Callback with 2 parameters: base value, other object value. Return true when difference\r\n * @param {object} object\r\n * @param {object} others\r\n * @return {*}\r\n */\r\n Util.diffObjectWith = function (callback, object, others) {\r\n return diff_object.apply(null, slice.apply(arguments))\r\n };\r\n\r\n\r\n /**\r\n * Get random string\r\n * @param {number} [length]\r\n * @param {string} [chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ']\r\n * @returns {string}\r\n * @example\r\n * Khoai.util.randomString(10); //'mYJeC1xBcl'\r\n * Khoai.util.randomString(10, 'ABCDEF'); //'CDABBEFADE'\r\n */\r\n Util.randomString = function (length, chars) {\r\n var result = '', chars_length, i;\r\n if (_.isUndefined(chars) || !chars.toString().length) {\r\n chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\r\n }\r\n chars_length = chars.length;\r\n\r\n length = Util.beNumber(length, 10);\r\n\r\n for (i = length; i > 0; --i) {\r\n result += chars[Math.round(Math.random() * (chars_length - 1))];\r\n }\r\n return result;\r\n };\r\n\r\n\r\n /**\r\n * Setup a object with field name and value or object of fields\r\n * @param {Object} object host object\r\n * @param {(string|Object)} option field name of object of fields\r\n * @param {*} value value of field when option param is field name\r\n * @returns {{}}\r\n * @example\r\n * var obj = {a: 'A', b: 'B'}\r\n * Khoai.util.setup(obj, 'a', '123'); //obj -> {a: '123', b: 'B'}\r\n * Khoai.util.setup(obj, {b: 'Yahoo', c: 'ASD'}); //obj -> {a: 123, b: 'Yahoo', c: 'ASD'}\r\n */\r\n Util.setup = function (object, option, value) {\r\n if (!_.isObject(object)) {\r\n object = {};\r\n }\r\n if (_.isObject(option)) {\r\n _.each(option, function (val, path) {\r\n _.set(object, path, val);\r\n });\r\n } else {\r\n _.set(object, option, value);\r\n }\r\n\r\n return object;\r\n };\r\n\r\n /**\r\n * Get all of valid keys that exists in object.\r\n *\r\n * @param {object} object\r\n * @param {Array} keys\r\n * @return {Array}\r\n */\r\n Util.validKeys = function (object, keys) {\r\n var result = [];\r\n\r\n keys = _.castArray(keys);\r\n for (var i = 0, length = keys.length; i < length; i++) {\r\n if (object.hasOwnProperty(keys[i])) {\r\n result.push(keys[i]);\r\n }\r\n }\r\n\r\n return result;\r\n };\r\n\r\n\r\n /**\r\n * Like _.pairs but array item is an object with field is \"key\", \"value\"\r\n * @param {{}} object\r\n * @param {string} [key = 'key']\r\n * @param {string} [value = 'value']\r\n * @returns {Array}\r\n * @example\r\n * Khoai.util.pairsAsObject({one: 1, two: 2, three: 3});\r\n * => [{key: 'one', value: 1},{key: 'two', value: 2},{key: 'three', value: 3}]\r\n */\r\n Util.pairsAsObject = function (object, key, value) {\r\n var result = [],\r\n field_key = key || 'key',\r\n field_value = value || 'value';\r\n\r\n _.each(object, function (value, key) {\r\n var item = {};\r\n\r\n item[field_key] = key;\r\n item[field_value] = value;\r\n\r\n result.push(item);\r\n });\r\n\r\n return result;\r\n };\r\n\r\n /**\r\n * A convenient version of what is perhaps the most common use-case for map: extracting a list of property values, with a column as key.\r\n * @param {Array} collection\r\n * @param {string} key_field If key field not found then use as \"undefined\"\r\n * @param {string} value_field If value field not found then use as \"undefined\"\r\n * @returns {{}}\r\n * @example\r\n * var stooges = [{name: 'moe', id: 1, age: 40}, {name: 'larry', id: 2, age: 50}, {name: 'curly', id: 4, age: 60}];\r\n * Khoai.util.pluckBy(stooges, 'id', 'name');\r\n * => {1: 'moe', 2: 'larry', 3: 'curly'}\r\n */\r\n Util.pluckBy = function (collection, key_field, value_field) {\r\n var result = {};\r\n\r\n _.each(collection, function (object) {\r\n if (object.hasOwnProperty(key_field)) {\r\n result[object[key_field]] = object.hasOwnProperty(value_field) ? object[value_field] : undefined;\r\n }\r\n });\r\n\r\n return result;\r\n };\r\n\r\n /**\r\n * Check value is numeric\r\n * @param value\r\n * @returns {boolean}\r\n *\r\n * @example\r\n * Khoai.util.isNumeric(123); //true\r\n * Khoai.util.isNumeric(123.5); //true\r\n * Khoai.util.isNumeric('123.5'); //true\r\n */\r\n Util.isNumeric = function (value) {\r\n return !_.isArray(value) && (value - parseFloat(value) + 1) >= 0;\r\n };\r\n\r\n /**\r\n * Make sure value is in array\r\n * @param {*} value\r\n * @param {Array} values\r\n * @param {*} [default_value] Default value if not found value in values\r\n * @returns {*} If found then return value itself, else, return default_value or first item of array\r\n * @example\r\n * var items = [1,2,3,'a'];\r\n * Khoai.util.oneOf(1, items); // 1\r\n * Khoai.util.oneOf(0, items); // 1\r\n * Khoai.util.oneOf('a', items); // 'a'\r\n * Khoai.util.oneOf('b', items, 'C'); // 'C'\r\n */\r\n Util.oneOf = function (value, values, default_value) {\r\n if (-1 !== values.indexOf(value)) {\r\n return value;\r\n }\r\n\r\n if (arguments.length >= 3) {\r\n return default_value;\r\n }\r\n\r\n return _.first(values);\r\n };\r\n\r\n /**\r\n * Escape URL\r\n * @param {string} url\r\n * @param {boolean} [param = false] Include param?\r\n * @returns {string}\r\n */\r\n Util.escapeURL = function (url, param) {\r\n return param ? encodeURIComponent(url) : encodeURI(url);\r\n };\r\n\r\n /**\r\n * Unescape URL\r\n * @param {string} url\r\n * @param {boolean} [param = false] Include param?\r\n * @returns {string}\r\n */\r\n Util.unescapeURL = function (url, param) {\r\n return param ? decodeURI(url) : decodeURIComponent(url);\r\n };\r\n\r\n\r\n /**\r\n * Split array to n of chunks\r\n * @param {Array} array\r\n * @param {number} chunks Number of chunks\r\n * @return {Array}\r\n * @example\r\n * var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];\r\n * Khoai.util.chunks(arr, 3)\r\n * => [\r\n * [0, 1, 2, 3],\r\n * [4, 5, 6, 7],\r\n * [8, 9]\r\n * ]\r\n */\r\n Util.chunks = function (array, chunks) {\r\n return _.chunk(array, Math.ceil(array.length / chunks));\r\n };\r\n\r\n /**\r\n * Toggle array's elements\r\n * @param {Array} array\r\n * @param {Array} elements\r\n * @param {boolean} status If this param is boolean then add/remove base on it value. By default it is undefined -\r\n * add if none exists, remove if existed\r\n * @returns {Array}\r\n * @example\r\n * var arr = ['A', 'B', 'C', 'D'];\r\n * Khoai.util.toggle(arr, ['A', 'V']) => ['B', 'C', 'D', 'V']\r\n * Khoai.util.toggle(arr, ['A', 'V'], true) => ['A', 'B', 'C', 'D', 'V']\r\n * Khoai.util.toggle(arr, ['A', 'V'], false) => ['B', 'C', 'D']\r\n */\r\n Util.toggle = function (array, elements, status) {\r\n elements = _.uniq(_.castArray(elements));\r\n if (_.isUndefined(status)) {\r\n var exclude = _.intersection(array, elements);\r\n var include = _.difference(elements, array);\r\n\r\n array = _.union(_.difference(array, exclude), include);\r\n } else {\r\n if (status) {\r\n array = _.union(array, elements);\r\n } else {\r\n array = _.difference(array, elements);\r\n }\r\n }\r\n return array;\r\n };\r\n\r\n /**\r\n * Create, define and return a object with properties. Object's properties are fixed and enumerable\r\n * @param {{}} properties\r\n * @returns {{}}\r\n * @example\r\n * var obj = Khoai.util.defineObject({name: 'Manh', old: 123, hello: function(){\r\n * alert('Hello ' + this.name);\r\n * }});\r\n *\r\n * obj.old = 10;\r\n * console.log(obj); //{name: 'Manh', old: 123}\r\n * _.each(obj, function(value, key){\r\n * console.log(key, '=>', value);\r\n * });\r\n * //name => Manh\r\n * //old => 123\r\n */\r\n Util.defineObject = function (properties) {\r\n var obj = {};\r\n _.each(properties, function (value, key) {\r\n key = key.trim();\r\n if (!_.has(obj, key)) {\r\n Object.defineProperty(obj, key, {\r\n enumerable: !_.isFunction(value),\r\n value: value\r\n });\r\n }\r\n });\r\n return obj;\r\n };\r\n\r\n /**\r\n * Define a MaDnh constant\r\n * @param {object} target\r\n * @param {(string|Object)} name\r\n * @param {*} [value = undefined]\r\n * @example\r\n * Khoai.util.defineConstant(obj, 'TEST', 123) => obj.TEST = 123\r\n */\r\n Util.defineConstant = function (target, name, value) {\r\n var obj = {};\r\n\r\n if (_.isObject(name)) {\r\n obj = name;\r\n value = undefined;\r\n } else {\r\n obj[name] = value;\r\n }\r\n _.each(obj, function (val, key) {\r\n key = key.trim().toUpperCase();\r\n\r\n if (!target.hasOwnProperty(key)) {\r\n Object.defineProperty(target, key, {\r\n enumerable: true,\r\n value: val\r\n });\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * Inherit constructor prototype\r\n * @param {function} subclass_constructor Destination constructor\r\n * @param {function} base_class_constructor Source constructor\r\n * @param {boolean} [addSuper = true] Add property to destination prototype that reference back to source prototype\r\n *\r\n * @see https://github.com/Olical/Heir\r\n *\r\n * @example\r\n * function MyEE(){\r\n * M.EventEmitter.call(this);\r\n * }\r\n *\r\n * M.inherit(MyEE, M.EventEmitter);\r\n */\r\n Util.inherit = function (subclass_constructor, base_class_constructor, addSuper) {\r\n var proto = subclass_constructor.prototype = Object.create(base_class_constructor.prototype);\r\n proto.constructor = subclass_constructor;\r\n\r\n if (addSuper || _.isUndefined(addSuper)) {\r\n proto._super = base_class_constructor.prototype;\r\n }\r\n };\r\n\r\n /**\r\n * Call callback with arguments\r\n * @param {string|function|Array} callback\r\n * @param {*} [args] Callback arguments, if only one argument as array passed then it must be wrapped by array, eg:\r\n * [users]\r\n * @param {Object|null} context context of \"this\" keyword\r\n * @returns {*}\r\n *\r\n * @example\r\n * Khoai.util.callFunc(null, alert, 123);\r\n * Khoai.util.callFunc(null, function(name, old){\r\n * alert('My name is ' + name + ', ' + old + 'years old');\r\n * }, ['Manh', 10]);\r\n *\r\n * var obj = {name: 'Manh', old: 10};\r\n * Khoai.util.callFunc(obj, function(say_hi){\r\n * alert((say_hi ? 'Hi' : 'Hello') + '! my name is ' + this.name + ', ' + this.old + ' years old');\r\n * }, true);\r\n */\r\n Util.callFunc = function (callback, args, context) {\r\n if (arguments.length >= 2) {\r\n args = _.castArray(args);\r\n } else {\r\n args = [];\r\n }\r\n\r\n if (callback) {\r\n if (_.isFunction(callback)) {\r\n return callback.apply(context || null, args);\r\n } else if (_.isString(callback)) {\r\n if (window.hasOwnProperty(callback) && _.isFunction(window[callback])) {\r\n return window[callback].apply(context || null, args);\r\n }\r\n\r\n throw new Error('Invalid callback!');\r\n } else if (_.isArray(callback)) {\r\n var result = [],\r\n this_func = arguments.callee;\r\n\r\n _.each(callback, function (tmpFunc) {\r\n result.push(this_func(tmpFunc, args, context));\r\n });\r\n\r\n return result;\r\n }\r\n }\r\n\r\n return undefined;\r\n };\r\n\r\n /**\r\n * Call callback asynchronous. Similar to Khoai.util.callFunc\r\n *\r\n * @param {(string|function|Array)} callback\r\n * @param {*} [args] Callback arguments, if only one argument as array passed then it must be wrapped by array, eg:\r\n * [users]\r\n * @param {number} [delay=1] Delay milliseconds\r\n * @param {Object|null} context context of \"this\" keyword\r\n * @see callFunc\r\n */\r\n Util.async = function (callback, args, delay, context) {\r\n delay = parseInt(delay);\r\n if (_.isNaN(delay)) {\r\n delay = 1;\r\n }\r\n\r\n return setTimeout(function () {\r\n Util.callFunc(callback, args, context || null);\r\n }, Math.max(1, delay));\r\n };\r\n\r\n function createConsoleCB(name, description) {\r\n return function () {\r\n console[name].apply(console, (description ? [description] : []).concat(slice.apply(arguments)));\r\n }\r\n }\r\n\r\n /**\r\n * Like console.log with dynamic arguments\r\n * @example\r\n * var args = [1,2,3,4];\r\n * Khoai.util.logArgs('a',123);\r\n * Khoai.util.logArgs.apply(null, args);\r\n */\r\n Util.logArgs = function () {\r\n console.log.apply(console, slice.apply(arguments));\r\n };\r\n\r\n /**\r\n * Create console log callback with description as first arguments\r\n * @param {string} description\r\n * @returns {*}\r\n * @example\r\n * var cb = Khoai.util.logCb('Test 1');\r\n * cb(1,2,3); // Console log: 'Test 1' 1 2 3\r\n */\r\n Util.logCb = function (description) {\r\n return createConsoleCB.apply(null, ['log'].concat(slice.apply(arguments)));\r\n };\r\n\r\n /**\r\n * Like console.warn with dynamic arguments\r\n * @example\r\n * var args = [1,2,3,4];\r\n * Khoai.util.warnArgs('a',123);\r\n * Khoai.util.warnArgs.apply(null, args);\r\n */\r\n Util.warnArgs = function () {\r\n console.warn.apply(console, slice.apply(arguments));\r\n };\r\n\r\n /**\r\n * Create console waring callback with description as first arguments\r\n * @param {string} description\r\n * @returns {*}\r\n * @example\r\n * var cb = Khoai.util.warnCb('Test 1');\r\n * cb(1,2,3); // Console warn as: 'Test 1' 1 2 3\r\n */\r\n Util.warnCb = function (description) {\r\n return createConsoleCB.apply(null, ['warn'].concat(slice.apply(arguments)));\r\n };\r\n\r\n /**\r\n * Like console.error with dynamic arguments\r\n * @example\r\n * var args = [1,2,3,4];\r\n * Khoai.util.errorArgs('a',123);\r\n * Khoai.util.errorArgs.apply(null, args);\r\n */\r\n Util.errorArgs = function () {\r\n console.error.apply(console, slice.apply(arguments));\r\n };\r\n\r\n /**\r\n * Create console error callback with description as first arguments\r\n * @param {string} description\r\n * @returns {*}\r\n * @example\r\n * var cb = Khoai.util.errorCb('Test 1');\r\n * cb(1,2,3); // Console error as: 'Test 1' 1 2 3\r\n */\r\n Util.errorCb = function (description) {\r\n return createConsoleCB.apply(null, ['error'].concat(slice.apply(arguments)));\r\n };\r\n\r\n\r\n var debug_types_status = {},\r\n all_debugging = false;\r\n\r\n /**\r\n *\r\n * @param type\r\n * @returns {boolean}\r\n */\r\n Util.isDebugging = function (type) {\r\n if (all_debugging || _.isEmpty(type)) {\r\n return all_debugging;\r\n }\r\n\r\n return debug_types_status.hasOwnProperty(type) && debug_types_status[type];\r\n\r\n };\r\n /**\r\n *\r\n * @param [type] default is all debug type\r\n */\r\n Util.debugging = function (type) {\r\n if (_.isEmpty(type)) {\r\n all_debugging = true;\r\n return;\r\n }\r\n\r\n debug_types_status[type] = true;\r\n };\r\n /**\r\n *\r\n * @param [type] default is all debug type\r\n */\r\n Util.debugComplete = function (type) {\r\n if (_.isEmpty(type)) {\r\n all_debugging = false;\r\n debug_types_status = {};\r\n\r\n return;\r\n }\r\n\r\n delete debug_types_status[type];\r\n };\r\n\r\n /**\r\n * Run callback if is in debugging of a type\r\n * @param type null - all debug type\r\n * @param {function} callback\r\n */\r\n Util.onDebugging = function (type, callback) {\r\n if (Util.isDebugging(type)) {\r\n Util.callFunc(callback);\r\n }\r\n };\r\n\r\n /**\r\n * Get json string of an array\r\n * @param {array} details\r\n * @param {string} [glue=\"\\n\"]\r\n * @returns {string}\r\n */\r\n Util.getDebugString = function (details, glue) {\r\n var result = [];\r\n\r\n _.each(_.castArray(details), function (item) {\r\n result.push(JSON.stringify(item));\r\n });\r\n\r\n return result.join(glue || \"\\n\");\r\n };\r\n\r\n /**\r\n *\r\n * @param args\r\n * @param order\r\n * @param rules\r\n * @returns {{}}\r\n * @example\r\n * var order = ['int', 'bool', 'str'],\r\n * rules = {int: 'number', bool: 'boolean', str: 'string'};\r\n *\r\n * Khoai.util.optionalArgs([1, true, 'A'], order, rules); //{int: 1, bool: true, str: \"A\"}\r\n * Khoai.util.optionalArgs([true, 'A'], order, rules);//{bool: true, str: \"A\"}\r\n * Khoai.util.optionalArgs(['A'], order, rules); //{str: \"A\"}\r\n * Khoai.util.optionalArgs(['A', 'V'], order, rules); //{int: \"A\", bool: \"V\"}\r\n * Khoai.util.optionalArgs([1, []], order, rules); //{int: 1, bool: []}\r\n * Khoai.util.optionalArgs([true, []], order, rules); //{int: true, bool: []}\r\n * Khoai.util.optionalArgs(['A', []], order, rules); //{int: \"A\", bool: []}\r\n * Khoai.util.optionalArgs([[], []], order, rules); //{int: Array[0], bool: []}\r\n *\r\n * Khoai.util.optionalArgs(['A', 'V'], ['int', 'bool', 'str', 'str2'], {int: 'number', bool: 'boolean', str: 'string', str2: 'string'});\r\n * //=> {str: \"A\", str2: \"V\"}\r\n */\r\n Util.optionalArgs = function (args, order, rules) {\r\n var result = {},\r\n arg, index = 0, last_index, missing_rules, type, args_cloned, args_with_type, found;\r\n\r\n missing_rules = _.difference(order, Object.keys(rules));\r\n missing_rules.forEach(function (missing) {\r\n rules[missing] = true;\r\n });\r\n\r\n args_with_type = order.map(function (arg_name) {\r\n return rules[arg_name];\r\n });\r\n\r\n if (_.isEmpty(args)) {\r\n return result;\r\n }\r\n if (args.length >= order.length) {\r\n result = _.zipObject(order, args.slice(0, order.length));\r\n } else {\r\n args_cloned = args.slice(0);\r\n\r\n while (arg = args_cloned.shift()) {\r\n type = Util.contentType(arg);\r\n found = false;\r\n last_index = index;\r\n\r\n Util.loop(args_with_type.slice(index), (function (tmp_arg, tmp_type) {\r\n return function (types) {\r\n if (types === true || tmp_type === types\r\n || (_.isArray(types) && -1 != types.indexOf(tmp_type))\r\n || (_.isFunction(types) && types(tmp_arg))) {\r\n found = true;\r\n\r\n return 'break';\r\n }\r\n\r\n index++;\r\n }\r\n })(arg, type));\r\n\r\n if (!found) {\r\n result = _.zipObject(order.slice(0, args.length), args);\r\n break;\r\n }\r\n\r\n result[order[index++]] = arg;\r\n }\r\n }\r\n\r\n return result;\r\n };\r\n\r\n /**\r\n * Sort number asc\r\n */\r\n function sortNumberCallback(a, b) {\r\n return a - b;\r\n }\r\n\r\n /**\r\n * Sort number desc\r\n */\r\n function sortNumberDescCallback(a, b) {\r\n return b - a;\r\n }\r\n\r\n function is_equal_strict(a, b) {\r\n return a === b;\r\n }\r\n\r\n function is_equal_loose(a, b) {\r\n return a == b;\r\n }\r\n\r\n\r\n Util.defineConstant(Util, {\r\n /**\r\n * Array sort compare function. Sort number\r\n * @constant\r\n * @example\r\n * var scores = [1, 10, 2, 21];\r\n * scores.sort(); // [1, 10, 2, 21]\r\n * scores.sort(Khoai.util.SORT_NUMBER); // [1, 2, 10, 21]\r\n */\r\n SORT_NUMBER: sortNumberCallback,\r\n /**\r\n * Array sort compare function. Sort number desc\r\n * @constant\r\n * @example\r\n * var scores = [1, 10, 2, 21];\r\n * scores.sort(Khoai.util.SORT_NUMBER_DESC); // [21, 10, 2, 1]\r\n */\r\n SORT_NUMBER_DESC: sortNumberDescCallback\r\n });\r\n\r\n\r\n Khoai.util = Util;\r\n}));\r\n//# sourceMappingURL=khoai.js.map\r\n"]} \ No newline at end of file diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..1f7a925 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,43 @@ +var gulp = require('gulp'), + gulp_clean = require('gulp-clean'), + concat = require('gulp-concat'), + uglify = require('gulp-uglify'), + rename = require('gulp-rename'), + sourcemaps = require("gulp-sourcemaps"); + + +var folder = { + src: 'src', + dist: 'dist' +}; + +var khoaiScripts = [ + 'src/khoai.js', + 'src/util.js' +]; + +gulp.task('clean-dist-folder', function () { + return gulp.src(folder.dist, {read: false}) + .pipe(gulp_clean()); +}); + +gulp.task('khoai_js__concat', ['clean-dist-folder'], function () { + return gulp.src(khoaiScripts) + .pipe(sourcemaps.init()) + .pipe(concat('khoai.js')) + .pipe(sourcemaps.write('.')) + .pipe(gulp.dest(folder.dist)); +}); + +gulp.task('khoai_js__uglify', ['khoai_js__concat'], function () { + + return gulp.src(folder.dist + '/khoai.js') + .pipe(rename({suffix: '.min'})) + .pipe(sourcemaps.init()) + .pipe(uglify()) + .pipe(sourcemaps.write('.')) + .pipe(gulp.dest(folder.dist)); +}); + +gulp.task('khoai_js', ['khoai_js__concat', 'khoai_js__uglify']); +gulp.task('default', ['khoai_js']); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..d8e0558 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "khoaijs", + "version": "0.0.1", + "description": "", + "main": "khoaijs.js", + "directories": { + "doc": "docs" + }, + "scripts": { + "test": "mocha" + }, + "repository": { + "type": "git", + "url": "https://github.com/madnh/khoaijs.git" + }, + "keywords": [ + "KhoaiJS", + "KhoaiJS" + ], + "author": "MaDnh ", + "license": "MIT", + "homepage": "https://github.com/madnh/khoaijs", + "devDependencies": { + "gulp": "^3.9.1", + "gulp-clean": "^0.3.2", + "gulp-concat": "^2.6.1", + "gulp-rename": "^1.2.2", + "gulp-sourcemaps": "^2.6.0", + "gulp-uglify": "^2.1.2", + "mocha": "^3.0.2", + "chai": "^3.5.0", + "expect.js": "^0.3.1" + } +} diff --git a/src/khoai.js b/src/khoai.js new file mode 100644 index 0000000..2da7b1c --- /dev/null +++ b/src/khoai.js @@ -0,0 +1,71 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define([], function(){ + return (root.Khoai = factory()); + }); + } else { + // Browser globals + root.Khoai = factory(); + } +}(this, function () { + "use strict"; + + var Khoai = {}; + var proxyCounter = 0; + + /** + * Khoai's version + * @constant {string} VERSION + * @default + */ + var version = '0.0.1'; + + + Object.defineProperty(Khoai, 'VERSION', { + value: version + }); + + var BaseClass = function () { + this.proxiedMethods = {} + }; + + + /** + * + * @return {number} + */ + Khoai.getProxyCounter = function () { + return proxyCounter; + }; + + BaseClass.prototype.dispose = function () { + for (var key in this.proxiedMethods) { + if (this.proxiedMethods.hasOwnProperty(key)) { + this.proxiedMethods[key] = null + } + } + + this.proxiedMethods = {} + }; + + /* + * Creates a proxied method reference or returns an existing proxied method. + */ + BaseClass.prototype.proxy = function (method) { + if (method.khoaiProxyId === undefined) { + proxyCounter++; + method.khoaiProxyId = proxyCounter + } + + if (this.proxiedMethods[method.khoaiProxyId] !== undefined) + return this.proxiedMethods[method.khoaiProxyId]; + + this.proxiedMethods[method.khoaiProxyId] = method.bind(this); + + return this.proxiedMethods[method.khoaiProxyId] + }; + + Khoai.baseClass = BaseClass; + + return Khoai; +})); \ No newline at end of file diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..e844ba3 --- /dev/null +++ b/src/util.js @@ -0,0 +1,1133 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define(['lodash', 'khoai'], function (_, Khoai) { + factory(_, Khoai); + }); + } else { + factory(root._, root.Khoai) + } +}(this, function (_, Khoai) { + /* + |-------------------------------------------------------------------------- + | Type Definitions + |-------------------------------------------------------------------------- + */ + + /** + * Loop callback. Useful in _.each, _.map, _.omit,... + * @callback loopCallback + * @param value Item value + * @param key Item name/index + * @param object object of items + * + * @see http://underscorejs.org/#each + * @see http://underscorejs.org/#map + * @see http://underscorejs.org/#omit + * + * @example Alerts each number value in turn... + * _.each([1, 2, 3], alert); + * _.each({one: 1, two: 2, three: 3}, alert); + * @example Log to console each number value in turn... + * var logger = function(item, key, object){ + * console.log(key, '=>', item, '<------ Object', object); + * }; + * _.each([1, 2, 3], logger); + * _.each({one: 1, two: 2, three: 3}, logger); + * + */ + + /* + |-------------------------------------------------------------------------- + | Core + |-------------------------------------------------------------------------- + */ + + + var Util = {}; + var slice = Array.prototype.slice; + + /** + * Slice arguments of a function as array + * @param args + * @param {Number} [start] + * @param {Number} [end] + * @return {*} + */ + Util.sliceArguments = function (args, start, end) { + return slice.apply(args, slice.call(arguments, 1)); + }; + + Util.beNumber = function (value, default_value) { + value = parseFloat(value); + + if (Util.isNumeric(value)) { + return value; + } + if (_.isUndefined(default_value)) { + return 0; + } + + return Util.isNumeric(default_value) ? parseFloat(default_value) : Util.beNumber(default_value, 0); + }; + + /** + * Make sure first argument is object or arguments are name and value of object + * @param {*} [name] + * @param {*} [value] + * @returns {*} + * @example + * Khoai.util.beObject(); //{} + * Khoai.util.beObject(['foo', 'bar', 123]); //{0: "a", 1: 'bar', 2: 123} + * Khoai.util.beObject('yahoo'); //{0: "yahoo"} + * Khoai.util.beObject(235); //{0: 235} + * Khoai.util.beObject('yahoo', 123); //{yahoo: 123} + * Khoai.util.beObject({yahoo: 123, goooo:'ASDWd'}); //{yahoo: 123, goooo:'ASDWd'} + * + */ + Util.beObject = function (name, value) { + switch (true) { + case arguments.length == 1: + if (_.isObject(name)) { + return name; + } else if (_.isArray(name) || _.isArguments(name)) { + return _.zipObject(Object.keys(name), name); + } + + return {0: name}; + break; + + case arguments.length >= 2: + if (_.isObject(name)) { + return name; + } + var obj = {}; + + obj[name] = value; + + return obj; + } + + return {}; + }; + + var cast_types = {}; + + cast_types['string'] = function (value) { + return value + ''; + }; + cast_types['boolean'] = function (value) { + return Boolean(value); + }; + + cast_types['number'] = function (value) { + return Util.beNumber(value); + }; + cast_types['integer'] = function (value) { + return Math.floor(Util.beNumber(value)); + }; + cast_types['array'] = _.castArray; + cast_types['object'] = function (value) { + return Util.beObject(value); + }; + + /** + * Convert array|object item to other type. Support types: + * - string + * - boolean + * - number + * - integer + * - array + * - object + * + * @param object + * @param type + * @return {Array|object} + * @throws Error when cast type is unsupported + */ + Util.castItemsType = function (object, type) { + if (!cast_types.hasOwnProperty(type)) { + throw new Error('Invalid cast type. Available types are: string, number, integer, array and object'); + } + if (_.isArray(object)) { + return _.map(_.clone(object), cast_types[type]); + } + + return _.mapObject(_.clone(object), cast_types[type]); + }; + + /** + * Loop over array or object like _.each but breakable + * @param {object|Array} obj Loop object + * @param {loopCallback} callback callback apply on every item, return break value to break the loop + * @param {string} [break_on=break] Value of callback result that break the loop, default is 'break' + * @returns {*} + * @example + * Khoai.util.loop([1,2,3,4,5], function(item){ + * console.log('Number', item); + * if(item > 3){ + * return 'break'; + * } + * }); + * //Console will log: + * // Number 1 + * // Number 2 + * // Number 3 + * @example + * Khoai.util.loop([1,2,3,4,5], function(item){ + * console.log('Number', item); + * if(item > 3){ + * return 'yahoo'; + * } + * }, 'yahoo'); + * //Console will log: + * // Number 1 + * // Number 2 + * // Number 3 + * + */ + Util.loop = function (obj, callback, break_on) { + var i, length; + if (_.isUndefined(break_on)) { + break_on = 'break'; + } + if (_.isArray(obj)) { + for (i = 0, length = obj.length; i < length; i++) { + if (callback(obj[i], i, obj) === break_on) { + break; + } + } + } else { + var keys = _.keys(obj); + for (i = 0, length = keys.length; i < length; i++) { + if (callback(obj[keys[i]], keys[i], obj) === break_on) { + break; + } + } + } + return obj; + }; + + var unique_id_current_status = {}; + + /** + * Return Next ID of type, start from 1 + * @param {string} [type="unique_id"] Type of ID + * @param {boolean} [type_as_prefix = true] Use type as prefix of return ID + * @returns {string|number} + * @example Default type + * Khoai.util.nextID(); //unique_id_1 + * Khoai.util.nextID(); //unique_id_2 + * Khoai.util.nextID(null, false); //3 + * Khoai.util.nextID('superman'); //superman_1 + * Khoai.util.nextID('superman'); //superman_2 + * Khoai.util.nextID(); //unique_id_4 + * Khoai.util.nextID('superman', false); //3 + * + */ + Util.nextID = function (type, type_as_prefix) { + if (_.isEmpty(type)) { + type = 'unique_id'; + } + if (!unique_id_current_status.hasOwnProperty(type)) { + unique_id_current_status[type] = 1; + } else { + unique_id_current_status[type]++; + } + + return get_unique_id_with_prefix(type, unique_id_current_status[type], type_as_prefix); + }; + + /** + * Return current ID of type + * @param {string} [type="unique_id"] Type of ID + * @param {boolean} [type_as_prefix = true] Use type as prefix of return ID + * @returns {boolean|string|number} + * @example + * Khoai.util.currentID(); //false + * Khoai.util.nextID(); //unique_id_0 + * Khoai.util.nextID(); //unique_id_1 + * Khoai.util.currentID(); //unique_id_1 + * Khoai.util.currentID(null, false); //1 + * Khoai.util.currentID('superman'); //false + * Khoai.util.nextID('superman'); //superman_0 + * Khoai.util.nextID('superman'); //superman_1 + * Khoai.util.currentID('superman'); //superman_1 + * Khoai.util.currentID('superman', false); //1 + * Khoai.util.nextID(); //2 + * Khoai.util.currentID(); //unique_id_2 + */ + Util.currentID = function (type, type_as_prefix) { + if (_.isEmpty(type)) { + type = 'unique_id'; + } + + if (!unique_id_current_status.hasOwnProperty(type)) { + return false; + } + + return get_unique_id_with_prefix(type, unique_id_current_status[type], type_as_prefix); + }; + + /** + * + * @param {string} [type] a type, do not require existed + * @param {number} [value] + * @returns {number|*} + */ + Util.resetID = function (type, value) { + if (!arguments.length) { + type = 'unique_id'; + } + + type = String(type); + + if (_.isUndefined(value)) { + delete unique_id_current_status[type]; + } else { + value = arguments.length > 1 ? Util.beNumber(value) : 0; + value = Math.max(value, 0); + unique_id_current_status[type] = value; + } + + return value; + }; + + function get_unique_id_with_prefix(type, id, type_as_prefix) { + if (type_as_prefix || _.isUndefined(type_as_prefix)) { + return type + '_' + id; + } + + return id; + } + + + /** + * Get constructor name of object + * @param obj + * @param {boolean} [constructor_only = false] Return object's constructor name only + * @returns {string} + * @example + * Khoai.util.className(Khoai.App); //"[object Object]" + * Khoai.util.className(Khoai.App, true); //App + * Khoai.util.className(new Khoai.EventEmitter(), true); //EventEmitter + */ + Util.className = function (obj, constructor_only) { + if (constructor_only) { + return obj.constructor.name; + } + return Object.prototype.toString.call(obj); + }; + + /** + * Get type of content. If is object then return constructor name + * @param content + * @returns {string} + * @example + * Khoai.util.contentType(123); //number + * Khoai.util.contentType('123'); //string + * Khoai.util.contentType('Yahooooooo'); //string + * Khoai.util.contentType(true); //boolean + * Khoai.util.contentType(true); //boolean + * Khoai.util.contentType([1,2]); //Array + * Khoai.util.contentType({}); //Object + * Khoai.util.contentType(_.App); //App + */ + Util.contentType = function (content) { + var type = typeof content; + + if (type === 'object') { + var class_name = Util.className(content, true); + + if (class_name) { + return class_name; + } + } + + return type; + }; + + /** + * Check object is an instance of a constructor type + * @param {*} obj + * @param {string} class_name Name of class + * @returns {boolean} + * @example + * Khoai.util.isInstanceOf(Khoai.App, 'App');//true + * Khoai.util.isInstanceOf(123, 'Object'); //false + * Khoai.util.isInstanceOf(123, 'Number'); //true + * Khoai.util.isInstanceOf('123', 'String'); //true + * Khoai.util.isInstanceOf(new Khoai.util.EventEmitter(), 'EventEmitter'); //true + */ + Util.isInstanceOf = function (obj, class_name) { + return Util.className(obj, true) === class_name; + }; + + /** + * Check if object is primitive type: null, string, number, boolean + * @param value + * @returns {boolean} + * @example + * Khoai.util.isPrimitiveType(123); //true + * Khoai.util.isPrimitiveType('123'); //true + * Khoai.util.isPrimitiveType(null); //true + * Khoai.util.isPrimitiveType(); //true + * Khoai.util.isPrimitiveType(_.App); //false + */ + Util.isPrimitiveType = function (value) { + if (_.isObject(value)) { + return false; + } + + var type = typeof value; + return value == null || type === 'string' || type === 'number' || type === 'boolean'; + }; + + Util.mergeObject = function () { + var next_index = 0; + + for (var i = 0, length = arguments.length; i < length; i++) { + if (_.isArray(arguments[i]) || !_.isObject(arguments[i])) { + arguments[i] = _.castArray(arguments[i]); + arguments[i] = _.zipObject(_.range(next_index, next_index += arguments[i].length), arguments[i]); + } + } + + return _.extend.apply(_, arguments); + }; + + function is_diff_strict_cb(value_1, value_2) { + return value_1 !== value_2; + } + + function is_diff_loose_cb(value_1, value_2) { + return value_1 != value_2; + } + + /** + * Get dirty of object with others object + * @param {function} cb Callback return true if 2 item is difference + * @param object + * @param [others...] + * @return {{}} + */ + function diff_object(cb, object, others) { + if (arguments.length < 2) { + return {}; + } + + var result = {}; + + if (!_.isFunction(cb)) { + cb = cb ? is_diff_strict_cb : is_diff_loose_cb; + } + + others = Util.mergeObject.apply(Util, slice.call(arguments, 2)); + + _.each(object, function (value, key) { + if (!others.hasOwnProperty(key)) { + result[key] = value; + } else { + if (cb(value, others[key])) { + result[key] = value; + } + } + }); + + return result; + } + + /** + * Get dirty of object with others object. Strict comparison + * @param {object} object + * @param {object} others + * @return {*} + * @example + * Khoai.util.diffObject({a: 0, b: 1}, {a: '0', b: 1}); //{a: 0} + */ + Util.diffObject = function (object, others) { + var args = _.flatten(_.toArray(arguments)); + + args.unshift(is_diff_strict_cb); + + return diff_object.apply(null, args); + }; + + /** + * Get dirty of object with others object. Loose comparison + * @param {object} object + * @param {object} others + * @return {*} + * @example + * Khoai.util.diffObjectLoose({a: 0, b: 1}, {a: '0', b: 2}); //{b: 1} + */ + Util.diffObjectLoose = function (object, others) { + var args = _.flatten(_.toArray(arguments)); + + args.unshift(is_diff_loose_cb); + + return diff_object.apply(null, args); + }; + + /** + * Get dirty of object with others object, use callback + * @param {function} callback Callback with 2 parameters: base value, other object value. Return true when difference + * @param {object} object + * @param {object} others + * @return {*} + */ + Util.diffObjectWith = function (callback, object, others) { + return diff_object.apply(null, slice.apply(arguments)) + }; + + + /** + * Get random string + * @param {number} [length] + * @param {string} [chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'] + * @returns {string} + * @example + * Khoai.util.randomString(10); //'mYJeC1xBcl' + * Khoai.util.randomString(10, 'ABCDEF'); //'CDABBEFADE' + */ + Util.randomString = function (length, chars) { + var result = '', chars_length, i; + if (_.isUndefined(chars) || !chars.toString().length) { + chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + } + chars_length = chars.length; + + length = Util.beNumber(length, 10); + + for (i = length; i > 0; --i) { + result += chars[Math.round(Math.random() * (chars_length - 1))]; + } + return result; + }; + + + /** + * Setup a object with field name and value or object of fields + * @param {Object} object host object + * @param {(string|Object)} option field name of object of fields + * @param {*} value value of field when option param is field name + * @returns {{}} + * @example + * var obj = {a: 'A', b: 'B'} + * Khoai.util.setup(obj, 'a', '123'); //obj -> {a: '123', b: 'B'} + * Khoai.util.setup(obj, {b: 'Yahoo', c: 'ASD'}); //obj -> {a: 123, b: 'Yahoo', c: 'ASD'} + */ + Util.setup = function (object, option, value) { + if (!_.isObject(object)) { + object = {}; + } + if (_.isObject(option)) { + _.each(option, function (val, path) { + _.set(object, path, val); + }); + } else { + _.set(object, option, value); + } + + return object; + }; + + /** + * Get all of valid keys that exists in object. + * + * @param {object} object + * @param {Array} keys + * @return {Array} + */ + Util.validKeys = function (object, keys) { + var result = []; + + keys = _.castArray(keys); + for (var i = 0, length = keys.length; i < length; i++) { + if (object.hasOwnProperty(keys[i])) { + result.push(keys[i]); + } + } + + return result; + }; + + + /** + * Like _.pairs but array item is an object with field is "key", "value" + * @param {{}} object + * @param {string} [key = 'key'] + * @param {string} [value = 'value'] + * @returns {Array} + * @example + * Khoai.util.pairsAsObject({one: 1, two: 2, three: 3}); + * => [{key: 'one', value: 1},{key: 'two', value: 2},{key: 'three', value: 3}] + */ + Util.pairsAsObject = function (object, key, value) { + var result = [], + field_key = key || 'key', + field_value = value || 'value'; + + _.each(object, function (value, key) { + var item = {}; + + item[field_key] = key; + item[field_value] = value; + + result.push(item); + }); + + return result; + }; + + /** + * A convenient version of what is perhaps the most common use-case for map: extracting a list of property values, with a column as key. + * @param {Array} collection + * @param {string} key_field If key field not found then use as "undefined" + * @param {string} value_field If value field not found then use as "undefined" + * @returns {{}} + * @example + * var stooges = [{name: 'moe', id: 1, age: 40}, {name: 'larry', id: 2, age: 50}, {name: 'curly', id: 4, age: 60}]; + * Khoai.util.pluckBy(stooges, 'id', 'name'); + * => {1: 'moe', 2: 'larry', 3: 'curly'} + */ + Util.pluckBy = function (collection, key_field, value_field) { + var result = {}; + + _.each(collection, function (object) { + if (object.hasOwnProperty(key_field)) { + result[object[key_field]] = object.hasOwnProperty(value_field) ? object[value_field] : undefined; + } + }); + + return result; + }; + + /** + * Check value is numeric + * @param value + * @returns {boolean} + * + * @example + * Khoai.util.isNumeric(123); //true + * Khoai.util.isNumeric(123.5); //true + * Khoai.util.isNumeric('123.5'); //true + */ + Util.isNumeric = function (value) { + return !_.isArray(value) && (value - parseFloat(value) + 1) >= 0; + }; + + /** + * Make sure value is in array + * @param {*} value + * @param {Array} values + * @param {*} [default_value] Default value if not found value in values + * @returns {*} If found then return value itself, else, return default_value or first item of array + * @example + * var items = [1,2,3,'a']; + * Khoai.util.oneOf(1, items); // 1 + * Khoai.util.oneOf(0, items); // 1 + * Khoai.util.oneOf('a', items); // 'a' + * Khoai.util.oneOf('b', items, 'C'); // 'C' + */ + Util.oneOf = function (value, values, default_value) { + if (-1 !== values.indexOf(value)) { + return value; + } + + if (arguments.length >= 3) { + return default_value; + } + + return _.first(values); + }; + + /** + * Escape URL + * @param {string} url + * @param {boolean} [param = false] Include param? + * @returns {string} + */ + Util.escapeURL = function (url, param) { + return param ? encodeURIComponent(url) : encodeURI(url); + }; + + /** + * Unescape URL + * @param {string} url + * @param {boolean} [param = false] Include param? + * @returns {string} + */ + Util.unescapeURL = function (url, param) { + return param ? decodeURI(url) : decodeURIComponent(url); + }; + + + /** + * Split array to n of chunks + * @param {Array} array + * @param {number} chunks Number of chunks + * @return {Array} + * @example + * var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + * Khoai.util.chunks(arr, 3) + * => [ + * [0, 1, 2, 3], + * [4, 5, 6, 7], + * [8, 9] + * ] + */ + Util.chunks = function (array, chunks) { + return _.chunk(array, Math.ceil(array.length / chunks)); + }; + + /** + * Toggle array's elements + * @param {Array} array + * @param {Array} elements + * @param {boolean} status If this param is boolean then add/remove base on it value. By default it is undefined - + * add if none exists, remove if existed + * @returns {Array} + * @example + * var arr = ['A', 'B', 'C', 'D']; + * Khoai.util.toggle(arr, ['A', 'V']) => ['B', 'C', 'D', 'V'] + * Khoai.util.toggle(arr, ['A', 'V'], true) => ['A', 'B', 'C', 'D', 'V'] + * Khoai.util.toggle(arr, ['A', 'V'], false) => ['B', 'C', 'D'] + */ + Util.toggle = function (array, elements, status) { + elements = _.uniq(_.castArray(elements)); + if (_.isUndefined(status)) { + var exclude = _.intersection(array, elements); + var include = _.difference(elements, array); + + array = _.union(_.difference(array, exclude), include); + } else { + if (status) { + array = _.union(array, elements); + } else { + array = _.difference(array, elements); + } + } + return array; + }; + + /** + * Create, define and return a object with properties. Object's properties are fixed and enumerable + * @param {{}} properties + * @returns {{}} + * @example + * var obj = Khoai.util.defineObject({name: 'Manh', old: 123, hello: function(){ + * alert('Hello ' + this.name); + * }}); + * + * obj.old = 10; + * console.log(obj); //{name: 'Manh', old: 123} + * _.each(obj, function(value, key){ + * console.log(key, '=>', value); + * }); + * //name => Manh + * //old => 123 + */ + Util.defineObject = function (properties) { + var obj = {}; + _.each(properties, function (value, key) { + key = key.trim(); + if (!_.has(obj, key)) { + Object.defineProperty(obj, key, { + enumerable: !_.isFunction(value), + value: value + }); + } + }); + return obj; + }; + + /** + * Define a MaDnh constant + * @param {object} target + * @param {(string|Object)} name + * @param {*} [value = undefined] + * @example + * Khoai.util.defineConstant(obj, 'TEST', 123) => obj.TEST = 123 + */ + Util.defineConstant = function (target, name, value) { + var obj = {}; + + if (_.isObject(name)) { + obj = name; + value = undefined; + } else { + obj[name] = value; + } + _.each(obj, function (val, key) { + key = key.trim().toUpperCase(); + + if (!target.hasOwnProperty(key)) { + Object.defineProperty(target, key, { + enumerable: true, + value: val + }); + } + }); + }; + + /** + * Inherit constructor prototype + * @param {function} subclass_constructor Destination constructor + * @param {function} base_class_constructor Source constructor + * @param {boolean} [addSuper = true] Add property to destination prototype that reference back to source prototype + * + * @see https://github.com/Olical/Heir + * + * @example + * function MyEE(){ + * M.EventEmitter.call(this); + * } + * + * M.inherit(MyEE, M.EventEmitter); + */ + Util.inherit = function (subclass_constructor, base_class_constructor, addSuper) { + var proto = subclass_constructor.prototype = Object.create(base_class_constructor.prototype); + proto.constructor = subclass_constructor; + + if (addSuper || _.isUndefined(addSuper)) { + proto._super = base_class_constructor.prototype; + } + }; + + /** + * Call callback with arguments + * @param {string|function|Array} callback + * @param {*} [args] Callback arguments, if only one argument as array passed then it must be wrapped by array, eg: + * [users] + * @param {Object|null} context context of "this" keyword + * @returns {*} + * + * @example + * Khoai.util.callFunc(null, alert, 123); + * Khoai.util.callFunc(null, function(name, old){ + * alert('My name is ' + name + ', ' + old + 'years old'); + * }, ['Manh', 10]); + * + * var obj = {name: 'Manh', old: 10}; + * Khoai.util.callFunc(obj, function(say_hi){ + * alert((say_hi ? 'Hi' : 'Hello') + '! my name is ' + this.name + ', ' + this.old + ' years old'); + * }, true); + */ + Util.callFunc = function (callback, args, context) { + if (arguments.length >= 2) { + args = _.castArray(args); + } else { + args = []; + } + + if (callback) { + if (_.isFunction(callback)) { + return callback.apply(context || null, args); + } else if (_.isString(callback)) { + if (window.hasOwnProperty(callback) && _.isFunction(window[callback])) { + return window[callback].apply(context || null, args); + } + + throw new Error('Invalid callback!'); + } else if (_.isArray(callback)) { + var result = [], + this_func = arguments.callee; + + _.each(callback, function (tmpFunc) { + result.push(this_func(tmpFunc, args, context)); + }); + + return result; + } + } + + return undefined; + }; + + /** + * Call callback asynchronous. Similar to Khoai.util.callFunc + * + * @param {(string|function|Array)} callback + * @param {*} [args] Callback arguments, if only one argument as array passed then it must be wrapped by array, eg: + * [users] + * @param {number} [delay=1] Delay milliseconds + * @param {Object|null} context context of "this" keyword + * @see callFunc + */ + Util.async = function (callback, args, delay, context) { + delay = parseInt(delay); + if (_.isNaN(delay)) { + delay = 1; + } + + return setTimeout(function () { + Util.callFunc(callback, args, context || null); + }, Math.max(1, delay)); + }; + + function createConsoleCB(name, description) { + return function () { + console[name].apply(console, (description ? [description] : []).concat(slice.apply(arguments))); + } + } + + /** + * Like console.log with dynamic arguments + * @example + * var args = [1,2,3,4]; + * Khoai.util.logArgs('a',123); + * Khoai.util.logArgs.apply(null, args); + */ + Util.logArgs = function () { + console.log.apply(console, slice.apply(arguments)); + }; + + /** + * Create console log callback with description as first arguments + * @param {string} description + * @returns {*} + * @example + * var cb = Khoai.util.logCb('Test 1'); + * cb(1,2,3); // Console log: 'Test 1' 1 2 3 + */ + Util.logCb = function (description) { + return createConsoleCB.apply(null, ['log'].concat(slice.apply(arguments))); + }; + + /** + * Like console.warn with dynamic arguments + * @example + * var args = [1,2,3,4]; + * Khoai.util.warnArgs('a',123); + * Khoai.util.warnArgs.apply(null, args); + */ + Util.warnArgs = function () { + console.warn.apply(console, slice.apply(arguments)); + }; + + /** + * Create console waring callback with description as first arguments + * @param {string} description + * @returns {*} + * @example + * var cb = Khoai.util.warnCb('Test 1'); + * cb(1,2,3); // Console warn as: 'Test 1' 1 2 3 + */ + Util.warnCb = function (description) { + return createConsoleCB.apply(null, ['warn'].concat(slice.apply(arguments))); + }; + + /** + * Like console.error with dynamic arguments + * @example + * var args = [1,2,3,4]; + * Khoai.util.errorArgs('a',123); + * Khoai.util.errorArgs.apply(null, args); + */ + Util.errorArgs = function () { + console.error.apply(console, slice.apply(arguments)); + }; + + /** + * Create console error callback with description as first arguments + * @param {string} description + * @returns {*} + * @example + * var cb = Khoai.util.errorCb('Test 1'); + * cb(1,2,3); // Console error as: 'Test 1' 1 2 3 + */ + Util.errorCb = function (description) { + return createConsoleCB.apply(null, ['error'].concat(slice.apply(arguments))); + }; + + + var debug_types_status = {}, + all_debugging = false; + + /** + * + * @param type + * @returns {boolean} + */ + Util.isDebugging = function (type) { + if (all_debugging || _.isEmpty(type)) { + return all_debugging; + } + + return debug_types_status.hasOwnProperty(type) && debug_types_status[type]; + + }; + /** + * + * @param [type] default is all debug type + */ + Util.debugging = function (type) { + if (_.isEmpty(type)) { + all_debugging = true; + return; + } + + debug_types_status[type] = true; + }; + /** + * + * @param [type] default is all debug type + */ + Util.debugComplete = function (type) { + if (_.isEmpty(type)) { + all_debugging = false; + debug_types_status = {}; + + return; + } + + delete debug_types_status[type]; + }; + + /** + * Run callback if is in debugging of a type + * @param type null - all debug type + * @param {function} callback + */ + Util.onDebugging = function (type, callback) { + if (Util.isDebugging(type)) { + Util.callFunc(callback); + } + }; + + /** + * Get json string of an array + * @param {array} details + * @param {string} [glue="\n"] + * @returns {string} + */ + Util.getDebugString = function (details, glue) { + var result = []; + + _.each(_.castArray(details), function (item) { + result.push(JSON.stringify(item)); + }); + + return result.join(glue || "\n"); + }; + + /** + * + * @param args + * @param order + * @param rules + * @returns {{}} + * @example + * var order = ['int', 'bool', 'str'], + * rules = {int: 'number', bool: 'boolean', str: 'string'}; + * + * Khoai.util.optionalArgs([1, true, 'A'], order, rules); //{int: 1, bool: true, str: "A"} + * Khoai.util.optionalArgs([true, 'A'], order, rules);//{bool: true, str: "A"} + * Khoai.util.optionalArgs(['A'], order, rules); //{str: "A"} + * Khoai.util.optionalArgs(['A', 'V'], order, rules); //{int: "A", bool: "V"} + * Khoai.util.optionalArgs([1, []], order, rules); //{int: 1, bool: []} + * Khoai.util.optionalArgs([true, []], order, rules); //{int: true, bool: []} + * Khoai.util.optionalArgs(['A', []], order, rules); //{int: "A", bool: []} + * Khoai.util.optionalArgs([[], []], order, rules); //{int: Array[0], bool: []} + * + * Khoai.util.optionalArgs(['A', 'V'], ['int', 'bool', 'str', 'str2'], {int: 'number', bool: 'boolean', str: 'string', str2: 'string'}); + * //=> {str: "A", str2: "V"} + */ + Util.optionalArgs = function (args, order, rules) { + var result = {}, + arg, index = 0, last_index, missing_rules, type, args_cloned, args_with_type, found; + + missing_rules = _.difference(order, Object.keys(rules)); + missing_rules.forEach(function (missing) { + rules[missing] = true; + }); + + args_with_type = order.map(function (arg_name) { + return rules[arg_name]; + }); + + if (_.isEmpty(args)) { + return result; + } + if (args.length >= order.length) { + result = _.zipObject(order, args.slice(0, order.length)); + } else { + args_cloned = args.slice(0); + + while (arg = args_cloned.shift()) { + type = Util.contentType(arg); + found = false; + last_index = index; + + Util.loop(args_with_type.slice(index), (function (tmp_arg, tmp_type) { + return function (types) { + if (types === true || tmp_type === types + || (_.isArray(types) && -1 != types.indexOf(tmp_type)) + || (_.isFunction(types) && types(tmp_arg))) { + found = true; + + return 'break'; + } + + index++; + } + })(arg, type)); + + if (!found) { + result = _.zipObject(order.slice(0, args.length), args); + break; + } + + result[order[index++]] = arg; + } + } + + return result; + }; + + /** + * Sort number asc + */ + function sortNumberCallback(a, b) { + return a - b; + } + + /** + * Sort number desc + */ + function sortNumberDescCallback(a, b) { + return b - a; + } + + function is_equal_strict(a, b) { + return a === b; + } + + function is_equal_loose(a, b) { + return a == b; + } + + + Util.defineConstant(Util, { + /** + * Array sort compare function. Sort number + * @constant + * @example + * var scores = [1, 10, 2, 21]; + * scores.sort(); // [1, 10, 2, 21] + * scores.sort(Khoai.util.SORT_NUMBER); // [1, 2, 10, 21] + */ + SORT_NUMBER: sortNumberCallback, + /** + * Array sort compare function. Sort number desc + * @constant + * @example + * var scores = [1, 10, 2, 21]; + * scores.sort(Khoai.util.SORT_NUMBER_DESC); // [21, 10, 2, 1] + */ + SORT_NUMBER_DESC: sortNumberDescCallback + }); + + + Khoai.util = Util; +})); \ No newline at end of file diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..33a1624 --- /dev/null +++ b/test/index.html @@ -0,0 +1,27 @@ + + + + KhoaiJS Flag tests + + + +Khoai + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..a8368aa --- /dev/null +++ b/test/test.js @@ -0,0 +1,598 @@ +describe('KhoaiJS', function () { + var expect = chai.expect, + chai_assert = chai.assert; + + describe('STATIC PROPERTIES', function () { + it('VERSION', function () { + expect(Khoai).to.have.property('VERSION'); + }); + + describe('SORT_NUMBER', function () { + it('Array of numbers', function () { + var scores = [1, 10, 2, 21]; + scores.sort(Khoai.util.SORT_NUMBER); + + chai_assert.deepEqual(scores, [1, 2, 10, 21]); + }); + + it('Array of numbers and characters', function () { + var scores = [1, 10, 'A', 2, 21]; + scores.sort(Khoai.util.SORT_NUMBER); + + chai_assert.deepEqual(scores, [1, 10, 'A', 2, 21]); + }); + }); + + describe('SORT_NUMBER_DESC', function () { + it('Array of numbers', function () { + var scores = [21, 10, 2, 1]; + scores.sort(Khoai.util.SORT_NUMBER_DESC); + + chai_assert.deepEqual(scores, [21, 10, 2, 1]); + }); + + it('Array of numbers and characters', function () { + var scores = [21, 10, 'A', 2, 1]; + scores.sort(Khoai.util.SORT_NUMBER_DESC); + + chai_assert.deepEqual(scores, [21, 10, 'A', 2, 1]); + }); + }); + + }); + + describe('Functions', function () { + describe('Debugging', function () { + beforeEach(function () { + Khoai.util.debugComplete('test'); + Khoai.util.debugComplete(); + }); + + it('debugging and debugComplete', function () { + Khoai.util.debugging('test'); + chai_assert.isTrue(Khoai.util.isDebugging('test')); + Khoai.util.debugComplete('test'); + chai_assert.isFalse(Khoai.util.isDebugging('test')); + }); + + it('on debugging', function (done) { + Khoai.util.debugging('test'); + Khoai.util.onDebugging('test', done); + }); + + it('if not debugging', function (done) { + Khoai.util.debugComplete('test'); + + if (Khoai.util.isDebugging('test')) { + done('it must be by pass this'); + } else { + done(); + } + }); + + }); + describe('Khoai.util.beNumber', function () { + it('Parameter is number', function () { + expect(Khoai.util.beNumber(123)).to.be.a('number').with.equal(123); + }); + it('Parameter is number, with default value', function () { + expect(Khoai.util.beNumber(123, 567)).to.be.a('number').with.equal(123); + }); + + it('Parameter is string of number', function () { + expect(Khoai.util.beNumber('123')).to.be.a('number').with.equal(123); + }); + it('Parameter is string of number, with default value', function () { + expect(Khoai.util.beNumber('123', 567)).to.be.a('number').with.equal(123); + }); + + it('Parameter isn\'t a number, with default value', function () { + expect(Khoai.util.beNumber(true, 567)).to.be.a('number').with.equal(567); + expect(Khoai.util.beNumber([], 567)).to.be.a('number').with.equal(567); + expect(Khoai.util.beNumber(new Array(), 567)).to.be.a('number').with.equal(567); + }); + }); + describe('Khoai.util.beObject', function () { + it('Parameter is empty', function () { + var obj = Khoai.util.beObject(); + // + chai_assert.isObject(obj); + }); + it('Parameter is string', function () { + var obj = Khoai.util.beObject('yahoo'); + // + chai_assert.isObject(obj); + }); + it('Parameter is number', function () { + var result = Khoai.util.beObject(123); + // + chai_assert.isObject(result); + }); + it('Parameters is string and number', function () { + var obj = Khoai.util.beObject('yahoo', 123); + // + chai_assert.isObject(obj); + chai_assert.property(obj, 'yahoo', 'Object missing key'); + chai_assert.propertyVal(obj, 'yahoo', 123, 'Wrong object value'); + }); + + + }); + describe('Khoai.util.callFunc', function () { + it('Call function object', function () { + function test(old) { + return old + 1; + } + + // + chai_assert.strictEqual(Khoai.util.callFunc(test, 10), 11); + }); + it('Call function as string', function () { + window.func_as_string = function (old) { + return old + 1; + }; + // + chai_assert.doesNotThrow(function () { + Khoai.util.callFunc('func_as_string', 10); + }); + }); + it('Call callback with first parameter is array', function () { + function test(arr) { + arr.push(4); + + return arr; + } + + // + var result = Khoai.util.callFunc(test, [[1, 2, 3]]); + // + chai_assert.isArray(result); + chai_assert.deepEqual(result, [1, 2, 3, 4]); + }); + it('Call function with context', function () { + var obj = {name: 'Manh'}; + // + function say_hi() { + return 'Hi, ' + this.name; + } + + var result = Khoai.util.callFunc(say_hi, [], obj); + // + chai_assert.strictEqual(result, 'Hi, Manh'); + }); + }); + describe('Khoai.util.castItemsType', function () { + var arr = []; + + beforeEach(function () { + arr = [1, 2, 'ABC', 0, false, {'name': 'Manh'}]; + }); + + it('cast to string', function () { + chai_assert.deepEqual(Khoai.util.castItemsType(arr, 'string'), ['1', '2', 'ABC', '0', 'false', "[object Object]"]); + }); + it('cast to number', function () { + chai_assert.deepEqual(Khoai.util.castItemsType(arr, 'number'), [1, 2, 0, 0, 0, 0]); + }); + it('cast to boolean', function () { + chai_assert.deepEqual(Khoai.util.castItemsType(arr, 'boolean'), [true, true, true, false, false, true]); + }); + it('cast to array', function () { + var cast_result = Khoai.util.castItemsType(arr, 'array'), + result_expect = [[1], [2], ['ABC'], [0], [false], [{'name': 'Manh'}]]; + + chai_assert.isArray(cast_result); + + for (var i = 0, len = cast_result.length; i < len; i++) { + if (cast_result.hasOwnProperty(i)) { + chai_assert.deepEqual(cast_result[i], result_expect[i]); + } + } + }); + it('cast to object', function () { + var cast_result = Khoai.util.castItemsType(arr, 'object'), + result_expect = [{0: 1}, {0: 2}, {0: 'ABC'}, {0: 0}, {0: false}, {'name': 'Manh'}]; + + chai_assert.isArray(cast_result); + + for (var i = 0, len = cast_result.length; i < len; i++) { + if (cast_result.hasOwnProperty(i)) { + chai_assert.deepEqual(cast_result[i], result_expect[i]); + } + } + }); + }); + + describe('Khoai.util.chunks', function () { + var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + chunked = Khoai.util.chunks(arr, 3); + + it('test', function () { + chai_assert.isArray(chunked); + chai_assert.lengthOf(chunked, 3); + chai_assert.deepEqual(chunked[0], [0, 1, 2, 3]); + chai_assert.deepEqual(chunked[1], [4, 5, 6, 7]); + chai_assert.deepEqual(chunked[2], [8, 9]); + }); + + }); + describe('Khoai.util.className', function () { + it('Default', function () { + chai_assert.strictEqual(Khoai.util.className(new Object()), '[object Object]'); + }); + it('Constructor only', function () { + function ABC() { + // + } + + chai_assert.strictEqual(Khoai.util.className(new ABC(), true), 'ABC'); + }); + }); + describe('Khoai.util.contentType', function () { + it('test', function () { + function ABC() { + + } + chai_assert.strictEqual(Khoai.util.contentType(123), 'number'); + chai_assert.strictEqual(Khoai.util.contentType('123'), 'string'); + chai_assert.strictEqual(Khoai.util.contentType('Yahooooooo'), 'string'); + chai_assert.strictEqual(Khoai.util.contentType(true), 'boolean'); + chai_assert.strictEqual(Khoai.util.contentType([1, 2]), 'Array'); + chai_assert.strictEqual(Khoai.util.contentType({color: 'red'}), 'Object'); + chai_assert.strictEqual(Khoai.util.contentType(new ABC()), 'ABC'); + }); + }); + describe('Khoai.util.currentID', function () { + before(function () { + Khoai.util.resetID(); + Khoai.util.resetID('superman'); + }); + + it('Default type', function () { + chai_assert.isFalse(Khoai.util.currentID()); + chai_assert.isFalse(Khoai.util.currentID(null)); + Khoai.util.nextID(); + chai_assert.strictEqual(Khoai.util.currentID(), 'unique_id_1'); + chai_assert.strictEqual(Khoai.util.currentID(null), 'unique_id_1'); + chai_assert.strictEqual(Khoai.util.currentID(null, false), 1); + }); + + it('Custom type', function () { + chai_assert.isFalse(Khoai.util.currentID('superman')); + Khoai.util.nextID('superman'); + chai_assert.strictEqual(Khoai.util.currentID('superman'), 'superman_1'); + chai_assert.strictEqual(Khoai.util.currentID('superman', false), 1); + }); + + }); + describe('Khoai.util.defineConstant', function () { + it('Define constant by name and value', function () { + var obj = {}; + Khoai.util.defineConstant(obj, 'TEST_DEFINECONSTANT', 123); + chai_assert.property(obj, 'TEST_DEFINECONSTANT'); + chai_assert.propertyVal(obj, 'TEST_DEFINECONSTANT', 123); + }); + it('Define constant by name (LOWERCASE) and value', function () { + var obj = {}; + Khoai.util.defineConstant(obj, 'test_defineconstant_lower', 123); + chai_assert.property(obj, 'TEST_DEFINECONSTANT_LOWER'); + chai_assert.propertyVal(obj, 'TEST_DEFINECONSTANT_LOWER', 123); + }); + it('Define constant by object of name and value', function () { + var obj = {}; + + Khoai.util.defineConstant(obj, { + X: 'A', + y: 'B' + }); + chai_assert.property(obj, 'X'); + chai_assert.propertyVal(obj, 'X', 'A'); + chai_assert.property(obj, 'Y'); + chai_assert.propertyVal(obj, 'Y', 'B'); + }); + }); + describe('Khoai.util.defineObject', function () { + it('test', function () { + var obj = Khoai.util.defineObject({ + name: 'Manh', + old: 123 + }); + // + chai_assert.property(obj, 'name'); + chai_assert.propertyVal(obj, 'name', 'Manh'); + // + chai_assert.property(obj, 'old'); + chai_assert.propertyVal(obj, 'old', 123); + // + obj.old = 456; + chai_assert.propertyVal(obj, 'old', 123); + }); + }); + describe('Khoai.util.inherit', function () { + function SourceClass() { + this.foo = 'bar'; + } + + SourceClass.prototype.test = function () { + return 'SourceClass.foo: ' + this.foo; + }; + + function DestClass() { + SourceClass.call(this) + } + + Khoai.util.inherit(DestClass, SourceClass); + + var obj = new DestClass(); + + it('Has property', function () { + chai_assert.property(obj, 'foo'); + chai_assert.propertyVal(obj, 'foo', 'bar'); + chai_assert.property(obj, 'test'); + chai_assert.isFunction(obj.test); + }); + it('Correct property', function () { + chai_assert.strictEqual(obj.test(), 'SourceClass.foo: bar'); + obj.foo = 'Ohoho'; + chai_assert.strictEqual(obj.test(), 'SourceClass.foo: Ohoho'); + + }); + it('Inherit and override parent methods', function () { + function FooClass() { + SourceClass.call(this) + } + + // + Khoai.util.inherit(FooClass, SourceClass); + // + FooClass.prototype.test = function () { + return 'FooClass.foo: ' + this.foo; + }; + // + var obj_foo = new FooClass(); + // + chai_assert.strictEqual(obj_foo.test(), 'FooClass.foo: bar'); + obj_foo.foo = 'Ohoho'; + chai_assert.strictEqual(obj_foo.test(), 'FooClass.foo: Ohoho'); + chai_assert.strictEqual(obj_foo.constructor.prototype._super.test.call(obj_foo), 'SourceClass.foo: Ohoho'); + }); + }); + describe('Khoai.util.isInstanceOf', function () { + it('test', function () { + chai_assert.isFalse(Khoai.util.isInstanceOf(123, 'Object')); + chai_assert.isTrue(Khoai.util.isInstanceOf(new Array(), 'Array')); + chai_assert.isTrue(Khoai.util.isInstanceOf(123, 'Number')); + chai_assert.isTrue(Khoai.util.isInstanceOf('123', 'String')); + }); + }); + describe('Khoai.util.isNumeric', function () { + it('test', function () { + chai_assert.isTrue(Khoai.util.isNumeric(123)); + chai_assert.isTrue(Khoai.util.isNumeric(123.5)); + chai_assert.isTrue(Khoai.util.isNumeric('123.5')); + chai_assert.isFalse(Khoai.util.isNumeric('123.5 yahoo')); + }) + }); + describe('Khoai.util.isPrimitiveType', function () { + it('test', function () { + chai_assert.isTrue(Khoai.util.isPrimitiveType(123)); + chai_assert.isTrue(Khoai.util.isPrimitiveType('123')); + chai_assert.isTrue(Khoai.util.isPrimitiveType(null)); + chai_assert.isTrue(Khoai.util.isPrimitiveType()); + chai_assert.isFalse(Khoai.util.isPrimitiveType(Khoai.util)); + }); + }); + describe('Khoai.util.mergeObject', function () { + var result, + object = {a: 'A', b: 'B'}, + object2 = {b: 'BB', c: 'C'}, + array = ['D', 'E', 'F'], + string = 'foo', + array_cloned = _.cloneDeep(array), + string_cloned = string; + + before(function () { + result = Khoai.util.mergeObject(object, array, object2, string); + console.log('result', result); + console.log('obj', object); + console.log('arr', array_cloned); + console.log('string', string_cloned); + }); + + it('result must contain all of data', function () { + chai_assert.deepEqual(result, { + a: 'A', b: 'BB', + c: 'C', + 0: 'D', 1: 'E', 2: 'F', + 3: 'foo' + }); + }); + it('result is first object assigned to function', function () { + chai_assert.deepEqual(result, object); + }); + it('except first object, all of other parameter will be reserved', function () { + chai_assert.deepEqual(array, array_cloned); + chai_assert.isTrue(string === string_cloned); + }); + + }); + describe('Khoai.util.loop', function () { + it('Loop over array', function () { + var array = [1, 2, 3, 4, 5], + array_looped = []; + // + Khoai.util.loop(array, function (item) { + array_looped.push(item); + }); + // + chai_assert.deepEqual(array, array_looped); + }); + + it('Loop over object', function () { + var obj = {a: 'A', b: 'B', c: 123, d: [1, 'A', true], e: false, f: (new Array())}, + obj_looped = {}; + // + Khoai.util.loop(obj, function (val, key) { + obj_looped[key] = val; + }); + // + chai_assert.deepEqual(obj, obj_looped); + }); + + it('Loop over array with break', function () { + var array = [1, 2, 3, 4, 5], + array_looped = []; + // + Khoai.util.loop(array, function (item) { + if (item > 3) { + return 'break'; + } + + array_looped.push(item); + }); + // + chai_assert.deepEqual(array_looped, [1, 2, 3]); + }); + it('Loop over array with custom break_on value', function () { + var array = [1, 2, 3, 4, 5], + array_looped = []; + // + Khoai.util.loop(array, function (item) { + if (item > 3) { + return 'break_loop'; + } + + array_looped.push(item); + }, 'break_loop'); + // + chai_assert.deepEqual(array_looped, [1, 2, 3]); + }); + }); + describe('Khoai.util.nextID', function () { + before(function () { + Khoai.util.resetID(); + Khoai.util.resetID('superman'); + }); + + it('Default type', function () { + chai_assert.strictEqual(Khoai.util.nextID(), 'unique_id_1'); + chai_assert.strictEqual(Khoai.util.nextID(), 'unique_id_2'); + chai_assert.strictEqual(Khoai.util.nextID(null, false), 3); + }); + it('Custom type', function () { + chai_assert.strictEqual(Khoai.util.nextID('superman'), 'superman_1'); + chai_assert.strictEqual(Khoai.util.nextID('superman'), 'superman_2'); + chai_assert.strictEqual(Khoai.util.nextID('superman', false), 3); + }); + }); + describe('Khoai.util.oneOf', function () { + it('Exists in array', function () { + var items = [1, 2, 3, 'a']; + // + chai_assert.strictEqual(Khoai.util.oneOf(1, items), 1); + }); + it('Exists in array, with default value', function () { + var items = [1, 2, 3, 'a']; + // + chai_assert.strictEqual(Khoai.util.oneOf(3, items, 'A'), 3); + }); + it('Not exists in array', function () { + var items = [1, 2, 3, 'a']; + // + chai_assert.strictEqual(Khoai.util.oneOf('FOO', items), 1); + }); + it('Not exists in array, with default value', function () { + var items = [1, 2, 3, 'a']; + // + chai_assert.strictEqual(Khoai.util.oneOf('FOO', items, 'BAR'), 'BAR'); + }); + }); + describe('Khoai.util.optionalArgs', function () { + it('test', function () { + var order = ['int', 'bool', 'str'], + rules = {int: 'number', bool: 'boolean', str: 'string'}; + + expect(Khoai.util.optionalArgs([1, true, 'A'], order, rules)).to.be.a('object').with.eql({ + int: 1, + bool: true, + str: "A" + }); + expect(Khoai.util.optionalArgs([true, 'A'], order, rules)).to.be.a('object').with.eql({bool: true, str: "A"}); + expect(Khoai.util.optionalArgs([true], order, rules)).to.be.a('object').with.eql({bool: true}); + expect(Khoai.util.optionalArgs(['A'], order, rules)).to.be.a('object').with.eql({str: "A"}); + expect(Khoai.util.optionalArgs(['A', 'V'], order, rules)).to.be.a('object').with.eql({int: "A", bool: "V"}); + expect(Khoai.util.optionalArgs([1, []], order, rules)).to.be.a('object').with.eql({int: 1, bool: []}); + expect(Khoai.util.optionalArgs([true, []], order, rules)).to.be.a('object').with.eql({int: true, bool: []}); + expect(Khoai.util.optionalArgs(['A', []], order, rules)).to.be.a('object').with.eql({int: "A", bool: []}); + expect(Khoai.util.optionalArgs([[], []], order, rules)).to.be.a('object').with.eql({int: [], bool: []}); + }) + }); + describe('Khoai.util.pairsAsObject', function () { + it('test', function () { + chai_assert.deepEqual(Khoai.util.pairsAsObject({one: 1, two: 2, three: 3}), [ + {key: 'one', value: 1}, + {key: 'two', value: 2}, + {key: 'three', value: 3} + ]); + }); + }); + describe('Khoai.util.pluckBy', function () { + it('test', function () { + var stooges = [{name: 'moe', id: 1, age: 40}, {name: 'larry', id: 2, age: 50}, { + name: 'curly', + id: 4, + age: 60 + }]; + // + chai_assert.deepEqual(Khoai.util.pluckBy(stooges, 'id', 'name'), { + 1: "moe", 2: "larry", 4: "curly" + }); + }); + }); + describe('Khoai.util.randomString', function () { + it('default chars', function () { + var rx = /^[0-9a-zA-Z]{10}$/; + + chai_assert.isTrue(rx.test(Khoai.util.randomString(10))); + }); + it('Custom chars', function () { + var rx = /^[ABCDEF]{10}$/; + + chai_assert.isTrue(rx.test(Khoai.util.randomString(10, 'ABCDEF'))); + }); + + }); + describe('Khoai.util.setup', function () { + var obj = {}; + + beforeEach(function () { + obj = {a: 'A', b: 'B'} + }); + + it('setup by key and value', function () { + chai_assert.deepEqual(Khoai.util.setup(obj, 'a', '123'), {a: '123', b: 'B'}); + }); + it('setup by an object', function () { + chai_assert.deepEqual(Khoai.util.setup(obj, {b: 'Yahoo', c: 'ASD'}), {a: 'A', b: 'Yahoo', c: 'ASD'}); + }); + + }); + describe('Khoai.util.toggle', function () { + var arr = []; + + beforeEach(function () { + arr = ['A', 'B', 'C', 'D']; + }); + + it('Toogle mode is ad', function () { + chai_assert.sameMembers(Khoai.util.toggle(arr, ['A', 'V'], true), ['A', 'B', 'C', 'D', 'V']); + }); + it('Toogle mode is remove', function () { + chai_assert.sameMembers(Khoai.util.toggle(arr, ['A', 'V'], false), ['B', 'C', 'D']); + }); + it('Toogle', function () { + chai_assert.sameMembers(Khoai.util.toggle(arr, ['A', 'V']), ['B', 'C', 'D', 'V']); + }); + }); + }); +}); \ No newline at end of file