Mercurial > pub > ImplabJs
diff src/implab/safe.js @ 0:fc2517695ee1
Initial commit, draft import of existing work
author | cin |
---|---|
date | Thu, 01 Jun 2017 13:20:03 +0300 |
parents | |
children | 00779cb63b12 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/implab/safe.js Thu Jun 01 13:20:03 2017 +0300 @@ -0,0 +1,356 @@ +define([], + + function () { + var id = 0, + OID_FIELD = "__core_safe_oid_field", + _create, _defineProperty, _keys; + + if (!Object.keys) { + _defineProperty = function (obj, prop, d) { + if (!d) + throw new Error("Invalid descriptor"); + obj[prop] = d.value; + }; + } else { + _defineProperty = Object.defineProperty; + } + + if (!Object.create) { + var tctor = function () {}; + _create = function (proto) { + if (arguments.length > 1) + throw new Error("The two arguments for isn't supported"); + + tctor.prototype = proto; + return new tctor(); + }; + } else { + _create = Object.create; + } + + if (!Object.keys) { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys#Polyfill + _keys = (function () { + 'use strict'; + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({ + toString: null + }).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + + return function (obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = [], + prop, i; + + for (prop in obj) { + if (prop != OID_FIELD && hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + }()); + } else { + _keys = Object.keys; + } + + var safe = null; + safe = { + OID_FIELD: OID_FIELD, + + 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; + } + } + }, + + oid: function (obj) { + return this.isPrimitive(obj) ? undefined : obj[OID_FIELD] || + (_defineProperty(obj, OID_FIELD, { + value: ++id, + enumerable: false + }) && id); + }, + + /** + * Копирует свойства одного объекта в другой. + * + * @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; + }, + + create: _create, + + 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