Add content selection before removing content
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / WoltLabSuite / Core / Dictionary.js
1 /**
2 * Dictionary implementation relying on an object or if supported on a Map to hold key => value data.
3 *
4 * If you're looking for a dictionary with object keys, please see `WoltLabSuite/Core/ObjectMap`.
5 *
6 * @author Tim Duesterhus, Alexander Ebert
7 * @copyright 2001-2018 WoltLab GmbH
8 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
9 * @module WoltLabSuite/Core/Dictionary
10 */
11 define(['Core'], function(Core) {
12 "use strict";
13
14 var _hasMap = objOwns(window, 'Map') && typeof window.Map === 'function';
15
16 /**
17 * @constructor
18 */
19 function Dictionary() {
20 this._dictionary = (_hasMap) ? new Map() : {};
21 }
22 Dictionary.prototype = {
23 /**
24 * Sets a new key with given value, will overwrite an existing key.
25 *
26 * @param {(number|string)} key key
27 * @param {?} value value
28 */
29 set: function(key, value) {
30 if (typeof key === 'number') key = key.toString();
31
32 if (typeof key !== "string") {
33 throw new TypeError("Only strings can be used as keys, rejected '" + key + "' (" + typeof key + ").");
34 }
35
36 if (_hasMap) this._dictionary.set(key, value);
37 else this._dictionary[key] = value;
38 },
39
40 /**
41 * Removes a key from the dictionary.
42 *
43 * @param {(number|string)} key key
44 */
45 'delete': function(key) {
46 if (typeof key === 'number') key = key.toString();
47
48 if (_hasMap) this._dictionary['delete'](key);
49 else this._dictionary[key] = undefined;
50 },
51
52 /**
53 * Returns true if dictionary contains a value for given key and is not undefined.
54 *
55 * @param {(number|string)} key key
56 * @return {boolean} true if key exists and value is not undefined
57 */
58 has: function(key) {
59 if (typeof key === 'number') key = key.toString();
60
61 if (_hasMap) return this._dictionary.has(key);
62 else {
63 return (objOwns(this._dictionary, key) && typeof this._dictionary[key] !== "undefined");
64 }
65 },
66
67 /**
68 * Retrieves a value by key, returns undefined if there is no match.
69 *
70 * @param {(number|string)} key key
71 * @return {*}
72 */
73 get: function(key) {
74 if (typeof key === 'number') key = key.toString();
75
76 if (this.has(key)) {
77 if (_hasMap) return this._dictionary.get(key);
78 else return this._dictionary[key];
79 }
80
81 return undefined;
82 },
83
84 /**
85 * Iterates over the dictionary keys and values, callback function should expect the
86 * value as first parameter and the key name second.
87 *
88 * @param {function<*, string>} callback callback for each iteration
89 */
90 forEach: function(callback) {
91 if (typeof callback !== "function") {
92 throw new TypeError("forEach() expects a callback as first parameter.");
93 }
94
95 if (_hasMap) {
96 this._dictionary.forEach(callback);
97 }
98 else {
99 var keys = Object.keys(this._dictionary);
100 for (var i = 0, length = keys.length; i < length; i++) {
101 callback(this._dictionary[keys[i]], keys[i]);
102 }
103 }
104 },
105
106 /**
107 * Merges one or more Dictionary instances into this one.
108 *
109 * @param {...Dictionary} var_args one or more Dictionary instances
110 */
111 merge: function() {
112 for (var i = 0, length = arguments.length; i < length; i++) {
113 var dictionary = arguments[i];
114 if (!(dictionary instanceof Dictionary)) {
115 throw new TypeError("Expected an object of type Dictionary, but argument " + i + " is not.");
116 }
117
118 dictionary.forEach((function(value, key) {
119 this.set(key, value);
120 }).bind(this));
121 }
122 },
123
124 /**
125 * Returns the object representation of the dictionary.
126 *
127 * @return {object} dictionary's object representation
128 */
129 toObject: function() {
130 if (!_hasMap) return Core.clone(this._dictionary);
131
132 var object = { };
133 this._dictionary.forEach(function(value, key) {
134 object[key] = value;
135 });
136
137 return object;
138 }
139 };
140
141 /**
142 * Creates a new Dictionary based on the given object.
143 * All properties that are owned by the object will be added
144 * as keys to the resulting Dictionary.
145 *
146 * @param {object} object
147 * @return {Dictionary}
148 */
149 Dictionary.fromObject = function(object) {
150 var result = new Dictionary();
151
152 for (var key in object) {
153 if (objOwns(object, key)) {
154 result.set(key, object[key]);
155 }
156 }
157
158 return result;
159 };
160
161 Object.defineProperty(Dictionary.prototype, 'size', {
162 enumerable: false,
163 configurable: true,
164 get: function() {
165 if (_hasMap) {
166 return this._dictionary.size;
167 }
168 else {
169 return Object.keys(this._dictionary).length;
170 }
171 }
172 });
173
174 return Dictionary;
175 });