DataMap.js

/* global window */
import './polyfills/Map.js';
import {arrayCache, greenSplice} from './utils/array.js';
import config from 'config';
import recycle from 'recycle';

export default (function () {
    var Map = window.Map,
        mapSet = function (keys, key, value) {
            if (this.get(key) !== value) {
                if (!this.has(key)) {
                    keys.push(key);
                }
                this.set(key, value);
            }
            return value;
        },
        mapDelete = function (keys, key) {
            var i = keys.indexOf(key),
                value = this.get(key);
            
            if (i >= 0) {
                greenSplice(keys, i);
                this.delete(key);
            }
            
            return value;
        },
        mapClear = function (keys) {
            var i = keys.length;
            
            while (i--) {
                this.delete(keys[i]);
            }
            keys.length = 0;
        },
        mapToJSON = function (keys) {
            var i = keys.length,
                json = {};
            
            while (i--) {
                json[keys[i]] = this.get(keys[i]);
            }

            return json;
        },
        mapMethods = {
            get: {
                value: null
            },
            has: {
                value: null
            },
            keys: {
                value: null
            },
            set: {
                value: null
            },
            delete: {
                value: null
            },
            clear: {
                value: null
            },
            toJSON: {
                value: null
            }
        },

        /**
         * This class defines a generic iterable data object. It behaves similarly to Map but maintains a list of keys as an Array. It includes recycle methods to encourage reuse.
         *
         * @memberof platypus
         * @class DataMap
         * @uses Map
         * @return {platypus.DataMap} Returns the new DataMap object.
         */
        DataMap = function (first) {
            var i = arguments.length,
                key = '',
                keys = null,
                map = null,
                mm = null;
            
            if (!this.map) {
                mm = mapMethods;
                map = this.map = new Map();
                
                /**
                 * Tracks keys on this object to make iteration faster.
                 *
                 * @property keys
                 * @type Array
                 * @default []
                 */
                keys = mm.keys.value = arrayCache.setUp();
                
                /**
                 * Returns the value of the provided key.
                 *
                 * @method platypus.DataMap#get
                 * @param key {String} The key to lookup.
                 * @return value {any} The value of the provded key.
                 */
                mm.get.value = map.get.bind(map);
                
                /**
                 * Determines whether the provided key is available on this DataMap.
                 *
                 * @method platypus.DataMap#has
                 * @param key {String} The key to lookup.
                 * @return value {Boolean} Whether the key is listed in this DataMap.
                 */
                mm.has.value = map.has.bind(map);
                
                /**
                 * Sets a value to a key in the DataMap.
                 *
                 * @method platypus.DataMap#set
                 * @param key {String} The key to associate with the provided value.
                 * @param value {any} The value to be stored by the DataMap.
                 * @return value {any} The value passed in is returned for chaining.
                 */
                mm.set.value = mapSet.bind(map, keys);
                
                /**
                 * Deletes a key (and value) from the DataMap.
                 *
                 * @method platypus.DataMap#delete
                 * @param key {String} The key to delete from the DataMap.
                 * @return value {any} The value of the key is returned.
                 */
                mm.delete.value = mapDelete.bind(map, keys);
                
                /**
                 * Clears out of keys (and values) from the DataMap.
                 *
                 * @method platypus.DataMap#clear
                 */
                mm.clear.value = mapClear.bind(map, keys);
                            
                /**
                 * Returns a JSON object describing the component.
                 *
                 * @method platypus.DataMap#toJSON
                 * @return {Object} Returns a JSON definition that can be used to recreate the component.
                 **/
                mm.toJSON.value = mapToJSON.bind(map, keys);
                
                Object.defineProperties(this, mm);
            }
            
            if (first) {
                keys = first.keys;

                if (typeof first === 'string') {
                    if (i % 2) {
                        i -= 1;
                        this.set(arguments[i], null);
                    }
                    while (i) {
                        i -= 2;
                        this.set(arguments[i], arguments[i + 1]);
                    }
                } else if (keys) {
                    i = keys.length;
                    while (i--) {
                        this.set(keys[i], first.get(keys[i]));
                    }
                } else {
                    for (key in first) {
                        if (first.hasOwnProperty(key)) {
                            this.set(key, first[key]);
                        }
                    }
                }
            }
        };

    /**
     * Returns DataMap from cache or creates a new one if none are available.
     *
     * @method platypus.DataMap.setUp
     * @return dataMap {platypus.DataMap} The instantiated DataMap.
     */
    /**
     * Returns DataMap back to the cache. Prefer the DataMap's recycle method since it recycles property objects as well.
     *
     * @method platypus.DataMap.recycle
     * @param dataMap {platypus.DataMap} The DataMap to be recycled.
     */
    /**
     * Relinquishes DataMap properties and recycles it.
     *
     * @method platypus.DataMap#recycle
     */
    recycle.add(DataMap, 'DataMap', DataMap, function () {
        this.clear();
    }, true, config.dev);
    
    return DataMap;
}());