comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:fc2517695ee1
1 define([],
2
3 function () {
4 var id = 0,
5 OID_FIELD = "__core_safe_oid_field",
6 _create, _defineProperty, _keys;
7
8 if (!Object.keys) {
9 _defineProperty = function (obj, prop, d) {
10 if (!d)
11 throw new Error("Invalid descriptor");
12 obj[prop] = d.value;
13 };
14 } else {
15 _defineProperty = Object.defineProperty;
16 }
17
18 if (!Object.create) {
19 var tctor = function () {};
20 _create = function (proto) {
21 if (arguments.length > 1)
22 throw new Error("The two arguments for isn't supported");
23
24 tctor.prototype = proto;
25 return new tctor();
26 };
27 } else {
28 _create = Object.create;
29 }
30
31 if (!Object.keys) {
32 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys#Polyfill
33 _keys = (function () {
34 'use strict';
35 var hasOwnProperty = Object.prototype.hasOwnProperty,
36 hasDontEnumBug = !({
37 toString: null
38 }).propertyIsEnumerable('toString'),
39 dontEnums = [
40 'toString',
41 'toLocaleString',
42 'valueOf',
43 'hasOwnProperty',
44 'isPrototypeOf',
45 'propertyIsEnumerable',
46 'constructor'
47 ],
48 dontEnumsLength = dontEnums.length;
49
50 return function (obj) {
51 if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
52 throw new TypeError('Object.keys called on non-object');
53 }
54
55 var result = [],
56 prop, i;
57
58 for (prop in obj) {
59 if (prop != OID_FIELD && hasOwnProperty.call(obj, prop)) {
60 result.push(prop);
61 }
62 }
63
64 if (hasDontEnumBug) {
65 for (i = 0; i < dontEnumsLength; i++) {
66 if (hasOwnProperty.call(obj, dontEnums[i])) {
67 result.push(dontEnums[i]);
68 }
69 }
70 }
71 return result;
72 };
73 }());
74 } else {
75 _keys = Object.keys;
76 }
77
78 var safe = null;
79 safe = {
80 OID_FIELD: OID_FIELD,
81
82 argumentNotNull: function (arg, name) {
83 if (arg === null || arg === undefined)
84 throw new Error("The argument " + name + " can't be null or undefined");
85 },
86
87 argumentNotEmptyString: function (arg, name) {
88 if (typeof (arg) !== "string" || !arg.length)
89 throw new Error("The argument '" + name + "' must be a not empty string");
90 },
91
92 argumentNotEmptyArray: function (arg, name) {
93 if (!(arg instanceof Array) || !arg.length)
94 throw new Error("The argument '" + name + "' must be a not empty array");
95 },
96
97 argumentOfType: function (arg, type, name) {
98 if (!(arg instanceof type))
99 throw new Error("The argument '" + name + "' type doesn't match");
100 },
101
102 isNull: function (arg) {
103 return (arg === null || arg === undefined);
104 },
105
106 isPrimitive: function (arg) {
107 return (arg === null || arg === undefined || typeof (arg) === "string" ||
108 typeof (arg) === "number" || typeof (arg) === "boolean");
109 },
110
111 isInteger: function (arg) {
112 return parseInt(arg) === arg;
113 },
114
115 isNumber: function (arg) {
116 return parseFloat(arg) === arg;
117 },
118
119 isString: function (val) {
120 return typeof (val) == "string" || val instanceof String;
121 },
122
123 isNullOrEmptyString: function (str) {
124 if (str === null || str === undefined ||
125 ((typeof (str) == "string" || str instanceof String) && str.length === 0))
126 return true;
127 },
128
129 isNotEmptyArray: function (arg) {
130 return (arg instanceof Array && arg.length > 0);
131 },
132
133 /**
134 * Выполняет метод для каждого элемента массива, останавливается, когда
135 * либо достигнут конец массива, либо функция <c>cb</c> вернула
136 * значение.
137 *
138 * @param{Array | Object} obj массив элементов для просмотра
139 * @param{Function} cb функция, вызываемая для каждого элемента
140 * @param{Object} thisArg значение, которое будет передано в качестве
141 * <c>this</c> в <c>cb</c>.
142 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
143 * если достигнут конец массива.
144 */
145 each: function (obj, cb, thisArg) {
146 safe.argumentNotNull(cb, "cb");
147 var i, x;
148 if (obj instanceof Array) {
149 for (i = 0; i < obj.length; i++) {
150 x = cb.call(thisArg, obj[i], i);
151 if (x !== undefined)
152 return x;
153 }
154 } else {
155 var keys = _keys(obj);
156 for (i = 0; i < keys.length; i++) {
157 var k = keys[i];
158 x = cb.call(thisArg, obj[k], k);
159 if (x !== undefined)
160 return x;
161 }
162 }
163 },
164
165 oid: function (obj) {
166 return this.isPrimitive(obj) ? undefined : obj[OID_FIELD] ||
167 (_defineProperty(obj, OID_FIELD, {
168 value: ++id,
169 enumerable: false
170 }) && id);
171 },
172
173 /**
174 * Копирует свойства одного объекта в другой.
175 *
176 * @param{Any} dest объект в который нужно скопировать значения
177 * @param{Any} src источник из которого будут копироваться значения
178 * @tmpl{Object|Array} tmpl шаблон по которому будет происходить
179 * копирование. Если шаблон является массивом
180 * (список свойств), тогда значения этого массива
181 * являются именами свойсвт которые будут
182 * скопированы. Если шаблон является объектом (карта
183 * преобразования имен свойств src->dst), тогда
184 * копирование будет осуществляться только
185 * собственных свойств источника, присутсвующих в
186 * шаблоне, при этом значение свойства шаблона
187 * является именем свойства в которое будет
188 * произведено коприрование
189 */
190 mixin: function (dest, src, tmpl) {
191 safe.argumentNotNull(dest, "dest");
192 if (!src)
193 return dest;
194
195 var keys, i, p;
196 if (arguments.length < 3) {
197 keys = _keys(src);
198 for (i = 0; i < keys.length; i++) {
199 p = keys[i];
200 dest[p] = src[p];
201 }
202 } else {
203 if (tmpl instanceof Array) {
204 for (i = 0; i < tmpl.length; i++) {
205 p = tmpl[i];
206 if (p in src)
207 dest[p] = src[p];
208 }
209
210 } else {
211 keys = _keys(src);
212 for (i = 0; i < keys.length; i++) {
213 p = keys[i];
214 if (p in tmpl)
215 dest[tmpl[p]] = src[p];
216 }
217 }
218 }
219 return dest;
220 },
221
222 create: _create,
223
224 delegate: function (target, method) {
225 if (!(method instanceof Function)) {
226 this.argumentNotNull(target, "target");
227 method = target[method];
228 }
229
230 if (!(method instanceof Function))
231 throw new Error("'method' argument must be a Function or a method name");
232
233 return function () {
234 return method.apply(target, arguments);
235 };
236 },
237
238 /**
239 * Для каждого элемента массива вызывает указанную функцию и сохраняет
240 * возвращенное значение в массиве результатов.
241 *
242 * @remarks cb может выполняться асинхронно, при этом одновременно будет
243 * только одна операция.
244 *
245 * @async
246 */
247 pmap: function (items, cb) {
248 safe.argumentNotNull(cb, "cb");
249
250 if (items && items.then instanceof Function)
251 return items.then(function (data) {
252 return safe.pmap(data, cb);
253 });
254
255 if (safe.isNull(items) || !items.length)
256 return items;
257
258 var i = 0,
259 result = [];
260
261 function next() {
262 var r, ri;
263
264 function chain(x) {
265 result[ri] = x;
266 return next();
267 }
268
269 while (i < items.length) {
270 r = cb(items[i], i);
271 ri = i;
272 i++;
273 if (r && r.then) {
274 return r.then(chain);
275 } else {
276 result[ri] = r;
277 }
278 }
279 return result;
280 }
281
282 return next();
283 },
284
285 /**
286 * Для каждого элемента массива вызывает указанную функцию, результаты
287 * не сохраняются
288 *
289 * @remarks cb может выполняться асинхронно, при этом одновременно будет
290 * только одна операция.
291 * @async
292 */
293 pfor: function (items, cb) {
294 safe.argumentNotNull(cb, "cb");
295
296 if (items && items.then instanceof Function)
297 return items.then(function (data) {
298 return safe.pmap(data, cb);
299 });
300
301 if (safe.isNull(items) || !items.length)
302 return items;
303
304 var i = 0;
305
306 function next() {
307 while (i < items.length) {
308 var r = cb(items[i], i);
309 i++;
310 if (r && r.then)
311 return r.then(next);
312 }
313 }
314
315 return next();
316 },
317
318 /**
319 * Выбирает первый элемент из последовательности, или обещания, если в
320 * качестве параметра используется обещание, оно должно вернуть массив.
321 *
322 * @param{Function} cb обработчик результата, ему будет передан первый
323 * элемент последовательности в случае успеха
324 * @param{Fucntion} err обработчик исключения, если массив пустой, либо
325 * не массив
326 *
327 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
328 * обещание, либо первый элемент.
329 * @async
330 */
331 first: function (sequence, cb, err) {
332 if (sequence) {
333 if (sequence.then instanceof Function) {
334 return sequence.then(function (res) {
335 return safe.first(res, cb, err);
336 }, err);
337 } else if (sequence && "length" in sequence) {
338 if (sequence.length === 0) {
339 if (err)
340 return err(new Error("The sequence is empty"));
341 else
342 throw new Error("The sequence is empty");
343 }
344 return cb ? cb(sequence[0]) : sequence[0];
345 }
346 }
347
348 if (err)
349 return err(new Error("The sequence is required"));
350 else
351 throw new Error("The sequence is required");
352 }
353 };
354
355 return safe;
356 });