1 /*
  2  * Filename: WeakMap.js
  3  * Created By: Ranando D. King
  4  * License: Apache 2.0
  5  *
  6  * Copyright 2014 Ranando D. King
  7  *
  8  * Licensed under the Apache License, Version 2.0 (the "License");
  9  * you may not use this file except in compliance with the License.
 10  * You may obtain a copy of the License at
 11  *
 12  * 		http://www.apache.org/licenses/LICENSE-2.0
 13  *
 14  * Unless required by applicable law or agreed to in writing, software
 15  * distributed under the License is distributed on an "AS IS" BASIS,
 16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 17  * See the License for the specific language governing permissions and
 18  * limitations under the License.
 19  */
 20 var WEAKMAP = "__$WEAKMAP$__",
 21 	WEAKMAPDATA = "__$WEAKMAPDATA$__";
 22 
 23 var WeakMapShim = function createWeakMapShim() {
 24 	//Unique key bank.
 25 	var keys = [];
 26 
 27 	var guid = function guid() {
 28         function s4() {
 29             return Math.floor((1 + Math.random()) * 0x10000)
 30                 .toString(16)
 31                 .substring(1);
 32         }
 33 
 34         return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
 35                s4() + '-' + s4() + s4() + s4();
 36     };
 37 
 38 	//Constructor
 39 	var $ = function WeakMap(preload) {
 40 		Object.defineProperty(this, "_id", {
 41 			enumerable: false,
 42 			configurable: false,
 43 			writable: true,
 44 			value: guid()
 45 		});
 46 
 47 		var isArray = Array.isArray(preload);
 48 		for (var elementId in preload) {
 49 			if (preload.hasOwnProperty(elementId)) {
 50 				var element = preload[elementId];
 51 
 52 				if (isArray)
 53 					this.set(element[0], element[1]);
 54 				else
 55 					this.set(elementId, element);
 56 			}
 57 		}
 58 	};
 59 
 60 	Object.defineProperty($.prototype, "get", {
 61 		enumerable: false,
 62 		configurable: false,
 63 		writable: false,
 64 		value: function (key) {
 65 			var retval;
 66 			if ((typeof key == "object") &&
 67 				key[WEAKMAP] && key[WEAKMAPDATA] &&
 68 				key[WEAKMAPDATA][this._id]) {
 69 				retval = key[WEAKMAPDATA][this._id][keys.indexOf(key[WEAKMAP])];
 70 			}
 71 
 72 			return retval;
 73 		}
 74 	});
 75 
 76 	Object.defineProperty($.prototype, "set", {
 77 		enumerable: false,
 78 		configurable: false,
 79 		writable: false,
 80 		value: function (key, value) {
 81 			if (typeof key != "object")
 82 				throw new Error("Invalid Key! WeakMap keys must be Objects!");
 83 
 84 			//Get a unique value for this key if it doesn't already have one.
 85 			if (!key.hasOwnProperty(WEAKMAP)) {
 86 				Object.defineProperty(key, WEAKMAP, {
 87 					enumerable: false,
 88 					configurable: false,
 89 					writable: false,
 90 					value: (function () {
 91 						var retval = null;
 92 
 93 						do{
 94 							var newKey = guid();
 95 
 96 							if (keys.indexOf(newKey) == -1) {
 97 								keys.push(newKey);
 98 								retval = newKey;
 99 							}
100 						} while (retval == null);
101 
102 						return retval;
103 					})()
104 				});
105 			}
106 
107 			//Make sure key has a datastore for the weak map's data.
108 			if (!key.hasOwnProperty(WEAKMAPDATA)) {
109 				Object.defineProperty(key, WEAKMAPDATA, {
110 					enumerable: false,
111 					configurable: false,
112 					writable: false,
113 					value: {}
114 				});
115 			}
116 
117 			if (!key[WEAKMAPDATA][this._id])
118 				key[WEAKMAPDATA][this._id] = [];
119 
120 			key[WEAKMAPDATA][this._id][keys.indexOf(key[WEAKMAP])] = value;
121 		}
122 	});
123 
124 	Object.defineProperty($.prototype, "has", {
125 		enumerable: false,
126 		configurable: false,
127 		writable: false,
128 		value: function (key) {
129 			var retval = false;
130 			if ((typeof key != "object") &&
131 				key[WEAKMAP] && key[WEAKMAPDATA] &&
132 				key[WEAKMAPDATA][this._id]) {
133 				retval = key[WEAKMAPDATA][this._id].hasOwnProperty(keys.indexOf(key[WEAKMAP]));
134 			}
135 
136 			return retval;
137 		}
138 	});
139 
140 	Object.defineProperty($.prototype, "delete", {
141 		enumerable: false,
142 		configurable: false,
143 		writable: false,
144 		value: function (key) {
145 			if ((typeof key != "object") &&
146 				key[WEAKMAP] && key[WEAKMAPDATA] &&
147 				key[WEAKMAPDATA][this._id]) {
148 				delete key[WEAKMAPDATA][this._id][keys.indexOf(key[WEAKMAP])];
149 			}
150 		}
151 	});
152 
153 	Object.defineProperty($.prototype, "clear", {
154 		enumerable: false,
155 		configurable: false,
156 		writable: false,
157 		value: function() {
158 			this._id = guid();
159 		}
160 	});
161 
162 	Object.defineProperty($.prototype, "length", {
163 		enumerable: false,
164 		configurable: false,
165 		writable: false,
166 		value: 1
167 	});
168 
169 	Object.seal($);
170 	return $;
171 };
172 
173 var WM;
174 if (typeof WeakMap != "function")
175 	WM = WeakMapShim();
176 else
177 	/*eslint-disable-next-line no-undef */
178 	WM = WeakMap;
179 
180 //If some form of require.js exists, export the class
181 if (module && exports && module.exports === exports)
182     module.exports = WM;
183