Mercurial > pub > ImplabJs
diff core/src/js/safe.js @ 34:27e8e9e38e07 default tip
Слияние
author | nickolay |
---|---|
date | Wed, 05 Jun 2019 20:44:15 +0300 |
parents | src/implab/safe.js@8af8e840dd49 src/implab/safe.js@acdcdf1a8d21 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/src/js/safe.js Wed Jun 05 20:44:15 2019 +0300 @@ -0,0 +1,325 @@ +define([], + + function () { + var _create = Object.create, + _keys = Object.keys; + + var safe = null; + safe = { + argumentNotNull: function (arg, name) { + if (arg === null || arg === undefined) + throw new Error("The argument " + name + " can't be null or undefined"); + }, + + argumentNotEmptyString: function (arg, name) { + if (typeof (arg) !== "string" || !arg.length) + throw new Error("The argument '" + name + "' must be a not empty string"); + }, + + argumentNotEmptyArray: function (arg, name) { + if (!(arg instanceof Array) || !arg.length) + throw new Error("The argument '" + name + "' must be a not empty array"); + }, + + argumentOfType: function (arg, type, name) { + if (!(arg instanceof type)) + throw new Error("The argument '" + name + "' type doesn't match"); + }, + + isNull: function (arg) { + return (arg === null || arg === undefined); + }, + + isPrimitive: function (arg) { + return (arg === null || arg === undefined || typeof (arg) === "string" || + typeof (arg) === "number" || typeof (arg) === "boolean"); + }, + + isInteger: function (arg) { + return parseInt(arg) == arg; + }, + + isNumber: function (arg) { + return parseFloat(arg) == arg; + }, + + isString: function (val) { + return typeof (val) == "string" || val instanceof String; + }, + + isNullOrEmptyString: function (str) { + if (str === null || str === undefined || + ((typeof (str) == "string" || str instanceof String) && str.length === 0)) + return true; + }, + + isNotEmptyArray: function (arg) { + return (arg instanceof Array && arg.length > 0); + }, + + /** + * Выполняет метод для каждого элемента массива, останавливается, когда + * либо достигнут конец массива, либо функция <c>cb</c> вернула + * значение. + * + * @param{Array | Object} obj массив элементов для просмотра + * @param{Function} cb функция, вызываемая для каждого элемента + * @param{Object} thisArg значение, которое будет передано в качестве + * <c>this</c> в <c>cb</c>. + * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c> + * если достигнут конец массива. + */ + each: function (obj, cb, thisArg) { + safe.argumentNotNull(cb, "cb"); + var i, x; + if (obj instanceof Array) { + for (i = 0; i < obj.length; i++) { + x = cb.call(thisArg, obj[i], i); + if (x !== undefined) + return x; + } + } else { + var keys = _keys(obj); + for (i = 0; i < keys.length; i++) { + var k = keys[i]; + x = cb.call(thisArg, obj[k], k); + if (x !== undefined) + return x; + } + } + }, + + /** + * Копирует свойства одного объекта в другой. + * + * @param{Any} dest объект в который нужно скопировать значения + * @param{Any} src источник из которого будут копироваться значения + * @tmpl{Object|Array} tmpl шаблон по которому будет происходить + * копирование. Если шаблон является массивом + * (список свойств), тогда значения этого массива + * являются именами свойсвт которые будут + * скопированы. Если шаблон является объектом (карта + * преобразования имен свойств src->dst), тогда + * копирование будет осуществляться только + * собственных свойств источника, присутсвующих в + * шаблоне, при этом значение свойства шаблона + * является именем свойства в которое будет + * произведено коприрование + */ + mixin: function (dest, src, tmpl) { + safe.argumentNotNull(dest, "dest"); + if (!src) + return dest; + + var keys, i, p; + if (arguments.length < 3) { + keys = _keys(src); + for (i = 0; i < keys.length; i++) { + p = keys[i]; + dest[p] = src[p]; + } + } else { + if (tmpl instanceof Array) { + for (i = 0; i < tmpl.length; i++) { + p = tmpl[i]; + if (p in src) + dest[p] = src[p]; + } + + } else { + keys = _keys(src); + for (i = 0; i < keys.length; i++) { + p = keys[i]; + if (p in tmpl) + dest[tmpl[p]] = src[p]; + } + } + } + return dest; + }, + + /** Wraps the specified function to emulate an asynchronous execution. + * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function. + * @param{Function|String} fn [Required] Function wich will be wrapped. + */ + async: function (fn, thisArg) { + if (arguments.length == 2 && !(fn instanceof Function)) + fn = thisArg[fn]; + + if (fn == null) + throw new Error("The function must be specified"); + + function wrapresult(x, e) { + if (e) { + return { + then: function (cb, eb) { + try { + return eb ? wrapresult(eb(e)) : this; + } catch (e2) { + return wrapresult(null, e2); + } + } + }; + } else { + if (x && x.then) + return x; + return { + then: function (cb) { + try { + return cb ? wrapresult(cb(x)) : this; + } catch (e2) { + return wrapresult(e2); + } + } + }; + } + } + + return function () { + try { + return wrapresult(fn.apply(thisArg, arguments)); + } catch (e) { + return wrapresult(null, e); + } + }; + }, + + create: function () { + if (console && console.warn) + console.warn("implab/safe::create is deprecated use Object.create instead"); + _create.apply(this, arguments); + }, + + delegate: function (target, method) { + if (!(method instanceof Function)) { + this.argumentNotNull(target, "target"); + method = target[method]; + } + + if (!(method instanceof Function)) + throw new Error("'method' argument must be a Function or a method name"); + + return function () { + return method.apply(target, arguments); + }; + }, + + /** + * Для каждого элемента массива вызывает указанную функцию и сохраняет + * возвращенное значение в массиве результатов. + * + * @remarks cb может выполняться асинхронно, при этом одновременно будет + * только одна операция. + * + * @async + */ + pmap: function (items, cb) { + safe.argumentNotNull(cb, "cb"); + + if (items && items.then instanceof Function) + return items.then(function (data) { + return safe.pmap(data, cb); + }); + + if (safe.isNull(items) || !items.length) + return items; + + var i = 0, + result = []; + + function next() { + var r, ri; + + function chain(x) { + result[ri] = x; + return next(); + } + + while (i < items.length) { + r = cb(items[i], i); + ri = i; + i++; + if (r && r.then) { + return r.then(chain); + } else { + result[ri] = r; + } + } + return result; + } + + return next(); + }, + + /** + * Для каждого элемента массива вызывает указанную функцию, результаты + * не сохраняются + * + * @remarks cb может выполняться асинхронно, при этом одновременно будет + * только одна операция. + * @async + */ + pfor: function (items, cb) { + safe.argumentNotNull(cb, "cb"); + + if (items && items.then instanceof Function) + return items.then(function (data) { + return safe.pmap(data, cb); + }); + + if (safe.isNull(items) || !items.length) + return items; + + var i = 0; + + function next() { + while (i < items.length) { + var r = cb(items[i], i); + i++; + if (r && r.then) + return r.then(next); + } + } + + return next(); + }, + + /** + * Выбирает первый элемент из последовательности, или обещания, если в + * качестве параметра используется обещание, оно должно вернуть массив. + * + * @param{Function} cb обработчик результата, ему будет передан первый + * элемент последовательности в случае успеха + * @param{Fucntion} err обработчик исключения, если массив пустой, либо + * не массив + * + * @remarks Если не указаны ни cb ни err, тогда функция вернет либо + * обещание, либо первый элемент. + * @async + */ + first: function (sequence, cb, err) { + if (sequence) { + if (sequence.then instanceof Function) { + return sequence.then(function (res) { + return safe.first(res, cb, err); + }, err); + } else if (sequence && "length" in sequence) { + if (sequence.length === 0) { + if (err) + return err(new Error("The sequence is empty")); + else + throw new Error("The sequence is empty"); + } + return cb ? cb(sequence[0]) : sequence[0]; + } + } + + if (err) + return err(new Error("The sequence is required")); + else + throw new Error("The sequence is required"); + } + }; + + return safe; + }); \ No newline at end of file