UNPKG

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