comparison src/implab/safe.js @ 34:27e8e9e38e07 default tip

Слияние
author nickolay
date Wed, 05 Jun 2019 20:44:15 +0300
parents 8af8e840dd49 1dc2fd263b90
children
comparison
equal deleted inserted replaced
33:8af8e840dd49 34:27e8e9e38e07
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 return function () {
179 try {
180 return wrapresult(fn.apply(thisArg, arguments));
181 } catch (e) {
182 return wrapresult(null, e);
183 }
184 };
185 },
186
187 create: function () {
188 if (console && console.warn)
189 console.warn("implab/safe::create is deprecated use Object.create instead");
190 _create.apply(this, arguments);
191 },
192
193 delegate: function (target, method) {
194 if (!(method instanceof Function)) {
195 this.argumentNotNull(target, "target");
196 method = target[method];
197 }
198
199 if (!(method instanceof Function))
200 throw new Error("'method' argument must be a Function or a method name");
201
202 return function () {
203 return method.apply(target, arguments);
204 };
205 },
206
207 /**
208 * Для каждого элемента массива вызывает указанную функцию и сохраняет
209 * возвращенное значение в массиве результатов.
210 *
211 * @remarks cb может выполняться асинхронно, при этом одновременно будет
212 * только одна операция.
213 *
214 * @async
215 */
216 pmap: function (items, cb) {
217 safe.argumentNotNull(cb, "cb");
218
219 if (items && items.then instanceof Function)
220 return items.then(function (data) {
221 return safe.pmap(data, cb);
222 });
223
224 if (safe.isNull(items) || !items.length)
225 return items;
226
227 var i = 0,
228 result = [];
229
230 function next() {
231 var r, ri;
232
233 function chain(x) {
234 result[ri] = x;
235 return next();
236 }
237
238 while (i < items.length) {
239 r = cb(items[i], i);
240 ri = i;
241 i++;
242 if (r && r.then) {
243 return r.then(chain);
244 } else {
245 result[ri] = r;
246 }
247 }
248 return result;
249 }
250
251 return next();
252 },
253
254 /**
255 * Для каждого элемента массива вызывает указанную функцию, результаты
256 * не сохраняются
257 *
258 * @remarks cb может выполняться асинхронно, при этом одновременно будет
259 * только одна операция.
260 * @async
261 */
262 pfor: function (items, cb) {
263 safe.argumentNotNull(cb, "cb");
264
265 if (items && items.then instanceof Function)
266 return items.then(function (data) {
267 return safe.pmap(data, cb);
268 });
269
270 if (safe.isNull(items) || !items.length)
271 return items;
272
273 var i = 0;
274
275 function next() {
276 while (i < items.length) {
277 var r = cb(items[i], i);
278 i++;
279 if (r && r.then)
280 return r.then(next);
281 }
282 }
283
284 return next();
285 },
286
287 /**
288 * Выбирает первый элемент из последовательности, или обещания, если в
289 * качестве параметра используется обещание, оно должно вернуть массив.
290 *
291 * @param{Function} cb обработчик результата, ему будет передан первый
292 * элемент последовательности в случае успеха
293 * @param{Fucntion} err обработчик исключения, если массив пустой, либо
294 * не массив
295 *
296 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
297 * обещание, либо первый элемент.
298 * @async
299 */
300 first: function (sequence, cb, err) {
301 if (sequence) {
302 if (sequence.then instanceof Function) {
303 return sequence.then(function (res) {
304 return safe.first(res, cb, err);
305 }, err);
306 } else if (sequence && "length" in sequence) {
307 if (sequence.length === 0) {
308 if (err)
309 return err(new Error("The sequence is empty"));
310 else
311 throw new Error("The sequence is empty");
312 }
313 return cb ? cb(sequence[0]) : sequence[0];
314 }
315 }
316
317 if (err)
318 return err(new Error("The sequence is required"));
319 else
320 throw new Error("The sequence is required");
321 }
322 };
323
324 return safe;
325 });