1 /*
  2  * Filename: Enum.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 
 21 var Enum = function Enum(defValName, params) {
 22 	var $ = function EnumType(param) {
 23         var self = this;
 24 
 25 		if ((param !== null) && (param !== undefined)) {
 26 			var n;
 27 			var v;
 28 			if ((typeof(param) == "object") && ("name" in param) && ("value" in param)) {
 29 				n = param.name;
 30 				v = param.value;
 31 			}
 32 			else {
 33 				n = param;
 34 				v = param;
 35 			}
 36 
 37 			try {
 38 				this.value = v;
 39 				if (n === v)
 40 					n = undefined;
 41 			}
 42 			catch (err1) {
 43 				try {
 44 					this.name = n;
 45 					if (n === v)
 46 						v = undefined;
 47 				}
 48 				catch (err2) {
 49 					throw new Error("Parameter '" + param + "' is not a name or value match for any member of this Enum!");
 50 				}
 51 			}
 52 			
 53 			if (((n === undefined) && (this.value !== v)) ||
 54 				((v === undefined) && (this.name != n)) ||
 55 				((n !== undefined) && (v !== undefined) &&
 56 				 (this.value !== v) && (this.name != n)))
 57 				throw new Error("Parameter '" + param + "' is not a name or value match for any member of this Enum!");
 58             else if (Object.isFrozen($))
 59                 self = $[this.name];
 60 		}
 61 		else {
 62 			self = $[defValName];
 63         }
 64 
 65         return self;
 66 	};
 67 	var validDefault = false;
 68 	
 69 	var addEnum = function(key, value) {
 70 		Object.defineProperty($, key, {
 71 			enumerable: true,
 72 			configurable: false,
 73 			writable: false,
 74 			value: new $(value)
 75 		});
 76 
 77         Object.freeze($[key]);
 78 	};
 79     
 80     Object.defineProperties($, {
 81         isMember: {
 82             enumerable: true,
 83             value: function isMember(obj) {
 84                 var retval = false;
 85                
 86                 if (obj && obj.isEnum)
 87                     retval = (params[obj.name] === obj.value);
 88                 
 89                 return retval;
 90             }
 91         },
 92         isEnumType: { value: true },
 93         memberByName: {
 94             enumerable: true,
 95             value: function memberByName(mName) {
 96                 var retval = null;
 97                 if (params.hasOwnProperty(mName))
 98                     retval = new $(mName);
 99 
100                 return retval;
101             }
102         }
103     });
104 	
105 	Object.defineProperties($.prototype, {
106         "isEnum": { value: true },
107         "valueOf": {
108             enumerable: false,
109             configurable: false,
110             writable: false,
111             value: function() { return this.value; }
112         },
113         "_value": {
114             enumerable: false,
115             configurable: false,
116             writable: true,
117             value: null
118         },
119         "name": {
120             enumerable: true,
121             configurable: false,
122             get: function() {
123                 var retval;
124                 for (var key in params)
125                     if (params.hasOwnProperty(key) && (params[key] === this._value)) {
126                         retval = key;
127                         break;
128                     }
129 
130                 return retval;
131             },
132             set: function(n) {
133                 if (params.hasOwnProperty(n))
134                     this._value = params[n];
135                 else
136                     throw new Error("Element name '" + n + "' is not a member of this Enum!");
137             }
138         },
139         "value": {
140             enumerable: true,
141             configurable: false,
142             get: function() { return this._value; },
143             set: function(v) {
144                 if (this._value != v) {
145                     for (var key in params)
146                         if (params.hasOwnProperty(key) && (params[key] === v)) {
147                             this._value = v;
148                             break;
149                         }
150 
151                     if (this._value != v)
152                         throw new Error("Element value '" + v + "' is not a member of this Enum!");
153                 }
154             }
155         },
156         "toString": {
157             enumerable: false,
158             configurable: false,
159             writable: false,
160             value: function() {
161                 return this._value.toString() + " (" + this.name +")";
162             }
163         },
164         "toJSON": {
165             enumerable: false,
166             configurable: false,
167             writable: false,
168             value: function() {
169                 return { name: this.name, value: this._value };
170             }
171         }
172     });
173 	
174 	if (Array.isArray(params)) {
175 		var len = params.length;
176 		for (var i=0; i<len; ++i) {
177 			var key = params[i];
178 			validDefault |= (key === defValName);
179 			params[key] = i;		//re-attach the index to params as a key:value pair.
180 			delete params[i];		//remove the array element.
181 			addEnum(key, i);
182 		}		
183 	}
184 	else {
185 		for (var key in params) {
186 			if (params.hasOwnProperty(key)) {
187 				validDefault |= (key === defValName);
188 				addEnum(key, params[key]);
189 			}
190 		}
191 	}
192 	
193 	if (!validDefault)
194 		throw new Error("Default value '" + defValName + "' is not a member of this Enum!");
195 	
196 	Object.seal($.prototype);
197 	Object.freeze($);
198 
199 	return $;
200 };
201 
202 //If some form of require.js exists, export the class
203 if (module && exports && module.exports === exports)
204     module.exports = Enum;
205