/* jWidget Lib source file. Copyright (C) 2015 Egor Nepomnyaschih This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * @class * * `<T> extends JW.AbstractMap<T>` * * Has several events and an observable property #length. * * See structurized list of methods in JW.AbstractMap. * * @extends JW.AbstractMap * * @constructor * @param {Object} [items] Initial contents. By default, created collection is empty. * @param {boolean} [adapter] Create map as adapter of `items`. Defaults to false, so `items` is copied. */ JW.ObservableMap = function(json, adapter) { JW.ObservableMap._super.call(this, json, adapter); this.length = new JW.Property(this.getLength()); this.spliceEvent = new JW.Event(); this.reindexEvent = new JW.Event(); this.clearEvent = new JW.Event(); this.changeEvent = new JW.Event(); }; JW.extend(JW.ObservableMap, JW.AbstractMap, { /** * @property {JW.Property} length `<Number>` Collection length. **Don't modify manually!** */ /** * @event spliceEvent * Items are removed from map, items are added to map and items are updated in map. Triggered in result * of calling #set, #trySet, #setAll, #trySetAll, #remove, #tryRemove, #removeItem, #removeAll, #tryRemoveAll, * {@link #removeItems}, #splice, #trySplice, #performSplice. * @param {JW.ObservableMap.SpliceEventParams} params `<T>` Parameters. */ /** * @event reindexEvent * Keys of items are changed in map. Triggered in result * of calling #setKey, #trySetKey, #reindex, #tryReindex, #performReindex. * @param {JW.ObservableMap.ReindexEventParams} params `<T>` Parameters. */ /** * @event clearEvent * Map is cleared. Triggered in result of calling #clear, #$clear, #tryClear. * @param {JW.ObservableMap.ItemsEventParams} params `<T>` Parameters. */ /** * @event changeEvent * Map is changed. Triggered right after one * of events #spliceEvent, #reindexEvent, #clearEvent. * @param {JW.ObservableMap.EventParams} params `<T>` Parameters. */ // override destroyObject: function() { this.changeEvent.destroy(); this.clearEvent.destroy(); this.reindexEvent.destroy(); this.spliceEvent.destroy(); this.length.destroy(); this._super(); }, // override trySet: function(item, key) { var result = this._trySet(item, key); if (result === undefined) { return; } var removedItems = {}; var removedItem = result.get(); if (removedItem !== undefined) { removedItems[key] = removedItem; } var addedItems = {}; addedItems[key] = item; var spliceResult = new JW.AbstractMap.SpliceResult(removedItems, addedItems); this.length.set(this.getLength()); this.spliceEvent.trigger(new JW.ObservableMap.SpliceEventParams(this, spliceResult)); this.changeEvent.trigger(new JW.ObservableMap.EventParams(this)); if ((removedItem !== undefined) && this._ownsItems) { removedItem.destroy(); } return result; }, // override setAll: function(items) { this.trySetAll(items); }, // override trySetKey: function(oldKey, newKey) { var item = this._super(oldKey, newKey); if (item === undefined) { return; } this.reindexEvent.trigger(new JW.ObservableMap.ReindexEventParams(this, JW.Map.single(oldKey, newKey))); this.changeEvent.trigger(new JW.ObservableMap.EventParams(this)); return item; }, // override tryRemove: function(key) { var item = this._tryRemove(key); if (item === undefined) { return; } var spliceResult = new JW.AbstractMap.SpliceResult(JW.Map.single(key, item), {}); this.length.set(this.getLength()); this.spliceEvent.trigger(new JW.ObservableMap.SpliceEventParams(this, spliceResult)); this.changeEvent.trigger(new JW.ObservableMap.EventParams(this)); if (this._ownsItems) { item.destroy(); } return item; }, // override removeAll: function(keys) { this.tryRemoveAll(keys); }, // override trySplice: function(removedKeys, updatedItems) { var spliceResult = this._trySplice(removedKeys, updatedItems); if (spliceResult === undefined) { return; } this.length.set(this.getLength()); this.spliceEvent.trigger(new JW.ObservableMap.SpliceEventParams(this, spliceResult)); this.changeEvent.trigger(new JW.ObservableMap.EventParams(this)); if (this._ownsItems) { JW.Array.backEvery(JW.Map.toArray(spliceResult.removedItems), JW.destroy); } return spliceResult; }, // override tryClear: function() { var items = this._tryClear(); if (items === undefined) { return; } this.length.set(0); this.clearEvent.trigger(new JW.ObservableMap.ItemsEventParams(this, items)); this.changeEvent.trigger(new JW.ObservableMap.EventParams(this)); if (this._ownsItems) { JW.Array.backEvery(JW.Map.toArray(items), JW.destroy); } return items; }, // override tryReindex: function(keyMap) { var result = this._super(keyMap); if (result === undefined) { return; } this.reindexEvent.trigger(new JW.ObservableMap.ReindexEventParams(this, result)); this.changeEvent.trigger(new JW.ObservableMap.EventParams(this)); return result; }, /** * `<U>` Creates empty collection of the same type. * @returns {JW.ObservableMap} `<U>` Collection. */ createEmpty: function() { return new JW.ObservableMap(); }, /** * `<U>` Creates empty array of the same observability level. * @returns {JW.ObservableArray} `<U>` Array. */ createEmptyArray: function() { return new JW.ObservableArray(); }, /** * `<U>` Creates empty map of the same observability level. * @returns {JW.ObservableMap} `<U>` Map. */ createEmptyMap: function() { return new JW.ObservableMap(); }, /** * `<U>` Creates empty set of the same observability level. * @returns {JW.ObservableSet} `<U>` Set. */ createEmptySet: function() { return new JW.ObservableSet(); }, /** * `<U>` Creates collection item mapper. * Selects appropriate synchronizer implementation automatically. * @param {Object} config Configuration (see synchronizer's Config options). * @returns {JW.ObservableMap.Mapper} * `<T, U>` Synchronizer. */ createMapper: function(config) { return new JW.ObservableMap.Mapper(this, config); }, /** * Creates collection filterer. * Selects appropriate synchronizer implementation automatically. * @param {Object} config Configuration (see synchronizer's Config options). * @returns {JW.ObservableMap.Filterer} * `<T>` Synchronizer. */ createFilterer: function(config) { return new JW.ObservableMap.Filterer(this, config); }, /** * Creates matching item counter. * Selects appropriate synchronizer implementation automatically. * @param {Object} config Configuration (see synchronizer's Config options). * @returns {JW.ObservableMap.Counter} * `<T>` Synchronizer. */ createCounter: function(config) { return new JW.ObservableMap.Counter(this, config); }, /** * Creates collection observer. * Selects appropriate synchronizer implementation automatically. * @param {Object} config Configuration (see synchronizer's Config options). * @returns {JW.ObservableMap.Observer} * `<T>` Synchronizer. */ createObserver: function(config) { return new JW.ObservableMap.Observer(this, config); }, /** * Creates collection converter to array (orderer). * Selects appropriate synchronizer implementation automatically. * @param {Object} [config] Configuration (see synchronizer's Config options). * @returns {JW.ObservableMap.Orderer} * `<T>` Synchronizer. */ createOrderer: function(config) { return new JW.ObservableMap.Orderer(this, config); }, /** * Creates collection converter to array (sorter by comparer). * Selects appropriate synchronizer implementation automatically. * @param {Object} config Configuration (see synchronizer's Config options). * @returns {JW.ObservableMap.SorterComparing} * `<T>` Synchronizer. */ createSorterComparing: function(config) { return new JW.ObservableMap.SorterComparing(this, config); }, /** * Creates collection converter to map (indexer). * Selects appropriate synchronizer implementation automatically. * @param {Object} config Configuration (see synchronizer's Config options). * @returns {JW.ObservableMap.Indexer} * `<T>` Synchronizer. */ createIndexer: function(config) { return new JW.ObservableMap.Indexer(this, config); }, /** * Creates collection converter to set. * Selects appropriate synchronizer implementation automatically. * @param {Object} [config] Configuration (see synchronizer's Config options). * @returns {JW.ObservableMap.Lister} * `<T>` Synchronizer. */ createLister: function(config) { return new JW.ObservableMap.Lister(this, config); }, /** * Creates view synchronizer with map. * Selects appropriate synchronizer implementation automatically. * @param {Object} config Configuration (see synchronizer's Config options). * @returns {JW.ObservableMap.Inserter} * `<T>` Synchronizer. */ createInserter: function(config) { return new JW.ObservableMap.Inserter(this, config); } }); JW.apply(JW.ObservableMap.prototype, JW.ObservableCollection); /** * @class * `<T>` JW.ObservableMap event parameters. * @extends JW.EventParams * * @constructor * @param {JW.ObservableMap} sender `<T>` Event sender. */ JW.ObservableMap.EventParams = function(sender) { JW.ObservableMap.EventParams._super.call(this, sender); }; JW.extend(JW.ObservableMap.EventParams, JW.EventParams, { /** * @property {JW.ObservableMap} sender `<T>` Event sender. */ }); /** * @class * * `<T> extends JW.ObservableMap.EventParams<T>` * * Parameters of JW.ObservableMap#spliceEvent. * * @extends JW.ObservableMap.EventParams * * @constructor * @param {JW.ObservableMap} sender `<T>` Event sender. * @param {JW.AbstractMap.SpliceResult} spliceResult `<T>` Result of JW.AbstractMap#splice method. */ JW.ObservableMap.SpliceEventParams = function(sender, spliceResult) { JW.ObservableMap.SpliceEventParams._super.call(this, sender); this.spliceResult = spliceResult; }; JW.extend(JW.ObservableMap.SpliceEventParams, JW.ObservableMap.EventParams, { /** * @property {JW.AbstractMap.SpliceResult} spliceResult `<T>` Result of JW.AbstractMap#splice method. */ }); /** * @class * * `<T> extends JW.ObservableMap.EventParams<T>` * * Parameters of JW.ObservableMap#reindexEvent. * * @extends JW.ObservableMap.EventParams * * @constructor * @param {JW.ObservableMap} sender `<T>` Event sender. * @param {Object} keyMap Map of changed keys. */ JW.ObservableMap.ReindexEventParams = function(sender, keyMap) { JW.ObservableMap.ReindexEventParams._super.call(this, sender); this.keyMap = keyMap; }; JW.extend(JW.ObservableMap.ReindexEventParams, JW.ObservableMap.EventParams, { /** * @property {Object} keyMap Map of changed keys. */ }); /** * @class * * `<T> extends JW.ObservableMap.EventParams<T>` * * Parameters of JW.ObservableMap event which bring its old contents. * * @extends JW.ObservableMap.EventParams * * @constructor * @param {JW.ObservableMap} sender `<T>` Event sender. * @param {Object} items Old map contents. */ JW.ObservableMap.ItemsEventParams = function(sender, items) { JW.ObservableMap.ItemsEventParams._super.call(this, sender); this.items = items; }; JW.extend(JW.ObservableMap.ItemsEventParams, JW.ObservableMap.EventParams, { /** * @property {Object} items Old map contents. */ });