UNPKG

24.3 kBJavaScriptView Raw
1/*
2Copyright 2015, 2016 OpenMarket Ltd
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16"use strict";
17/**
18 * This is an internal module.
19 * @module utils
20 */
21
22/**
23 * Encode a dictionary of query parameters.
24 * @param {Object} params A dict of key/values to encode e.g.
25 * {"foo": "bar", "baz": "taz"}
26 * @return {string} The encoded string e.g. foo=bar&baz=taz
27 */
28
29var _create = require("babel-runtime/core-js/object/create");
30
31var _create2 = _interopRequireDefault(_create);
32
33var _typeof2 = require("babel-runtime/helpers/typeof");
34
35var _typeof3 = _interopRequireDefault(_typeof2);
36
37var _stringify = require("babel-runtime/core-js/json/stringify");
38
39var _stringify2 = _interopRequireDefault(_stringify);
40
41function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
42
43module.exports.encodeParams = function (params) {
44 var qs = "";
45 for (var key in params) {
46 if (!params.hasOwnProperty(key)) {
47 continue;
48 }
49 qs += "&" + encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
50 }
51 return qs.substring(1);
52};
53
54/**
55 * Encodes a URI according to a set of template variables. Variables will be
56 * passed through encodeURIComponent.
57 * @param {string} pathTemplate The path with template variables e.g. '/foo/$bar'.
58 * @param {Object} variables The key/value pairs to replace the template
59 * variables with. E.g. { "$bar": "baz" }.
60 * @return {string} The result of replacing all template variables e.g. '/foo/baz'.
61 */
62module.exports.encodeUri = function (pathTemplate, variables) {
63 for (var key in variables) {
64 if (!variables.hasOwnProperty(key)) {
65 continue;
66 }
67 pathTemplate = pathTemplate.replace(key, encodeURIComponent(variables[key]));
68 }
69 return pathTemplate;
70};
71
72/**
73 * Applies a map function to the given array.
74 * @param {Array} array The array to apply the function to.
75 * @param {Function} fn The function that will be invoked for each element in
76 * the array with the signature <code>fn(element){...}</code>
77 * @return {Array} A new array with the results of the function.
78 */
79module.exports.map = function (array, fn) {
80 var results = new Array(array.length);
81 for (var i = 0; i < array.length; i++) {
82 results[i] = fn(array[i]);
83 }
84 return results;
85};
86
87/**
88 * Applies a filter function to the given array.
89 * @param {Array} array The array to apply the function to.
90 * @param {Function} fn The function that will be invoked for each element in
91 * the array. It should return true to keep the element. The function signature
92 * looks like <code>fn(element, index, array){...}</code>.
93 * @return {Array} A new array with the results of the function.
94 */
95module.exports.filter = function (array, fn) {
96 var results = [];
97 for (var i = 0; i < array.length; i++) {
98 if (fn(array[i], i, array)) {
99 results.push(array[i]);
100 }
101 }
102 return results;
103};
104
105/**
106 * Get the keys for an object. Same as <code>Object.keys()</code>.
107 * @param {Object} obj The object to get the keys for.
108 * @return {string[]} The keys of the object.
109 */
110module.exports.keys = function (obj) {
111 var keys = [];
112 for (var key in obj) {
113 if (!obj.hasOwnProperty(key)) {
114 continue;
115 }
116 keys.push(key);
117 }
118 return keys;
119};
120
121/**
122 * Get the values for an object.
123 * @param {Object} obj The object to get the values for.
124 * @return {Array<*>} The values of the object.
125 */
126module.exports.values = function (obj) {
127 var values = [];
128 for (var key in obj) {
129 if (!obj.hasOwnProperty(key)) {
130 continue;
131 }
132 values.push(obj[key]);
133 }
134 return values;
135};
136
137/**
138 * Invoke a function for each item in the array.
139 * @param {Array} array The array.
140 * @param {Function} fn The function to invoke for each element. Has the
141 * function signature <code>fn(element, index)</code>.
142 */
143module.exports.forEach = function (array, fn) {
144 for (var i = 0; i < array.length; i++) {
145 fn(array[i], i);
146 }
147};
148
149/**
150 * The findElement() method returns a value in the array, if an element in the array
151 * satisfies (returns true) the provided testing function. Otherwise undefined
152 * is returned.
153 * @param {Array} array The array.
154 * @param {Function} fn Function to execute on each value in the array, with the
155 * function signature <code>fn(element, index, array)</code>
156 * @param {boolean} reverse True to search in reverse order.
157 * @return {*} The first value in the array which returns <code>true</code> for
158 * the given function.
159 */
160module.exports.findElement = function (array, fn, reverse) {
161 var i = void 0;
162 if (reverse) {
163 for (i = array.length - 1; i >= 0; i--) {
164 if (fn(array[i], i, array)) {
165 return array[i];
166 }
167 }
168 } else {
169 for (i = 0; i < array.length; i++) {
170 if (fn(array[i], i, array)) {
171 return array[i];
172 }
173 }
174 }
175};
176
177/**
178 * The removeElement() method removes the first element in the array that
179 * satisfies (returns true) the provided testing function.
180 * @param {Array} array The array.
181 * @param {Function} fn Function to execute on each value in the array, with the
182 * function signature <code>fn(element, index, array)</code>. Return true to
183 * remove this element and break.
184 * @param {boolean} reverse True to search in reverse order.
185 * @return {boolean} True if an element was removed.
186 */
187module.exports.removeElement = function (array, fn, reverse) {
188 var i = void 0;
189 var removed = void 0;
190 if (reverse) {
191 for (i = array.length - 1; i >= 0; i--) {
192 if (fn(array[i], i, array)) {
193 removed = array[i];
194 array.splice(i, 1);
195 return removed;
196 }
197 }
198 } else {
199 for (i = 0; i < array.length; i++) {
200 if (fn(array[i], i, array)) {
201 removed = array[i];
202 array.splice(i, 1);
203 return removed;
204 }
205 }
206 }
207 return false;
208};
209
210/**
211 * Checks if the given thing is a function.
212 * @param {*} value The thing to check.
213 * @return {boolean} True if it is a function.
214 */
215module.exports.isFunction = function (value) {
216 return Object.prototype.toString.call(value) == "[object Function]";
217};
218
219/**
220 * Checks if the given thing is an array.
221 * @param {*} value The thing to check.
222 * @return {boolean} True if it is an array.
223 */
224module.exports.isArray = function (value) {
225 return Array.isArray ? Array.isArray(value) : Boolean(value && value.constructor === Array);
226};
227
228/**
229 * Checks that the given object has the specified keys.
230 * @param {Object} obj The object to check.
231 * @param {string[]} keys The list of keys that 'obj' must have.
232 * @throws If the object is missing keys.
233 */
234module.exports.checkObjectHasKeys = function (obj, keys) {
235 for (var i = 0; i < keys.length; i++) {
236 if (!obj.hasOwnProperty(keys[i])) {
237 throw new Error("Missing required key: " + keys[i]);
238 }
239 }
240};
241
242/**
243 * Checks that the given object has no extra keys other than the specified ones.
244 * @param {Object} obj The object to check.
245 * @param {string[]} allowedKeys The list of allowed key names.
246 * @throws If there are extra keys.
247 */
248module.exports.checkObjectHasNoAdditionalKeys = function (obj, allowedKeys) {
249 for (var key in obj) {
250 if (!obj.hasOwnProperty(key)) {
251 continue;
252 }
253 if (allowedKeys.indexOf(key) === -1) {
254 throw new Error("Unknown key: " + key);
255 }
256 }
257};
258
259/**
260 * Deep copy the given object. The object MUST NOT have circular references and
261 * MUST NOT have functions.
262 * @param {Object} obj The object to deep copy.
263 * @return {Object} A copy of the object without any references to the original.
264 */
265module.exports.deepCopy = function (obj) {
266 return JSON.parse((0, _stringify2.default)(obj));
267};
268
269/**
270 * Compare two objects for equality. The objects MUST NOT have circular references.
271 *
272 * @param {Object} x The first object to compare.
273 * @param {Object} y The second object to compare.
274 *
275 * @return {boolean} true if the two objects are equal
276 */
277var deepCompare = module.exports.deepCompare = function (x, y) {
278 // Inspired by
279 // http://stackoverflow.com/questions/1068834/object-comparison-in-javascript#1144249
280
281 // Compare primitives and functions.
282 // Also check if both arguments link to the same object.
283 if (x === y) {
284 return true;
285 }
286
287 if ((typeof x === "undefined" ? "undefined" : (0, _typeof3.default)(x)) !== (typeof y === "undefined" ? "undefined" : (0, _typeof3.default)(y))) {
288 return false;
289 }
290
291 // special-case NaN (since NaN !== NaN)
292 if (typeof x === 'number' && isNaN(x) && isNaN(y)) {
293 return true;
294 }
295
296 // special-case null (since typeof null == 'object', but null.constructor
297 // throws)
298 if (x === null || y === null) {
299 return x === y;
300 }
301
302 // everything else is either an unequal primitive, or an object
303 if (!(x instanceof Object)) {
304 return false;
305 }
306
307 // check they are the same type of object
308 if (x.constructor !== y.constructor || x.prototype !== y.prototype) {
309 return false;
310 }
311
312 // special-casing for some special types of object
313 if (x instanceof RegExp || x instanceof Date) {
314 return x.toString() === y.toString();
315 }
316
317 // the object algorithm works for Array, but it's sub-optimal.
318 if (x instanceof Array) {
319 if (x.length !== y.length) {
320 return false;
321 }
322
323 for (var i = 0; i < x.length; i++) {
324 if (!deepCompare(x[i], y[i])) {
325 return false;
326 }
327 }
328 } else {
329 // disable jshint "The body of a for in should be wrapped in an if
330 // statement"
331 /* jshint -W089 */
332
333 // check that all of y's direct keys are in x
334 var p = void 0;
335 for (p in y) {
336 if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
337 return false;
338 }
339 }
340
341 // finally, compare each of x's keys with y
342 for (p in y) {
343 if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
344 return false;
345 }
346 if (!deepCompare(x[p], y[p])) {
347 return false;
348 }
349 }
350 }
351 /* jshint +W089 */
352 return true;
353};
354
355/**
356 * Copy properties from one object to another.
357 *
358 * All enumerable properties, included inherited ones, are copied.
359 *
360 * This is approximately equivalent to ES6's Object.assign, except
361 * that the latter doesn't copy inherited properties.
362 *
363 * @param {Object} target The object that will receive new properties
364 * @param {...Object} source Objects from which to copy properties
365 *
366 * @return {Object} target
367 */
368module.exports.extend = function () {
369 var target = arguments[0] || {};
370 for (var i = 1; i < arguments.length; i++) {
371 var source = arguments[i];
372 for (var propName in source) {
373 // eslint-disable-line guard-for-in
374 target[propName] = source[propName];
375 }
376 }
377 return target;
378};
379
380/**
381 * Run polyfills to add Array.map and Array.filter if they are missing.
382 */
383module.exports.runPolyfills = function () {
384 // Array.prototype.filter
385 // ========================================================
386 // SOURCE:
387 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
388 if (!Array.prototype.filter) {
389 Array.prototype.filter = function (fun /*, thisArg*/) {
390 if (this === void 0 || this === null) {
391 throw new TypeError();
392 }
393
394 var t = Object(this);
395 var len = t.length >>> 0;
396 if (typeof fun !== 'function') {
397 throw new TypeError();
398 }
399
400 var res = [];
401 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
402 for (var i = 0; i < len; i++) {
403 if (i in t) {
404 var val = t[i];
405
406 // NOTE: Technically this should Object.defineProperty at
407 // the next index, as push can be affected by
408 // properties on Object.prototype and Array.prototype.
409 // But that method's new, and collisions should be
410 // rare, so use the more-compatible alternative.
411 if (fun.call(thisArg, val, i, t)) {
412 res.push(val);
413 }
414 }
415 }
416
417 return res;
418 };
419 }
420
421 // Array.prototype.map
422 // ========================================================
423 // SOURCE:
424 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
425 // Production steps of ECMA-262, Edition 5, 15.4.4.19
426 // Reference: http://es5.github.io/#x15.4.4.19
427 if (!Array.prototype.map) {
428 Array.prototype.map = function (callback, thisArg) {
429 var T = void 0,
430 k = void 0;
431
432 if (this === null || this === undefined) {
433 throw new TypeError(' this is null or not defined');
434 }
435
436 // 1. Let O be the result of calling ToObject passing the |this|
437 // value as the argument.
438 var O = Object(this);
439
440 // 2. Let lenValue be the result of calling the Get internal
441 // method of O with the argument "length".
442 // 3. Let len be ToUint32(lenValue).
443 var len = O.length >>> 0;
444
445 // 4. If IsCallable(callback) is false, throw a TypeError exception.
446 // See: http://es5.github.com/#x9.11
447 if (typeof callback !== 'function') {
448 throw new TypeError(callback + ' is not a function');
449 }
450
451 // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
452 if (arguments.length > 1) {
453 T = thisArg;
454 }
455
456 // 6. Let A be a new array created as if by the expression new Array(len)
457 // where Array is the standard built-in constructor with that name and
458 // len is the value of len.
459 var A = new Array(len);
460
461 // 7. Let k be 0
462 k = 0;
463
464 // 8. Repeat, while k < len
465 while (k < len) {
466 var kValue, mappedValue;
467
468 // a. Let Pk be ToString(k).
469 // This is implicit for LHS operands of the in operator
470 // b. Let kPresent be the result of calling the HasProperty internal
471 // method of O with argument Pk.
472 // This step can be combined with c
473 // c. If kPresent is true, then
474 if (k in O) {
475 // i. Let kValue be the result of calling the Get internal
476 // method of O with argument Pk.
477 kValue = O[k];
478
479 // ii. Let mappedValue be the result of calling the Call internal
480 // method of callback with T as the this value and argument
481 // list containing kValue, k, and O.
482 mappedValue = callback.call(T, kValue, k, O);
483
484 // iii. Call the DefineOwnProperty internal method of A with arguments
485 // Pk, Property Descriptor
486 // { Value: mappedValue,
487 // Writable: true,
488 // Enumerable: true,
489 // Configurable: true },
490 // and false.
491
492 // In browsers that support Object.defineProperty, use the following:
493 // Object.defineProperty(A, k, {
494 // value: mappedValue,
495 // writable: true,
496 // enumerable: true,
497 // configurable: true
498 // });
499
500 // For best browser support, use the following:
501 A[k] = mappedValue;
502 }
503 // d. Increase k by 1.
504 k++;
505 }
506
507 // 9. return A
508 return A;
509 };
510 }
511
512 // Array.prototype.forEach
513 // ========================================================
514 // SOURCE:
515 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
516 // Production steps of ECMA-262, Edition 5, 15.4.4.18
517 // Reference: http://es5.github.io/#x15.4.4.18
518 if (!Array.prototype.forEach) {
519 Array.prototype.forEach = function (callback, thisArg) {
520 var T = void 0,
521 k = void 0;
522
523 if (this === null || this === undefined) {
524 throw new TypeError(' this is null or not defined');
525 }
526
527 // 1. Let O be the result of calling ToObject passing the |this| value as the
528 // argument.
529 var O = Object(this);
530
531 // 2. Let lenValue be the result of calling the Get internal method of O with the
532 // argument "length".
533 // 3. Let len be ToUint32(lenValue).
534 var len = O.length >>> 0;
535
536 // 4. If IsCallable(callback) is false, throw a TypeError exception.
537 // See: http://es5.github.com/#x9.11
538 if (typeof callback !== "function") {
539 throw new TypeError(callback + ' is not a function');
540 }
541
542 // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
543 if (arguments.length > 1) {
544 T = thisArg;
545 }
546
547 // 6. Let k be 0
548 k = 0;
549
550 // 7. Repeat, while k < len
551 while (k < len) {
552 var kValue;
553
554 // a. Let Pk be ToString(k).
555 // This is implicit for LHS operands of the in operator
556 // b. Let kPresent be the result of calling the HasProperty internal
557 // method of O with
558 // argument Pk.
559 // This step can be combined with c
560 // c. If kPresent is true, then
561 if (k in O) {
562 // i. Let kValue be the result of calling the Get internal method of O with
563 // argument Pk
564 kValue = O[k];
565
566 // ii. Call the Call internal method of callback with T as the this value and
567 // argument list containing kValue, k, and O.
568 callback.call(T, kValue, k, O);
569 }
570 // d. Increase k by 1.
571 k++;
572 }
573 // 8. return undefined
574 };
575 }
576};
577
578/**
579 * Inherit the prototype methods from one constructor into another. This is a
580 * port of the Node.js implementation with an Object.create polyfill.
581 *
582 * @param {function} ctor Constructor function which needs to inherit the
583 * prototype.
584 * @param {function} superCtor Constructor function to inherit prototype from.
585 */
586module.exports.inherits = function (ctor, superCtor) {
587 // Add Object.create polyfill for IE8
588 // Source:
589 // https://developer.mozilla.org/en-US/docs/Web/JavaScript
590 // /Reference/Global_Objects/Object/create#Polyfill
591 if (typeof _create2.default != 'function') {
592 // Production steps of ECMA-262, Edition 5, 15.2.3.5
593 // Reference: http://es5.github.io/#x15.2.3.5
594 Object.create = function () {
595 // To save on memory, use a shared constructor
596 function Temp() {}
597
598 // make a safe reference to Object.prototype.hasOwnProperty
599 var hasOwn = Object.prototype.hasOwnProperty;
600
601 return function (O) {
602 // 1. If Type(O) is not Object or Null throw a TypeError exception.
603 if ((typeof O === "undefined" ? "undefined" : (0, _typeof3.default)(O)) != 'object') {
604 throw new TypeError('Object prototype may only be an Object or null');
605 }
606
607 // 2. Let obj be the result of creating a new object as if by the
608 // expression new Object() where Object is the standard built-in
609 // constructor with that name
610 // 3. Set the [[Prototype]] internal property of obj to O.
611 Temp.prototype = O;
612 var obj = new Temp();
613 Temp.prototype = null; // Let's not keep a stray reference to O...
614
615 // 4. If the argument Properties is present and not undefined, add
616 // own properties to obj as if by calling the standard built-in
617 // function Object.defineProperties with arguments obj and
618 // Properties.
619 if (arguments.length > 1) {
620 // Object.defineProperties does ToObject on its first argument.
621 var Properties = Object(arguments[1]);
622 for (var prop in Properties) {
623 if (hasOwn.call(Properties, prop)) {
624 obj[prop] = Properties[prop];
625 }
626 }
627 }
628
629 // 5. Return obj
630 return obj;
631 };
632 }();
633 }
634 // END polyfill
635
636 // Add util.inherits from Node.js
637 // Source:
638 // https://github.com/joyent/node/blob/master/lib/util.js
639 // Copyright Joyent, Inc. and other Node contributors.
640 //
641 // Permission is hereby granted, free of charge, to any person obtaining a
642 // copy of this software and associated documentation files (the
643 // "Software"), to deal in the Software without restriction, including
644 // without limitation the rights to use, copy, modify, merge, publish,
645 // distribute, sublicense, and/or sell copies of the Software, and to permit
646 // persons to whom the Software is furnished to do so, subject to the
647 // following conditions:
648 //
649 // The above copyright notice and this permission notice shall be included
650 // in all copies or substantial portions of the Software.
651 //
652 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
653 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
654 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
655 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
656 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
657 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
658 // USE OR OTHER DEALINGS IN THE SOFTWARE.
659 ctor.super_ = superCtor;
660 ctor.prototype = (0, _create2.default)(superCtor.prototype, {
661 constructor: {
662 value: ctor,
663 enumerable: false,
664 writable: true,
665 configurable: true
666 }
667 });
668};
669
670/**
671 * Returns whether the given value is a finite number without type-coercion
672 *
673 * @param {*} value the value to test
674 * @return {boolean} whether or not value is a finite number without type-coercion
675 */
676module.exports.isNumber = function (value) {
677 return typeof value === 'number' && isFinite(value);
678};
679
680/**
681 * Removes zero width chars, diacritics and whitespace from the string
682 * @param {string} str the string to remove hidden characters from
683 * @return {string} a string with the hidden characters removed
684 */
685module.exports.removeHiddenChars = function (str) {
686 return str.normalize('NFD').replace(removeHiddenCharsRegex, '');
687};
688var removeHiddenCharsRegex = /[\u200B-\u200D\u0300-\u036f\uFEFF\s]/g;
689//# sourceMappingURL=utils.js.map
\No newline at end of file