Updated the Dictionary implementation
authorAlexander Ebert <ebert@woltlab.com>
Thu, 15 Oct 2020 17:47:12 +0000 (19:47 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Wed, 28 Oct 2020 11:23:26 +0000 (12:23 +0100)
wcfsetup/install/files/js/WoltLabSuite/Core/Core.js
wcfsetup/install/files/js/WoltLabSuite/Core/Dictionary.js
wcfsetup/install/files/ts/WoltLabSuite/Core/Core.ts
wcfsetup/install/files/ts/WoltLabSuite/Core/Dictionary.ts

index 266180e087216b5f33e7c6317d958b6ff78b1027..17be029c996ce24d8e8b4a4a7f0f6565949dbcfe 100644 (file)
@@ -66,7 +66,7 @@ define(["require", "exports"], function (require, exports) {
     function extend(out, ...args) {
         out = out || {};
         const newObj = clone(out);
-        for (let i = 1, length = args.length; i < length; i++) {
+        for (let i = 0, length = args.length; i < length; i++) {
             const obj = args[i];
             if (!obj)
                 continue;
index 2467e33314329f0842da7c51cd50046f82d81776..798fd1fdb32bd3eb2a511ed1e20ef8fa08a5fc34 100644 (file)
-var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
-    if (k2 === undefined) k2 = k;
-    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
-}) : (function(o, m, k, k2) {
-    if (k2 === undefined) k2 = k;
-    o[k2] = m[k];
-}));
-var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
-    Object.defineProperty(o, "default", { enumerable: true, value: v });
-}) : function(o, v) {
-    o["default"] = v;
-});
-var __importStar = (this && this.__importStar) || function (mod) {
-    if (mod && mod.__esModule) return mod;
-    var result = {};
-    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
-    __setModuleDefault(result, mod);
-    return result;
-};
-define(["require", "exports", "./Core"], function (require, exports, Core) {
+define(["require", "exports"], function (require, exports) {
     "use strict";
-    Core = __importStar(Core);
-    var _hasMap = window.hasOwnProperty('Map') && typeof window.Map === 'function';
+    /**
+     * Dictionary implementation relying on an object or if supported on a Map to hold key => value data.
+     *
+     * If you're looking for a dictionary with object keys, please see `WoltLabSuite/Core/ObjectMap`.
+     *
+     * This is a legacy implementation, that does not implement all methods of `Map`, furthermore it has
+     * the side effect of converting all numeric keys to string values, treating 1 === "1".
+     *
+     * @author  Tim Duesterhus, Alexander Ebert
+     * @copyright  2001-2019 WoltLab GmbH
+     * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+     * @module  Dictionary (alias)
+     * @module  WoltLabSuite/Core/Dictionary
+     * @deprecated 5.4
+     */
     /**
      * @constructor
      */
-    function Dictionary() {
-        this._dictionary = (_hasMap) ? new Map() : {};
-    }
-    Dictionary.prototype = {
+    class Dictionary {
+        constructor() {
+            this._dictionary = new Map();
+        }
         /**
          * Sets a new key with given value, will overwrite an existing key.
-         *
-         * @param  {(number|string)}  key  key
-         * @param  {?}            value  value
          */
-        set: function (key, value) {
-            if (typeof key === 'number')
-                key = key.toString();
-            if (typeof key !== 'string') {
-                throw new TypeError('Only strings can be used as keys, rejected \'' + key + '\' (' + typeof key + ').');
-            }
-            if (_hasMap)
-                this._dictionary.set(key, value);
-            else
-                this._dictionary[key] = value;
-        },
+        set(key, value) {
+            this._dictionary.set(key.toString(), value);
+        }
         /**
          * Removes a key from the dictionary.
-         *
-         * @param  {(number|string)}  key  key
          */
-        'delete': function (key) {
-            if (typeof key === 'number')
-                key = key.toString();
-            if (_hasMap)
-                this._dictionary['delete'](key);
-            else
-                this._dictionary[key] = undefined;
-        },
+        delete(key) {
+            return this._dictionary.delete(key.toString());
+        }
         /**
          * Returns true if dictionary contains a value for given key and is not undefined.
-         *
-         * @param  {(number|string)}  key  key
-         * @return  {boolean}  true if key exists and value is not undefined
          */
-        has: function (key) {
-            if (typeof key === 'number')
-                key = key.toString();
-            if (_hasMap)
-                return this._dictionary.has(key);
-            else {
-                return (this._dictionary.hasOwnProperty(key) && typeof this._dictionary[key] !== 'undefined');
-            }
-        },
+        has(key) {
+            return this._dictionary.has(key.toString());
+        }
         /**
          * Retrieves a value by key, returns undefined if there is no match.
-         *
-         * @param  {(number|string)}  key  key
-         * @return  {*}
          */
-        get: function (key) {
-            if (typeof key === 'number')
-                key = key.toString();
-            if (this.has(key)) {
-                if (_hasMap)
-                    return this._dictionary.get(key);
-                else
-                    return this._dictionary[key];
-            }
-            return undefined;
-        },
+        get(key) {
+            return this._dictionary.get(key.toString());
+        }
         /**
          * Iterates over the dictionary keys and values, callback function should expect the
          * value as first parameter and the key name second.
-         *
-         * @param  {function<*, string>}  callback  callback for each iteration
          */
-        forEach: function (callback) {
-            if (typeof callback !== 'function') {
-                throw new TypeError('forEach() expects a callback as first parameter.');
-            }
-            if (_hasMap) {
-                this._dictionary.forEach(callback);
-            }
-            else {
-                var keys = Object.keys(this._dictionary);
-                for (var i = 0, length = keys.length; i < length; i++) {
-                    callback(this._dictionary[keys[i]], keys[i]);
-                }
-            }
-        },
+        forEach(callback) {
+            this._dictionary.forEach(callback);
+        }
         /**
          * Merges one or more Dictionary instances into this one.
-         *
-         * @param  {...Dictionary}    var_args  one or more Dictionary instances
          */
-        merge: function () {
-            for (var i = 0, length = arguments.length; i < length; i++) {
-                var dictionary = arguments[i];
-                if (!(dictionary instanceof Dictionary)) {
-                    throw new TypeError('Expected an object of type Dictionary, but argument ' + i + ' is not.');
-                }
-                dictionary.forEach((function (value, key) {
-                    this.set(key, value);
-                }).bind(this));
+        merge(...dictionaries) {
+            for (let i = 0, length = dictionaries.length; i < length; i++) {
+                const dictionary = dictionaries[i];
+                dictionary.forEach((value, key) => this.set(key, value));
             }
-        },
+        }
         /**
          * Returns the object representation of the dictionary.
-         *
-         * @return  {object}  dictionary's object representation
          */
-        toObject: function () {
-            if (!_hasMap)
-                return Core.clone(this._dictionary);
-            var object = {};
-            this._dictionary.forEach(function (value, key) {
-                object[key] = value;
-            });
+        toObject() {
+            const object = {};
+            this._dictionary.forEach((value, key) => object[key] = value);
             return object;
-        },
-    };
-    /**
-     * Creates a new Dictionary based on the given object.
-     * All properties that are owned by the object will be added
-     * as keys to the resulting Dictionary.
-     *
-     * @param  {object}  object
-     * @return  {Dictionary}
-     */
-    Dictionary.fromObject = function (object) {
-        var result = new Dictionary();
-        for (var key in object) {
-            if (object.hasOwnProperty(key)) {
-                result.set(key, object[key]);
-            }
         }
-        return result;
-    };
-    Object.defineProperty(Dictionary.prototype, 'size', {
-        enumerable: false,
-        configurable: true,
-        get: function () {
-            if (_hasMap) {
-                return this._dictionary.size;
-            }
-            else {
-                return Object.keys(this._dictionary).length;
+        /**
+         * Creates a new Dictionary based on the given object.
+         * All properties that are owned by the object will be added
+         * as keys to the resulting Dictionary.
+         */
+        static fromObject(object) {
+            const result = new Dictionary();
+            for (const key in object) {
+                if (object.hasOwnProperty(key)) {
+                    result.set(key, object[key]);
+                }
             }
-        },
-    });
+            return result;
+        }
+        get size() {
+            return this._dictionary.size;
+        }
+    }
     return Dictionary;
 });
index 7e31a9135b98a0b9f56c132a9932c2631fdaa2fe..ee0524bb97583755560e8b17ee00796996ee2ab9 100644 (file)
@@ -71,7 +71,7 @@ export function extend(out: object, ...args: object[]): object {
   out = out || {};
   const newObj = clone(out);
 
-  for (let i = 1, length = args.length; i < length; i++) {
+  for (let i = 0, length = args.length; i < length; i++) {
     const obj = args[i];
 
     if (!obj) continue;
index 0c9dfa5619cb49e82e1fe2995ac2d850863b4d87..b8c93704cdf315b87569c1370252839ad409c806 100644 (file)
  * Dictionary implementation relying on an object or if supported on a Map to hold key => value data.
  *
  * If you're looking for a dictionary with object keys, please see `WoltLabSuite/Core/ObjectMap`.
+ * 
+ * This is a legacy implementation, that does not implement all methods of `Map`, furthermore it has
+ * the side effect of converting all numeric keys to string values, treating 1 === "1".
  *
  * @author  Tim Duesterhus, Alexander Ebert
  * @copyright  2001-2019 WoltLab GmbH
  * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @module  Dictionary (alias)
  * @module  WoltLabSuite/Core/Dictionary
+ * @deprecated 5.4
  */
-import * as Core from './Core';
-
-var _hasMap = window.hasOwnProperty('Map') && typeof window.Map === 'function';
-
 /**
  * @constructor
  */
-function Dictionary() {
-  this._dictionary = (_hasMap) ? new Map() : {};
-}
+class Dictionary {
+  private readonly _dictionary = new Map<number | string, any>();
 
-Dictionary.prototype = {
   /**
    * Sets a new key with given value, will overwrite an existing key.
-   *
-   * @param  {(number|string)}  key  key
-   * @param  {?}            value  value
    */
-  set: function (key, value) {
-    if (typeof key === 'number') key = key.toString();
-
-    if (typeof key !== 'string') {
-      throw new TypeError('Only strings can be used as keys, rejected \'' + key + '\' (' + typeof key + ').');
-    }
-
-    if (_hasMap) this._dictionary.set(key, value);
-    else this._dictionary[key] = value;
-  },
+  set(key: number | string, value: any): void {
+    this._dictionary.set(key.toString(), value);
+  }
 
   /**
    * Removes a key from the dictionary.
-   *
-   * @param  {(number|string)}  key  key
    */
-  'delete': function (key) {
-    if (typeof key === 'number') key = key.toString();
-
-    if (_hasMap) this._dictionary['delete'](key);
-    else this._dictionary[key] = undefined;
-  },
+  delete(key: number | string): boolean {
+    return this._dictionary.delete(key.toString());
+  }
 
   /**
    * Returns true if dictionary contains a value for given key and is not undefined.
-   *
-   * @param  {(number|string)}  key  key
-   * @return  {boolean}  true if key exists and value is not undefined
    */
-  has: function (key) {
-    if (typeof key === 'number') key = key.toString();
-
-    if (_hasMap) return this._dictionary.has(key);
-    else {
-      return (this._dictionary.hasOwnProperty(key) && typeof this._dictionary[key] !== 'undefined');
-    }
-  },
+  has(key: number | string): boolean {
+    return this._dictionary.has(key.toString());
+  }
 
   /**
    * Retrieves a value by key, returns undefined if there is no match.
-   *
-   * @param  {(number|string)}  key  key
-   * @return  {*}
    */
-  get: function (key) {
-    if (typeof key === 'number') key = key.toString();
-
-    if (this.has(key)) {
-      if (_hasMap) return this._dictionary.get(key);
-      else return this._dictionary[key];
-    }
-
-    return undefined;
-  },
+  get(key: number | string): any {
+    return this._dictionary.get(key.toString());
+  }
 
   /**
    * Iterates over the dictionary keys and values, callback function should expect the
    * value as first parameter and the key name second.
-   *
-   * @param  {function<*, string>}  callback  callback for each iteration
    */
-  forEach: function (callback) {
-    if (typeof callback !== 'function') {
-      throw new TypeError('forEach() expects a callback as first parameter.');
-    }
-
-    if (_hasMap) {
-      this._dictionary.forEach(callback);
-    } else {
-      var keys = Object.keys(this._dictionary);
-      for (var i = 0, length = keys.length; i < length; i++) {
-        callback(this._dictionary[keys[i]], keys[i]);
-      }
-    }
-  },
+  forEach(callback: (value: any, key: string) => void): void {
+    this._dictionary.forEach(callback);
+  }
 
   /**
    * Merges one or more Dictionary instances into this one.
-   *
-   * @param  {...Dictionary}    var_args  one or more Dictionary instances
    */
-  merge: function () {
-    for (var i = 0, length = arguments.length; i < length; i++) {
-      var dictionary = arguments[i];
-      if (!(dictionary instanceof Dictionary)) {
-        throw new TypeError('Expected an object of type Dictionary, but argument ' + i + ' is not.');
-      }
+  merge(...dictionaries: Dictionary[]): void {
+    for (let i = 0, length = dictionaries.length; i < length; i++) {
+      const dictionary = dictionaries[i];
 
-      (dictionary as any).forEach((function (value, key) {
-        this.set(key, value);
-      }).bind(this));
+      dictionary.forEach((value, key) => this.set(key, value));
     }
-  },
+  }
 
   /**
    * Returns the object representation of the dictionary.
-   *
-   * @return  {object}  dictionary's object representation
    */
-  toObject: function () {
-    if (!_hasMap) return Core.clone(this._dictionary);
-
-    var object = {};
-    this._dictionary.forEach(function (value, key) {
-      object[key] = value;
-    });
+  toObject(): object {
+    const object = {};
+    this._dictionary.forEach((value, key) => object[key] = value);
 
     return object;
-  },
-};
-
-/**
- * Creates a new Dictionary based on the given object.
- * All properties that are owned by the object will be added
- * as keys to the resulting Dictionary.
- *
- * @param  {object}  object
- * @return  {Dictionary}
- */
-Dictionary.fromObject = function (object) {
-  var result = new Dictionary();
-
-  for (var key in object) {
-    if (object.hasOwnProperty(key)) {
-      result.set(key, object[key]);
-    }
   }
 
-  return result;
-};
+  /**
+   * Creates a new Dictionary based on the given object.
+   * All properties that are owned by the object will be added
+   * as keys to the resulting Dictionary.
+   */
+  static fromObject (object: object): Dictionary {
+    const result = new Dictionary();
 
-Object.defineProperty(Dictionary.prototype, 'size', {
-  enumerable: false,
-  configurable: true,
-  get: function () {
-    if (_hasMap) {
-      return this._dictionary.size;
-    } else {
-      return Object.keys(this._dictionary).length;
+    for (const key in object) {
+      if (object.hasOwnProperty(key)) {
+        result.set(key, object[key]);
+      }
     }
-  },
-});
+
+    return result;
+  }
+  
+  get size(): number {
+    return this._dictionary.size;
+  }
+}
 
 export = Dictionary;