| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 | // stringmap.js// MIT licensed, see LICENSE file// Copyright (c) 2013 Olov Lassus <olov.lassus@gmail.com>var StringMap = (function() {    "use strict";    // to save us a few characters    var hasOwnProperty = Object.prototype.hasOwnProperty;    var create = (function() {        function hasOwnEnumerableProps(obj) {            for (var prop in obj) {                if (hasOwnProperty.call(obj, prop)) {                    return true;                }            }            return false;        }        // FF <= 3.6:        // o = {}; o.hasOwnProperty("__proto__" or "__count__" or "__parent__") => true        // o = {"__proto__": null}; Object.prototype.hasOwnProperty.call(o, "__proto__" or "__count__" or "__parent__") => false        function hasOwnPollutedProps(obj) {            return hasOwnProperty.call(obj, "__count__") || hasOwnProperty.call(obj, "__parent__");        }        var useObjectCreate = false;        if (typeof Object.create === "function") {            if (!hasOwnEnumerableProps(Object.create(null))) {                useObjectCreate = true;            }        }        if (useObjectCreate === false) {            if (hasOwnEnumerableProps({})) {                throw new Error("StringMap environment error 0, please file a bug at https://github.com/olov/stringmap/issues");            }        }        // no throw yet means we can create objects without own enumerable props (safe-guard against VMs and shims)        var o = (useObjectCreate ? Object.create(null) : {});        var useProtoClear = false;        if (hasOwnPollutedProps(o)) {            o.__proto__ = null;            if (hasOwnEnumerableProps(o) || hasOwnPollutedProps(o)) {                throw new Error("StringMap environment error 1, please file a bug at https://github.com/olov/stringmap/issues");            }            useProtoClear = true;        }        // no throw yet means we can create objects without own polluted props (safe-guard against VMs and shims)        return function() {            var o = (useObjectCreate ? Object.create(null) : {});            if (useProtoClear) {                o.__proto__ = null;            }            return o;        };    })();    // stringmap ctor    function stringmap(optional_object) {        // use with or without new        if (!(this instanceof stringmap)) {            return new stringmap(optional_object);        }        this.obj = create();        this.hasProto = false; // false (no __proto__ key) or true (has __proto__ key)        this.proto = undefined; // value for __proto__ key when hasProto is true, undefined otherwise        if (optional_object) {            this.setMany(optional_object);        }    };    // primitive methods that deals with data representation    stringmap.prototype.has = function(key) {        // The type-check of key in has, get, set and delete is important because otherwise an object        // {toString: function() { return "__proto__"; }} can avoid the key === "__proto__" test.        // The alternative to type-checking would be to force string conversion, i.e. key = String(key);        if (typeof key !== "string") {            throw new Error("StringMap expected string key");        }        return (key === "__proto__" ?            this.hasProto :            hasOwnProperty.call(this.obj, key));    };    stringmap.prototype.get = function(key) {        if (typeof key !== "string") {            throw new Error("StringMap expected string key");        }        return (key === "__proto__" ?            this.proto :            (hasOwnProperty.call(this.obj, key) ? this.obj[key] : undefined));    };    stringmap.prototype.set = function(key, value) {        if (typeof key !== "string") {            throw new Error("StringMap expected string key");        }        if (key === "__proto__") {            this.hasProto = true;            this.proto = value;        } else {            this.obj[key] = value;        }    };    stringmap.prototype.remove = function(key) {        if (typeof key !== "string") {            throw new Error("StringMap expected string key");        }        var didExist = this.has(key);        if (key === "__proto__") {            this.hasProto = false;            this.proto = undefined;        } else {            delete this.obj[key];        }        return didExist;    };    // alias remove to delete but beware:    // sm.delete("key"); // OK in ES5 and later    // sm['delete']("key"); // OK in all ES versions    // sm.remove("key"); // OK in all ES versions    stringmap.prototype['delete'] = stringmap.prototype.remove;    stringmap.prototype.isEmpty = function() {        for (var key in this.obj) {            if (hasOwnProperty.call(this.obj, key)) {                return false;            }        }        return !this.hasProto;    };    stringmap.prototype.size = function() {        var len = 0;        for (var key in this.obj) {            if (hasOwnProperty.call(this.obj, key)) {                ++len;            }        }        return (this.hasProto ? len + 1 : len);    };    stringmap.prototype.keys = function() {        var keys = [];        for (var key in this.obj) {            if (hasOwnProperty.call(this.obj, key)) {                keys.push(key);            }        }        if (this.hasProto) {            keys.push("__proto__");        }        return keys;    };    stringmap.prototype.values = function() {        var values = [];        for (var key in this.obj) {            if (hasOwnProperty.call(this.obj, key)) {                values.push(this.obj[key]);            }        }        if (this.hasProto) {            values.push(this.proto);        }        return values;    };    stringmap.prototype.items = function() {        var items = [];        for (var key in this.obj) {            if (hasOwnProperty.call(this.obj, key)) {                items.push([key, this.obj[key]]);            }        }        if (this.hasProto) {            items.push(["__proto__", this.proto]);        }        return items;    };    // methods that rely on the above primitives    stringmap.prototype.setMany = function(object) {        if (object === null || (typeof object !== "object" && typeof object !== "function")) {            throw new Error("StringMap expected Object");        }        for (var key in object) {            if (hasOwnProperty.call(object, key)) {                this.set(key, object[key]);            }        }        return this;    };    stringmap.prototype.merge = function(other) {        var keys = other.keys();        for (var i = 0; i < keys.length; i++) {            var key = keys[i];            this.set(key, other.get(key));        }        return this;    };    stringmap.prototype.map = function(fn) {        var keys = this.keys();        for (var i = 0; i < keys.length; i++) {            var key = keys[i];            keys[i] = fn(this.get(key), key); // re-use keys array for results        }        return keys;    };    stringmap.prototype.forEach = function(fn) {        var keys = this.keys();        for (var i = 0; i < keys.length; i++) {            var key = keys[i];            fn(this.get(key), key);        }    };    stringmap.prototype.clone = function() {        var other = stringmap();        return other.merge(this);    };    stringmap.prototype.toString = function() {        var self = this;        return "{" + this.keys().map(function(key) {            return JSON.stringify(key) + ":" + JSON.stringify(self.get(key));        }).join(",") + "}";    };    return stringmap;})();if (typeof module !== "undefined" && typeof module.exports !== "undefined") {    module.exports = StringMap;}
 |