'use strict'; angular.module('take', []) .factory('take', function () { var debug = false; /** * @example * * function (options) { * take(options, 'id').it('String').set(this, 'id'); * take(options, 'name').it('String').set(this, 'name'); * take(options, 'activeUserId').skip(null).it('Number', 'String').setFn(this, 'setActiveUser'); * return this; * }; * * @alias take * @param {Object} src * @param {String} srcName * @param {*} [defaultValue] * @constructor */ var TakeTool = function (src, srcName, defaultValue) { this.skipValue = false; this.src = src; this.srcName = srcName; if (arguments.length >= 3) { this.defaultValue = defaultValue; } if (this.srcName in this.src) { this.value = this.src[this.srcName]; } else { if (this.hasOwnProperty('defaultValue')) { this.value = this.defaultValue; } else { delete this.value; } } }; /** * Marks value as optional, if source has no specified property - value will not be validated and set * @param {...*} value * @returns {TakeTool} */ TakeTool.prototype.skip = function (value) { var i, c; if (this.hasOwnProperty('value')) { if (arguments.length) { for (i = 0, c = arguments.length; i < c; ++i) { if (this.value === arguments[i]) { this.skipValue = true; break; } } } } else { this.skipValue = true; } return this; }; /** * Validates source value type, can generates exception * @param {...*} type - Constructor or its name (ex. jQuery, "String") * @returns {TakeTool} */ TakeTool.prototype.it = function (type) { var i, c, success, valueType; if (this.skipValue) { return this; } if (!this.hasOwnProperty('value')) { if (debug) { console.log('TakeTool:', this); } throw new Error('TakeTool: Source has no property "' + this.srcName + '"'); } if ('*' === type || 0 === arguments.length) { return this; } success = false; valueType = Object.prototype.toString.call(this.value).substring(8); valueType = valueType.substring(0, valueType.length - 1); for (i = 0, c = arguments.length; i < c; ++i) { type = arguments[i]; if (('function' === typeof type && this.value instanceof type) || (valueType === type)) { success = true; break; } } if (false === success) { if (debug) { console.log('TakeTool:', this); } type = arguments.length > 1 ? '[' + Array.prototype.slice.call(arguments).join(', ') + ']' : type; throw new Error( ('function' === typeof type) ? 'TakeTool: Source property "' + this.srcName + '" is not instance of specified type' : 'TakeTool: Source property "' + this.srcName + '" expected to be "' + type + '" but found "' + valueType + '"' ); } return this; }; /** * Converts source value to specified type * @param {Function} castFn - Type cast function * @returns {TakeTool} */ TakeTool.prototype.to = function (castFn) { if (this.hasOwnProperty('value') && !this.skipValue) { this.value = castFn(this.value); } return this; }; /** * Set value to specified property * @param {Object} dst * @param {String} dstName * @returns {TakeTool} */ TakeTool.prototype.set = function (dst, dstName) { if (this.hasOwnProperty('value') && !this.skipValue) { dst[dstName] = this.value; } return this; }; /** * Calls specified method * @param {Object} dst * @param {String} methodName * @returns {TakeTool} */ TakeTool.prototype.setFn = function (dst, methodName) { if (this.hasOwnProperty('value') && !this.skipValue) { dst[methodName].call(dst, this.value); } return this; }; /** * Calls specified callback * @param {Function} fn * @param {*} [context] * @returns {TakeTool} */ TakeTool.prototype.fn = function (fn, context) { if (this.hasOwnProperty('value') && !this.skipValue) { fn.call(context || null, this.value); } return this; }; /** * Extends destination property with source value * @param {Object} dst * @param {String} dstName * @returns {TakeTool} */ TakeTool.prototype.extend = function (dst, dstName) { if (this.hasOwnProperty('value') && !this.skipValue) { if (!(dstName in dst)) { dst[dstName] = {}; } angular.extend(dst[dstName], this.value); } return this; }; /** * @example * * function (options) { * take(options, 'id').it('String').set(this, 'id'); * take(options, 'name').it('String').set(this, 'name'); * take(options, 'activeUserId').skip(null).it('Number', 'String').setFn(this, 'setActiveUser'); * return this; * }; * * @param {Object} src * @param {String} srcName * @param {*} [defaultValue] * @returns {TakeTool} */ return function take(src, srcName, defaultValue) { switch(arguments.length) { case 2: return new TakeTool(src, srcName); case 3: return new TakeTool(src, srcName, defaultValue); default: throw new Error('TakeTool: wrong arguments count', arguments.length); } }; });