comparison core/src/js/safe.js @ 29:acdcdf1a8d21

repository reorganized
author cin
date Tue, 26 Jun 2018 19:35:44 +0300
parents src/implab/safe.js@8332e287d552
children 27e8e9e38e07
comparison
equal deleted inserted replaced
28:d796bbbe558c 29:acdcdf1a8d21
1 define([],
2
3 function () {
4 var _create = Object.create,
5 _keys = Object.keys;
6
7 var safe = null;
8 safe = {
9 argumentNotNull: function (arg, name) {
10 if (arg === null || arg === undefined)
11 throw new Error("The argument " + name + " can't be null or undefined");
12 },
13
14 argumentNotEmptyString: function (arg, name) {
15 if (typeof (arg) !== "string" || !arg.length)
16 throw new Error("The argument '" + name + "' must be a not empty string");
17 },
18
19 argumentNotEmptyArray: function (arg, name) {
20 if (!(arg instanceof Array) || !arg.length)
21 throw new Error("The argument '" + name + "' must be a not empty array");
22 },
23
24 argumentOfType: function (arg, type, name) {
25 if (!(arg instanceof type))
26 throw new Error("The argument '" + name + "' type doesn't match");
27 },
28
29 isNull: function (arg) {
30 return (arg === null || arg === undefined);
31 },
32
33 isPrimitive: function (arg) {
34 return (arg === null || arg === undefined || typeof (arg) === "string" ||
35 typeof (arg) === "number" || typeof (arg) === "boolean");
36 },
37
38 isInteger: function (arg) {
39 return parseInt(arg) == arg;
40 },
41
42 isNumber: function (arg) {
43 return parseFloat(arg) == arg;
44 },
45
46 isString: function (val) {
47 return typeof (val) == "string" || val instanceof String;
48 },
49
50 isNullOrEmptyString: function (str) {
51 if (str === null || str === undefined ||
52 ((typeof (str) == "string" || str instanceof String) && str.length === 0))
53 return true;
54 },
55
56 isNotEmptyArray: function (arg) {
57 return (arg instanceof Array && arg.length > 0);
58 },
59
60 /**
61 * Выполняет метод для каждого элемента массива, останавливается, когда
62 * либо достигнут конец массива, либо функция <c>cb</c> вернула
63 * значение.
64 *
65 * @param{Array | Object} obj массив элементов для просмотра
66 * @param{Function} cb функция, вызываемая для каждого элемента
67 * @param{Object} thisArg значение, которое будет передано в качестве
68 * <c>this</c> в <c>cb</c>.
69 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
70 * если достигнут конец массива.
71 */
72 each: function (obj, cb, thisArg) {
73 safe.argumentNotNull(cb, "cb");
74 var i, x;
75 if (obj instanceof Array) {
76 for (i = 0; i < obj.length; i++) {
77 x = cb.call(thisArg, obj[i], i);
78 if (x !== undefined)
79 return x;
80 }
81 } else {
82 var keys = _keys(obj);
83 for (i = 0; i < keys.length; i++) {
84 var k = keys[i];
85 x = cb.call(thisArg, obj[k], k);
86 if (x !== undefined)
87 return x;
88 }
89 }
90 },
91
92 /**
93 * Копирует свойства одного объекта в другой.
94 *
95 * @param{Any} dest объект в который нужно скопировать значения
96 * @param{Any} src источник из которого будут копироваться значения
97 * @tmpl{Object|Array} tmpl шаблон по которому будет происходить
98 * копирование. Если шаблон является массивом
99 * (список свойств), тогда значения этого массива
100 * являются именами свойсвт которые будут
101 * скопированы. Если шаблон является объектом (карта
102 * преобразования имен свойств src->dst), тогда
103 * копирование будет осуществляться только
104 * собственных свойств источника, присутсвующих в
105 * шаблоне, при этом значение свойства шаблона
106 * является именем свойства в которое будет
107 * произведено коприрование
108 */
109 mixin: function (dest, src, tmpl) {
110 safe.argumentNotNull(dest, "dest");
111 if (!src)
112 return dest;
113
114 var keys, i, p;
115 if (arguments.length < 3) {
116 keys = _keys(src);
117 for (i = 0; i < keys.length; i++) {
118 p = keys[i];
119 dest[p] = src[p];
120 }
121 } else {
122 if (tmpl instanceof Array) {
123 for (i = 0; i < tmpl.length; i++) {
124 p = tmpl[i];
125 if (p in src)
126 dest[p] = src[p];
127 }
128
129 } else {
130 keys = _keys(src);
131 for (i = 0; i < keys.length; i++) {
132 p = keys[i];
133 if (p in tmpl)
134 dest[tmpl[p]] = src[p];
135 }
136 }
137 }
138 return dest;
139 },
140
141 /** Wraps the specified function to emulate an asynchronous execution.
142 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
143 * @param{Function|String} fn [Required] Function wich will be wrapped.
144 */
145 async: function (fn, thisArg) {
146 if (arguments.length == 2 && !(fn instanceof Function))
147 fn = thisArg[fn];
148
149 if (fn == null)
150 throw new Error("The function must be specified");
151
152 function wrapresult(x, e) {
153 if (e) {
154 return {
155 then: function (cb, eb) {
156 try {
157 return eb ? wrapresult(eb(e)) : this;
158 } catch (e2) {
159 return wrapresult(null, e2);
160 }
161 }
162 };
163 } else {
164 if (x && x.then)
165 return x;
166 return {
167 then : function(cb) {
168 try {
169 return cb ? wrapresult(cb(x)) : this;
170 } catch(e2) {
171 return wrapresult(e2);
172 }
173 }
174 };
175 }
176 }
177
178 try {
179 return wrapresult(fn.apply(thisArg, arguments));
180 } catch (e) {
181 return wrapresult(null, e);
182 };
183 },
184
185 create: function () {
186 if (console && console.warn)
187 console.warn("implab/safe::create is deprecated use Object.create instead");
188 _create.apply(this, arguments);
189 },
190
191 delegate: function (target, method) {
192 if (!(method instanceof Function)) {
193 this.argumentNotNull(target, "target");
194 method = target[method];
195 }
196
197 if (!(method instanceof Function))
198 throw new Error("'method' argument must be a Function or a method name");
199
200 return function () {
201 return method.apply(target, arguments);
202 };
203 },
204
205 /**
206 * Для каждого элемента массива вызывает указанную функцию и сохраняет
207 * возвращенное значение в массиве результатов.
208 *
209 * @remarks cb может выполняться асинхронно, при этом одновременно будет
210 * только одна операция.
211 *
212 * @async
213 */
214 pmap: function (items, cb) {
215 safe.argumentNotNull(cb, "cb");
216
217 if (items && items.then instanceof Function)
218 return items.then(function (data) {
219 return safe.pmap(data, cb);
220 });
221
222 if (safe.isNull(items) || !items.length)
223 return items;
224
225 var i = 0,
226 result = [];
227
228 function next() {
229 var r, ri;
230
231 function chain(x) {
232 result[ri] = x;
233 return next();
234 }
235
236 while (i < items.length) {
237 r = cb(items[i], i);
238 ri = i;
239 i++;
240 if (r && r.then) {
241 return r.then(chain);
242 } else {
243 result[ri] = r;
244 }
245 }
246 return result;
247 }
248
249 return next();
250 },
251
252 /**
253 * Для каждого элемента массива вызывает указанную функцию, результаты
254 * не сохраняются
255 *
256 * @remarks cb может выполняться асинхронно, при этом одновременно будет
257 * только одна операция.
258 * @async
259 */
260 pfor: function (items, cb) {
261 safe.argumentNotNull(cb, "cb");
262
263 if (items && items.then instanceof Function)
264 return items.then(function (data) {
265 return safe.pmap(data, cb);
266 });
267
268 if (safe.isNull(items) || !items.length)
269 return items;
270
271 var i = 0;
272
273 function next() {
274 while (i < items.length) {
275 var r = cb(items[i], i);
276 i++;
277 if (r && r.then)
278 return r.then(next);
279 }
280 }
281
282 return next();
283 },
284
285 /**
286 * Выбирает первый элемент из последовательности, или обещания, если в
287 * качестве параметра используется обещание, оно должно вернуть массив.
288 *
289 * @param{Function} cb обработчик результата, ему будет передан первый
290 * элемент последовательности в случае успеха
291 * @param{Fucntion} err обработчик исключения, если массив пустой, либо
292 * не массив
293 *
294 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
295 * обещание, либо первый элемент.
296 * @async
297 */
298 first: function (sequence, cb, err) {
299 if (sequence) {
300 if (sequence.then instanceof Function) {
301 return sequence.then(function (res) {
302 return safe.first(res, cb, err);
303 }, err);
304 } else if (sequence && "length" in sequence) {
305 if (sequence.length === 0) {
306 if (err)
307 return err(new Error("The sequence is empty"));
308 else
309 throw new Error("The sequence is empty");
310 }
311 return cb ? cb(sequence[0]) : sequence[0];
312 }
313 }
314
315 if (err)
316 return err(new Error("The sequence is required"));
317 else
318 throw new Error("The sequence is required");
319 }
320 };
321
322 return safe;
323 });