UNPKG

@opra/common

Version:
152 lines (151 loc) 4.57 kB
function isMap(v) { return v && typeof v.forEach === 'function'; } const kEntries = Symbol.for('kEntries'); const kKeyMap = Symbol.for('kKeyMap'); const kWellKnownKeys = Symbol.for('kWellKnownKeys'); const kOptions = Symbol.for('kOptions'); const kSize = Symbol.for('kSize'); /** * A Map implementation that supports case-insensitivity and ordered keys */ export class ResponsiveMap { constructor(init, options) { Object.defineProperty(this, kSize, { value: 0, enumerable: false, writable: true, }); Object.defineProperty(this, kEntries, { value: {}, enumerable: false, writable: true, }); Object.defineProperty(this, kKeyMap, { value: {}, enumerable: false, writable: true, }); Object.defineProperty(this, kWellKnownKeys, { value: {}, enumerable: false, writable: true, }); const caseSensitive = !!options?.caseSensitive; Object.defineProperty(this, kOptions, { value: { caseSensitive, }, enumerable: false, }); if (options?.wellKnownKeys) { options.wellKnownKeys.forEach(k => { if (caseSensitive) this[kWellKnownKeys][k] = k; else this[kWellKnownKeys][k.toLowerCase()] = k; }); } this.clear(); if (init) this.setAll(init); } get size() { return this[kSize]; } clear() { Object.keys(this[kEntries]).forEach(k => delete this[kEntries][k]); Object.keys(this[kKeyMap]).forEach(k => delete this[kKeyMap][k]); this[kSize] = 0; } forEach(callbackfn, thisArg) { for (const [k, v] of this.entries()) { callbackfn.call(thisArg, v, k, this); } } get(key) { if (!key) return; return this[kEntries][this._getStoringKey(key)]; } has(key) { if (!key) return false; return Object.prototype.hasOwnProperty.call(this[kEntries], this._getStoringKey(key)); } set(key, value) { const storeKey = this._getStoringKey(key); key = this._getOriginalKey(key); const exists = Object.prototype.hasOwnProperty.call(this[kEntries], storeKey); this[kEntries][storeKey] = value; if (!exists) this[kSize]++; this[kKeyMap][storeKey] = key; return this; } setAll(source) { if (isMap(source)) source.forEach((v, k) => this.set(k, v)); else Object.keys(source).forEach(k => this.set(k, source[k])); return this; } keys() { return Object.values(this[kKeyMap])[Symbol.iterator](); } values() { return Object.values(this[kEntries])[Symbol.iterator](); } entries() { return Object.entries(this[kEntries])[Symbol.iterator](); } delete(key) { const storeKey = this._getStoringKey(key); const exists = Object.prototype.hasOwnProperty.call(this[kEntries], storeKey); delete this[kEntries][storeKey]; delete this[kKeyMap][storeKey]; if (!exists) this[kSize]--; return exists; } sort(compareFn) { const oldValues = { ...this[kEntries] }; const oldKeymap = { ...this[kKeyMap] }; const keys = Array.from(this.keys()); if (compareFn) keys.sort(compareFn); else keys.sort(); this.clear(); for (const k of keys) { this[kEntries][k] = oldValues[k]; this[kKeyMap][k] = oldKeymap[k]; } this[kSize] = keys.length; return this; } toObject() { const out = {}; for (const [storeKey, orgKey] of Object.entries(this[kKeyMap])) { out[orgKey] = this[kEntries][storeKey]; } return out; } [Symbol.iterator]() { return this.entries(); } get [Symbol.toStringTag]() { return '[Object ResponsiveMap]'; } _getOriginalKey(key) { if (!key || this[kOptions].caseSensitive) return key; const storeKey = this._getStoringKey(key); return this[kKeyMap][storeKey] ?? this[kWellKnownKeys][storeKey] ?? key; } _getStoringKey(key) { if (this[kOptions].caseSensitive) return key; return key.toLowerCase(); } }