|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 "use strict"; |
|
5 |
|
6 module.metadata = { |
|
7 "stability": "experimental" |
|
8 }; |
|
9 |
|
10 const { Trait } = require('../deprecated/traits'); |
|
11 const { iteratorSymbol } = require('../util/iteration'); |
|
12 |
|
13 /** |
|
14 * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/list |
|
15 */ |
|
16 const Iterable = Trait.compose({ |
|
17 /** |
|
18 * Hash map of key-values to iterate over. |
|
19 * Note: That this property can be a getter if you need dynamic behavior. |
|
20 * @type {Object} |
|
21 */ |
|
22 _keyValueMap: Trait.required, |
|
23 /** |
|
24 * Custom iterator providing `Iterable`s enumeration behavior. |
|
25 * @param {Boolean} onKeys |
|
26 */ |
|
27 __iterator__: function __iterator__(onKeys, onKeyValue) { |
|
28 let map = this._keyValueMap; |
|
29 for (let key in map) |
|
30 yield onKeyValue ? [key, map[key]] : onKeys ? key : map[key]; |
|
31 } |
|
32 }); |
|
33 exports.Iterable = Iterable; |
|
34 |
|
35 /** |
|
36 * An ordered collection (also known as a sequence) disallowing duplicate |
|
37 * elements. List is composed out of `Iterable` there for it provides custom |
|
38 * enumeration behavior that is similar to array (enumerates only on the |
|
39 * elements of the list). List is a base trait and is meant to be a part of |
|
40 * composition, since all of it's API is private except length property. |
|
41 */ |
|
42 const listOptions = { |
|
43 _keyValueMap: null, |
|
44 /** |
|
45 * List constructor can take any number of element to populate itself. |
|
46 * @params {Object|String|Number} element |
|
47 * @example |
|
48 * List(1,2,3).length == 3 // true |
|
49 */ |
|
50 constructor: function List() { |
|
51 this._keyValueMap = []; |
|
52 for (let i = 0, ii = arguments.length; i < ii; i++) |
|
53 this._add(arguments[i]); |
|
54 }, |
|
55 /** |
|
56 * Number of elements in this list. |
|
57 * @type {Number} |
|
58 */ |
|
59 get length() this._keyValueMap.length, |
|
60 /** |
|
61 * Returns a string representing this list. |
|
62 * @returns {String} |
|
63 */ |
|
64 toString: function toString() 'List(' + this._keyValueMap + ')', |
|
65 /** |
|
66 * Returns `true` if this list contains the specified `element`. |
|
67 * @param {Object|Number|String} element |
|
68 * @returns {Boolean} |
|
69 */ |
|
70 _has: function _has(element) 0 <= this._keyValueMap.indexOf(element), |
|
71 /** |
|
72 * Appends the specified `element` to the end of this list, if it doesn't |
|
73 * contains it. Ignores the call if `element` is already contained. |
|
74 * @param {Object|Number|String} element |
|
75 */ |
|
76 _add: function _add(element) { |
|
77 let list = this._keyValueMap, |
|
78 index = list.indexOf(element); |
|
79 if (0 > index) |
|
80 list.push(this._public[list.length] = element); |
|
81 }, |
|
82 /** |
|
83 * Removes specified `element` from this list, if it contains it. |
|
84 * Ignores the call if `element` is not contained. |
|
85 * @param {Object|Number|String} element |
|
86 */ |
|
87 _remove: function _remove(element) { |
|
88 let list = this._keyValueMap, |
|
89 index = list.indexOf(element); |
|
90 if (0 <= index) { |
|
91 delete this._public[list.length - 1]; |
|
92 list.splice(index, 1); |
|
93 for (let length = list.length; index < length; index++) |
|
94 this._public[index] = list[index]; |
|
95 } |
|
96 }, |
|
97 /** |
|
98 * Removes all of the elements from this list. |
|
99 */ |
|
100 _clear: function _clear() { |
|
101 for (let i = 0, ii = this._keyValueMap.length; i < ii; i ++) |
|
102 delete this._public[i]; |
|
103 this._keyValueMap.splice(0); |
|
104 }, |
|
105 /** |
|
106 * Custom iterator providing `List`s enumeration behavior. |
|
107 * We cant reuse `_iterator` that is defined by `Iterable` since it provides |
|
108 * iteration in an arbitrary order. |
|
109 * @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in |
|
110 * @param {Boolean} onKeys |
|
111 */ |
|
112 __iterator__: function __iterator__(onKeys, onKeyValue) { |
|
113 let array = this._keyValueMap.slice(0), |
|
114 i = -1; |
|
115 for (let element of array) |
|
116 yield onKeyValue ? [++i, element] : onKeys ? ++i : element; |
|
117 }, |
|
118 }; |
|
119 listOptions[iteratorSymbol] = function* iterator() { |
|
120 let array = this._keyValueMap.slice(0); |
|
121 |
|
122 for (let element of array) |
|
123 yield element; |
|
124 } |
|
125 const List = Trait.resolve({ toString: null }).compose(listOptions); |
|
126 exports.List = List; |