1 //
  2 // Copyright 2010, Tridium, Inc. All Rights Reserved.
  3 //
  4 
  5 /**
  6  * Core Object Architecture for BajaScript.
  7  *
  8  * @author Gareth Johnson
  9  * @version 1.0.0.0
 10  */
 11 
 12 //JsLint options (see http://www.jslint.com )
 13 /*jslint rhino: true, onevar: false, plusplus: true, white: true, undef: false, nomen: false, eqeqeq: true, 
 14 bitwise: true, regexp: true, newcap: true, immed: true, strict: false, indent: 2, vars: true, continue: true */
 15 
 16 // Globals for JsLint to ignore 
 17 /*global baja, BaseBajaObj*/ 
 18   
 19 (function obj(baja) {
 20 
 21   // Use ECMAScript 5 Strict Mode
 22   "use strict";
 23    
 24   // Create local for improved minification
 25   var strictArg = baja.strictArg,
 26       strictAllArgs = baja.strictAllArgs,
 27       bajaDef = baja.def,
 28       objectify = baja.objectify,
 29       bajaHasType = baja.hasType;
 30    
 31   ////////////////////////////////////////////////////////////////
 32   // Baja Objects
 33   //////////////////////////////////////////////////////////////// 
 34 
 35   /**
 36    * @class Represents baja:Object in BajaScript.
 37    * <p>
 38    * Since this Constructor represents an abstract class, it should never
 39    * be directly used to create a new Object.
 40    *
 41    * @name baja.Object
 42    * @extend BaseBajaObj
 43    */
 44   baja.Object = function () {
 45     baja.Object.$super.apply(this, arguments);  
 46   }.$extend(BaseBajaObj);
 47   
 48   // Default equals equivalent  
 49   var equalsEquivalent = function (obj) {
 50     // By default, just perform equals
 51     return this.equals(obj);
 52   };
 53   
 54   /**
 55    * Equivalence test.
 56    * <p>
 57    * Equivalent is used to compare if two objects have equivalent
 58    * state, but might not want to return true for equals since it
 59    * it has implied semantics for many operations.  The default
 60    * implementation returns the result of <code>equals()</code>.
 61    * 
 62    * @param obj
 63    * @returns {Boolean}
 64    */
 65   baja.Object.prototype.equivalent = equalsEquivalent;
 66 
 67   var objectGetIcon = function () {
 68     return this.getType().getIcon();
 69   };
 70   
 71   /**
 72    * Return the Object's Icon.
 73    * 
 74    * @returns {baja.Icon}
 75    */
 76   baja.Object.prototype.getIcon = objectGetIcon;
 77   
 78   /**
 79    * @class Represents baja:Singleton in BajaScript.
 80    * <p>
 81    * Since this Constructor represents an abstract class, it should never
 82    * be directly used to create a new Object.
 83    *
 84    * @name baja.Singleton
 85    * @extends baja.Object
 86    */
 87   baja.Singleton = function () {
 88     baja.Singleton.$super.apply(this, arguments); 
 89   }.$extend(baja.Object).registerType("baja:Singleton");
 90   
 91   /**
 92    * @class Represents baja:Value in BajaScript.
 93    * <p>
 94    * Since this Constructor represents an abstract class, it should never
 95    * be directly used to create a new Object.
 96    *
 97    * @name baja.Value
 98    * @extends baja.Object
 99    */
100   baja.Value = function () {
101     baja.Value.$super.apply(this, arguments);
102   }.$extend(baja.Object);
103   
104   // The default newCopy method  
105   var defaultNewCopy = function (exact) {
106     return this;
107   };
108   
109   /**
110    * Every value may be cloned using the newCopy method.
111    * <p>
112    * Please note that Simples are immutable so they don't
113    * allocate a new instance.   
114    *
115    * @see baja.Simple
116    *
117    * @param {Boolean} [exact] true if an exact copy of the value should be made (only valid in the Component architecture).
118    * @returns a copy of the value (or the same instance if the value is a Simple).
119    */
120   baja.Value.prototype.newCopy = defaultNewCopy;
121     
122   // A caching function wrapper for decodeFromString
123   var cacheDecode = function (decodeFunc) {
124     // Since Simples are immutable, this function will cache the String used to decode the Simple
125     // so when encodeToString is called, this string can be returned
126     return function decodeFromString(str) {
127       var v = decodeFunc.call(this, str);
128       v.$cEncStr = str;
129       return v;    
130     };
131   };
132 
133   // A caching function wrapper for encodeToString
134   var cacheEncode = function (encodeFunc) {
135     // Since Simples are immutable, this function will attempt to get or create a cached string 
136     // encoded value
137     return function encodeToString() {
138       if (this.$cEncStr === undefined) {
139         this.$cEncStr = encodeFunc.call(this);
140       }
141       return this.$cEncStr;
142     };
143   };
144   
145   // Removes caching wrapper for Simple constants (mainly needed for number Simples)
146   var uncacheConstantEncodeDecode = function (constant) {
147     // Take the encodeToString and decodeToString on the prototype chain 
148     // and apply it directly as a property on the constant
149     var c = constant; // Workaround for JsLint
150     constant.decodeFromString = c.decodeFromString;
151     constant.encodeToString = c.encodeToString;
152     return constant;
153   };
154   
155   /**
156    * @class Represents baja:Simple in BajaScript.
157    * <p>
158    * Simples are immutable and represent primitive data types in Niagara. They are the
159    * basic building blocks of the architecture. Simples contain no slots themselves but do
160    * contain an implicit data value that can be encoded and decoded in a String format.
161    * <p>
162    * Simples must be immutable and under no circumstances should there be any attempt
163    * to modify the contents of a Simple.
164    * <p>
165    * all Simples must conform to the following conventions... 
166    * <ul>
167    *   <li>Define a DEFAULT instance on the Simple Constructor.</li>
168    *   <li>Define a make method.</li>
169    *   <li>Define a decodeFromString method on the Object's instance that takes a String and returns an instance of the Simple.</li>
170    *   <li>Define an encodeToString method on the Object's instance that encodes the value to a String.</li>
171    * </ul>
172    * <p>
173    * Since this Constructor represents an abstract class, it should never
174    * be directly used to create a new Object.
175    *
176    * @name baja.Simple
177    * @extends baja.Value
178    */
179   baja.Simple = function () { 
180     baja.Simple.$super.apply(this, arguments);
181   }.$extend(baja.Value);
182   
183   /**
184    * Equality test.
185    * 
186    * @param obj
187    * @returns {Boolean}
188    */
189   baja.Simple.prototype.equals = function (obj) {
190     // Comparing in via encodeToString is ok because most Simples
191     // lazily cache their string encoding (since they're all immutable)    
192     return bajaHasType(obj) && 
193            obj.getType().equals(this.getType()) && 
194            obj.encodeToString() === this.encodeToString();
195   };
196       
197   ////////////////////////////////////////////////////////////////
198   // Default Simple
199   //////////////////////////////////////////////////////////////// 
200   
201   /**
202    * @class DefaultSimple
203    * <p>
204    * Most of the core Simples in BajaScript are represented (i.e. 'baja:String', 'baja:Double' etc). However, they'll always 
205    * be some Simples that BajaScript won't have support for. If a dedicated JS Constructor for a Simple can't be found, it'll
206    * default back to an instance of a DefaultSimple.
207    * <p>
208    * A DefaultSimple holds the decoded String representation of a Simple.
209    * <p>
210    * When creating a Simple, always use the 'make' method instead of creating a new Object.
211    *
212    * @name baja.DefaultSimple
213    * @extends baja.Simple
214    */
215   baja.DefaultSimple = function (val) {
216     baja.DefaultSimple.$super.apply(this, arguments);    
217     this.$val = strictArg(val || "", String);
218   }.$extend(baja.Simple);
219   
220   /**
221    * Default DefaultSimple instance.
222    */
223   baja.DefaultSimple.DEFAULT = new baja.DefaultSimple();
224     
225   /**
226    * Make a DefaultSimple.
227    *
228    * @param {String} str  the String to be used for this Simple
229    * @returns {baja.DefaultSimple} an instance of the Simple
230    */
231   baja.DefaultSimple.prototype.make = function (str) {
232     return this.decodeFromString(str);
233   };
234   
235   /**
236    * Decode a DefaultSimple from a String
237    *
238    * @param {String} str
239    * @returns {baja.DefaultSimple}
240    */
241   baja.DefaultSimple.prototype.decodeFromString = function (str) {
242     var s = new baja.DefaultSimple(str);
243     s.getType = this.getType;
244     return s;
245   };
246   
247   /**
248    * Encode a DefaultSimple to a String.
249    *
250    * @returns {String}
251    */
252   baja.DefaultSimple.prototype.encodeToString = function () {
253     return this.$val;
254   };
255   
256   // Register Type
257   baja.DefaultSimple.registerType("baja:Simple");
258   
259   /**
260    * Return the String encoding of a DefaultSimple.
261    *
262    * @returns {String}
263    */  
264   baja.DefaultSimple.prototype.valueOf = function () {
265     return this.$val;
266   };
267   
268   /**
269    * Return the String encoding of a DefaultSimple.
270    *
271    * @returns {String}
272    */  
273   baja.DefaultSimple.prototype.toString = function () {
274     return this.$val;
275   };
276     
277   ////////////////////////////////////////////////////////////////
278   // Augmented ECMAScript Baja Objects
279   //////////////////////////////////////////////////////////////// 
280   
281   // New copy that gets the value of the object for the clone
282   var valueOfNewCopy = function (exact) {
283     return this.valueOf();
284   };
285   
286   // Equals that uses valueOf for the comparison
287   var valueOfEquals = function (obj) { 
288     return bajaHasType(obj) && 
289            obj.getType().equals(this.getType()) && 
290            obj.valueOf() === this.valueOf();
291   };
292    
293   /**
294    * @class Represents baja:String in BajaScript.
295    * <p>
296    * All JavaScript Strings are augmented to be baja:String objects.
297    *
298    * @name String
299    */ 
300 
301   /**
302    * Default String instance.
303    */   
304   String.DEFAULT = "";
305   
306   /**
307    * Make a String.
308    * 
309    * @param {String} [str]
310    * @returns {String}
311    */
312   String.make = function (str) {
313     if (str === undefined) {
314       return String.DEFAULT;
315     }
316     if (typeof str !== "string") {
317       throw new Error("Must supply a String when making BString instances: " + str);
318     }
319     return str;
320   };
321   
322   /**
323    * Make a String.
324    *
325    * @param {String} [str]
326    * @returns {String}
327    */
328   String.prototype.make = function (str) {
329     return String.make(str);
330   };
331   
332   /**
333    * Decode a String.
334    *
335    * @param {String} str
336    * @returns {String}
337    */
338   String.prototype.decodeFromString =  function prototype(str) {
339     return str;
340   };
341   
342   /**
343    * Encode a String.
344    *
345    * @returns {String}
346    */
347   String.prototype.encodeToString = function () {
348     return this;
349   };
350     
351   // Register String with a Baja Type
352   String.registerType("baja:String");
353   
354   /**
355    * Returns a new String with the first letter Capitalized.
356    *
357    * @returns {String}
358    */
359   String.prototype.capitalizeFirstLetter = function () {
360     if (this.length > 0) {
361       return this.charAt(0).toUpperCase() + this.substring(1, this.length);
362     }
363     else {
364       return this;
365     }
366   };
367   
368   /**
369    * Replace patterned items in a string from an Object Map.
370    * <p>
371    * <pre>
372    *   // For example...
373    *   var str = "The weather is {state}!";
374    *   str = str.patternReplace({state: "really warm"});
375    * </pre>
376    *
377    * @returns {String}
378    */
379   String.prototype.patternReplace = function (obj) {
380     return this.replace(/\{[a-zA-Z0-9]*\}/g, function (match) {
381       match = match.substring(1, match.length - 1);
382       return typeof obj[match] === "string" ? obj[match] : match;
383     });
384   };
385   
386   /**
387    * Return the Symbol used for encoding this data type (primarily used for facets).
388    *
389    * @returns {String}
390    */
391   String.prototype.getDataTypeSymbol = function () {
392     return "s";
393   };
394   
395   /**
396    * Equality test.
397    * 
398    * @function
399    *
400    * @param obj
401    * @returns {Boolean}
402    */
403   String.prototype.equals = valueOfEquals;
404   
405   /**
406    * Equivalence test.
407    * <p>
408    * Equivalent is used to compare if two objects have equivalent
409    * state, but might not want to return true for equals since it
410    * it has implied semantics for many operations.  The default
411    * implementation returns the result of <code>equals()</code>.
412    *
413    * @function
414    * 
415    * @param obj
416    * @returns {Boolean}
417    */
418   String.prototype.equivalent = equalsEquivalent;
419   
420   /**
421    * New Copy.
422    *
423    * @function
424    *
425    * @returns {String}
426    */
427   String.prototype.newCopy = valueOfNewCopy;
428   
429   /**
430    * Return the Object's Icon.
431    *
432    * @function
433    * 
434    * @returns {baja.Icon}
435    */
436   String.prototype.getIcon = objectGetIcon;
437   
438   /**
439    * @class Represents a baja:Boolean in BajaScript.
440    * <p>
441    * Augments Boolean to be a baja:Boolean.
442    *
443    * @name Boolean
444    */
445    
446   /**
447    * Default Boolean instance.
448    */
449   Boolean.DEFAULT = false;
450 
451   /**
452    * Make a Boolean.
453    *
454    * @param {Boolean} b
455    * @returns {Boolean}
456    */
457   Boolean.make = function (b) {
458     if (b === undefined) {
459       return Boolean.DEFAULT;
460     }
461     if (typeof b !== "boolean") {
462       throw new Error("Must supply a Boolean when making BBoolean instances: " + b);
463     }
464     return b;
465   };
466   
467   /**
468    * Make a Boolean.
469    *
470    * @param {Boolean} b
471    * 
472    * @returns {Boolean}
473    */
474   Boolean.prototype.make = function (b) {
475     return Boolean.make(b);
476   };
477   
478   /**
479    * Decode a Boolean from a String.
480    *
481    * @param {String} str
482    * @returns {Boolean}
483    */
484   Boolean.prototype.decodeFromString = function (str) {
485     return str === "true";
486   };
487   
488   /**
489    * Encode a Boolean to a String.
490    *
491    * @returns {String}
492    */
493   Boolean.prototype.encodeToString = function () {
494     return this.toString();
495   };
496   
497   /**
498    * Return the data type symbol.
499    *
500    * @returns {String} the Symbol used for encoding this data type (primarily used for facets).
501    */
502   Boolean.prototype.getDataTypeSymbol = function () {
503     return "b";
504   };
505   
506   // Register Type
507   Boolean.registerType("baja:Boolean");
508   
509   /**
510    * Equality test.
511    *
512    * @function
513    * 
514    * @param obj
515    * @returns {Boolean} 
516    */
517   Boolean.prototype.equals = valueOfEquals;
518   
519   /**
520    * Equivalence test.
521    * <p>
522    * Equivalent is used to compare if two objects have equivalent
523    * state, but might not want to return true for equals since it
524    * it has implied semantics for many operations.  The default
525    * implementation returns the result of <code>equals()</code>.
526    *
527    * @function
528    * 
529    * @param obj
530    * @returns {Boolean}
531    */
532   Boolean.prototype.equivalent = equalsEquivalent;
533   
534   /**
535    * New Copy.
536    *
537    * @function
538    *
539    * @returns {Boolean}
540    */
541   Boolean.prototype.newCopy = valueOfNewCopy;
542    
543   // Boolean Enum Methods
544    
545   /**
546    * Return true if this enum's value is considered to be in an active state.
547    *
548    * @returns {Boolean} whether the enum is active.
549    */
550   Boolean.prototype.isActive = function () {
551     return this.valueOf();
552   };
553   
554   /**
555    * Return the enum ordinal.
556    *
557    * @returns {Number} the enum ordinal.
558    */
559   Boolean.prototype.getOrdinal = function () {
560     return this.valueOf() ? 1 : 0;
561   };
562   
563   /**
564    * Return the string identifier of this enum value.
565    *
566    * @returns {String} tag
567    */
568   Boolean.prototype.getTag = function () {
569     return this.valueOf() ? "true" : "false";
570   };
571 
572   var getBooleanDisplayTag = function (facets) {
573     var text;
574     
575     if (facets) {
576       if (this.valueOf()) {
577         text = facets instanceof baja.Facets ? facets.get("trueText") : facets.trueText;
578       }
579       else {
580         text = facets instanceof baja.Facets ? facets.get("falseText") : facets.falseText;
581       }
582     }
583   
584     if (text) {
585       // Handle Format text
586       return baja.Format.format(text);
587     }
588     else {
589       return baja.lex("baja").get(this.getTag());
590     }
591   };
592   
593   /**
594    * Return the display tag of this enum value.
595    *
596    * @name Boolean#getDisplayTag
597    * @function
598    *
599    * @param {baja.Facets|Object} [facets] used to specify the true and false text.
600    *                                      For true text the facets key is 'trueText' and
601    *                                      false is 'falseText'. The argument can also be an
602    *                                      Object Literal.
603    *
604    * @returns {String} display tag
605    */
606   Boolean.prototype.getDisplayTag = getBooleanDisplayTag;
607   
608   var oldBooleanToString = Boolean.prototype.toString;
609 
610   /**
611    * Return the String representation of a Boolean.
612    *
613    * @param {baja.Facets|Object} [facets] used to specify the true and false text.
614    *                                      For true text the facets key is 'trueText' and
615    *                                      false is 'falseText'. The argument can also be an
616    *                                      Object Literal.
617    *
618    * @returns String
619    */
620   Boolean.prototype.toString = function () {
621     if (arguments.length === 1) {
622       return getBooleanDisplayTag.apply(this, arguments);
623     }
624     else {
625       return oldBooleanToString.apply(this, arguments);
626     }
627   };
628     
629   /**
630    * Return the enum range.
631    *
632    * @returns {baja.EnumRange} the enum range.
633    */
634   Boolean.prototype.getRange = function () {
635     return baja.EnumRange.BOOLEAN_RANGE;
636   };
637     
638   // TODO: What about encoding/decoding numbers from different locales?
639   // How does this work in JavaScript?
640   
641   /**
642    * Return the Object's Icon.
643    *
644    * @function
645    * 
646    * @returns {baja.Icon}
647    */
648   Boolean.prototype.getIcon = objectGetIcon;
649   
650   /**
651    * Return the boolean from a BIBoolean.
652    *
653    * @return resolved boolean value.
654    */
655   Boolean.getBooleanFromIBoolean = function (boolVal) {
656     var val = boolVal;
657     
658     if (boolVal.getType().isComplex() && boolVal.has("out") && boolVal.getOut().getType().is("baja:StatusBoolean")) {
659       val = boolVal.getOut().getValue().valueOf();
660     }
661     else if (boolVal.getType().is("baja:StatusBoolean")) {
662       val = boolVal.getValue().valueOf();
663     }
664     else if (boolVal.getType().is("baja:Boolean")) {
665       val = boolVal.valueOf();
666     }
667     
668     return val;
669   };
670   
671   /**
672    * @class Represents a baja:Double in BajaScript.
673    * <p>
674    * Augments Number to be a Baja Double. ECMAScript only has one numeric type for 
675    * Doubles, Floats, Longs and Integers. Therefore, this type naturally maps to 
676    * baja:Double.
677    *
678    * @name Number
679    */
680    
681   /**
682    * Default Number instance.
683    */
684   Number.DEFAULT = 0;
685   
686   /**
687    * @name baja.Double
688    * @augments Number
689    */
690   baja.Double = Number;
691   
692   /**
693    * Make a Number.
694    *
695    * @param {Number} num  the number for the Simple.
696    * @returns {Number} the Number.
697    */
698   Number.make = function (num) {
699     return Number(num);
700   };
701   
702   /**
703    * Make a Number.
704    *
705    * @param {Number} num  the number.
706    * 
707    * @returns {Number} the Number.
708    */
709   Number.prototype.make = function (num) {
710     return Number.make(num);
711   };
712   
713   /**
714    * Decode a Number from a String.
715    *
716    * @param {String} str
717    * @returns {Number}
718    */
719   Number.prototype.decodeFromString = function (str) {
720     if (str === "0") {
721       return 0;
722     }
723     // Infinity and NaN
724     else if (str === "+inf") {
725       return Number.POSITIVE_INFINITY;
726     }
727     else if (str === "-inf") {
728       return Number.NEGATIVE_INFINITY;
729     }
730     else if (str === "nan") {
731       return Number["NaN"]; // Workaround for JsLint
732     }
733         
734     // Parse number
735     return Number(str);
736   };
737   
738   /**
739    * Encode to the Number to a String.
740    *
741    * @returns {String}
742    */
743   Number.prototype.encodeToString = function () {
744   
745     // Infinity and NaN
746     if (this.valueOf() === Number.POSITIVE_INFINITY) {
747       return "+inf";
748     }
749     else if (this.valueOf() === Number.NEGATIVE_INFINITY) {
750       return "-inf";
751     }
752     else if (isNaN(this.valueOf())) {
753       return "nan";
754     }
755     
756     return this.toString();
757   };
758   
759   /**
760    * Return the data type symbol for a Double.
761    *
762    * @returns {String} the Symbol used for encoding this data type (primarily used for facets).
763    */
764   Number.prototype.getDataTypeSymbol = function () {
765     return "d";
766   };
767   
768   // Register Type
769   Number.registerType("baja:Double"); 
770   
771   /**
772    * Equality test.
773    *
774    * @function
775    * 
776    * @param obj
777    * @returns {Boolean}
778    */
779   Number.prototype.equals = valueOfEquals;
780   
781   /**
782    * Equivalence test.
783    * <p>
784    * Equivalent is used to compare if two objects have equivalent
785    * state, but might not want to return true for equals since it
786    * it has implied semantics for many operations.  The default
787    * implementation returns the result of <code>equals()</code>.
788    *
789    * @function
790    * 
791    * @param obj
792    * @returns {Boolean}
793    */
794   Number.prototype.equivalent = equalsEquivalent;
795   
796   /**
797    * New Copy.
798    *
799    * @function
800    *
801    * @returns {Number}
802    */
803   Number.prototype.newCopy = valueOfNewCopy;
804   
805   /**
806    * Return the Object's Icon.
807    *
808    * @function
809    * 
810    * @returns {baja.Icon}
811    */
812   Number.prototype.getIcon = objectGetIcon;
813   
814   /**
815    * Return the number from a BINumeric.
816    *
817    * @returns resolved numeric value.
818    */
819   Number.getNumberFromINumeric = function (numeric) {
820     var val = numeric;
821 
822     if (numeric.getType().isComplex() && numeric.has("out") && numeric.getOut().getType().is("baja:StatusNumeric")) {
823       val = numeric.getOut().getValue().valueOf();
824     }
825     else if (numeric.getType().is("baja:StatusNumeric")) {
826       val = numeric.getValue().valueOf();
827     }
828     else if (numeric.getType().is("baja:Number")) {
829       val = numeric.valueOf();
830     }
831     
832     return val;
833   };
834   
835   var oldNumberToString = Number.prototype.toString;
836   
837   var numberToString = function (facets) {
838     if (facets) {
839       // Attempt to get the precision from the facets or an Object Literal argument
840       var precision = null;
841       if (facets instanceof baja.Facets) {
842         precision = facets.get("precision");
843       }
844       else if (typeof facets.precision === "number") {
845         precision = facets.precision;
846       }
847       if (precision !== null) {
848         return this.valueOf().toFixed(Number(precision.valueOf())); 
849       }
850     }
851 
852     // If no arguments are specified then use the old 
853     return oldNumberToString.apply(this.valueOf(), arguments);
854   };
855   
856   /**
857    * Return the String representation of a Number.
858    *
859    * @name Number#toString
860    * @function
861    *
862    * @param {baja.Facets|Object} [facets] used to specify the 'precision'.
863    *                                      The argument can also be an Object Literal.
864    *
865    * @returns String
866    */
867   Number.prototype.toString = numberToString;
868     
869   /**
870    * @class Represents a baja:Float in BajaScript.
871    * <p>
872    * Boxes JavaScript Number to represent a baja:Float.
873    * <p>
874    * When creating a Simple, always use the 'make' method instead of creating a new Object.
875    *
876    * @name baja.Float
877    * @extends baja.Simple
878    */
879   baja.Float = function (val) {
880     baja.Float.$super.apply(this, arguments); 
881     this.$val = strictArg(val, Number);
882   }.$extend(baja.Simple);
883       
884   /**
885    * Make a Float.
886    *
887    * @param {Number} num  the Float for the Simple.
888    * @returns {baja.Float} the Float.
889    */
890   baja.Float.make = function (val) {        
891     if (val === 0) {
892       return baja.Float.DEFAULT;
893     }
894     else if (isNaN(val)) {
895       return baja.Float.NAN;
896     }   
897     else if (val === baja.Float.POSITIVE_INFINITY.valueOf()) {
898       return baja.Float.POSITIVE_INFINITY;
899     }
900     else if (val === baja.Float.NEGATIVE_INFINITY.valueOf()) {
901       return baja.Float.NEGATIVE_INFINITY;
902     }
903     else if (val >= baja.Float.MAX_VALUE.valueOf()) {
904       return baja.Float.MAX_VALUE;
905     }
906     else if (val > 0 && val <= baja.Float.MIN_VALUE.valueOf()) {
907       return baja.Float.MIN_VALUE;
908     }
909       
910     return new baja.Float(val);
911   };
912   
913   /**
914    * Make a Float.
915    *
916    * @param {Number} num  the Float for the Simple.
917    * @returns {baja.Float} the Float.
918    */
919   baja.Float.prototype.make = function (val) {
920     return baja.Float.make(val);
921   };
922     
923   /**
924    * Decode a Float from a String.
925    *
926    * @param {String} str
927    * @returns {baja.Float}
928    */
929   baja.Float.prototype.decodeFromString = function (str) {
930     if (str === "0") {
931       return baja.Float.DEFAULT;
932     }
933     // Infinity and NaN
934     else if (str === "+inf") {
935       return baja.Float.POSITIVE_INFINITY;
936     }
937     else if (str === "-inf") {
938       return baja.Float.NEGATIVE_INFINITY;
939     }
940     else if (str === "nan") {
941       return baja.Float.NAN;
942     }     
943     return baja.Float.make(parseFloat(str));
944   };
945       
946   /**
947    * Encode the Float to a String.
948    *
949    * @returns {String}
950    */
951   baja.Float.prototype.encodeToString = function () {
952   
953     // Infinity and NaN
954     if (this.$val === Number.POSITIVE_INFINITY) {
955       return "+inf";
956     }
957     else if (this.$val === Number.NEGATIVE_INFINITY) {
958       return "-inf";
959     }
960     else if (isNaN(this.$val)) {
961       return "nan";
962     }
963     
964     // Check range limits
965     if (this.$val >= baja.Float.MAX_VALUE.valueOf()) {
966       return baja.Float.MAX_VALUE.valueOf().toString();
967     }
968     else if (this.$val > 0 && this.$val <= baja.Float.MIN_VALUE.valueOf()) {
969       return baja.Float.MIN_VALUE.valueOf().toString();
970     }
971     
972     return this.$val.toString();
973   };
974   
975   /**
976    * Default Float instance.
977    */
978   baja.Float.DEFAULT = uncacheConstantEncodeDecode(new baja.Float(0));
979   
980   /**
981    * Float Max Value.
982    */
983   baja.Float.MAX_VALUE = uncacheConstantEncodeDecode(new baja.Float(3.4028235E38));
984   
985   /**
986    * Float Min Value.
987    */
988   baja.Float.MIN_VALUE = uncacheConstantEncodeDecode(new baja.Float(1.4E-45));
989   
990   /**
991    * Float Positive Infinity.
992    */
993   baja.Float.POSITIVE_INFINITY = uncacheConstantEncodeDecode(new baja.Float(Number.POSITIVE_INFINITY));
994   
995   /**
996    * Float Negative Infinity.
997    */
998   baja.Float.NEGATIVE_INFINITY = uncacheConstantEncodeDecode(new baja.Float(Number.NEGATIVE_INFINITY));
999   
1000   /**
1001    * Float Not A Number.
1002    */
1003   baja.Float.NAN = uncacheConstantEncodeDecode(new baja.Float(Number["NaN"]));
1004   
1005   baja.Float.prototype.decodeFromString = cacheDecode(baja.Float.prototype.decodeFromString);
1006   baja.Float.prototype.encodeToString = cacheEncode(baja.Float.prototype.encodeToString);
1007   
1008   // Register Type  
1009   baja.Float.registerType("baja:Float");
1010   
1011   /**
1012    * Equality test.
1013    *
1014    * @function
1015    *
1016    * @param obj
1017    * @returns {Boolean}
1018    */
1019   baja.Float.prototype.equals = valueOfEquals;
1020   
1021   /**
1022    * Return the Number encapsulated in the Float.
1023    *
1024    * @returns {Number} Number for the Float.
1025    */
1026   baja.Float.prototype.valueOf = function () {
1027     return this.$val;
1028   };
1029   
1030   /**
1031    * Return the Symbol used for encoding this data type (primarily used for facets).
1032    *
1033    * @returns {String}
1034    */
1035   baja.Float.prototype.getDataTypeSymbol = function () {
1036     return "f";
1037   };
1038   
1039   /**
1040    * Return the String representation of a Float.
1041    *
1042    * @name baja.Float#toString
1043    * @function
1044    *
1045    * @param {baja.Facets|Object} [facets] used to specify the 'precision'.
1046    *                                      The argument can also be an Object Literal.
1047    *
1048    * @returns String
1049    */
1050   baja.Float.prototype.toString = numberToString;
1051   
1052   /**
1053    * @class Represents a baja:Integer in BajaScript.
1054    * <p>
1055    * Boxes JavaScript Number to represent a baja:Integer.
1056    * <p>
1057    * When creating a Simple, always use the 'make' method instead of creating a new Object.
1058    *
1059    * @name baja.Integer
1060    * @extends baja.Simple
1061    */
1062   baja.Integer = function (val) {
1063     baja.Integer.$super.apply(this, arguments); 
1064     this.$val = strictArg(val, Number);
1065   }.$extend(baja.Simple);
1066       
1067   /**
1068    * Make an Integer.
1069    *
1070    * @param {Number} num  the Integer for the Simple.
1071    * @returns {baja.Integer} the Integer.
1072    */
1073   baja.Integer.make = function (val) {
1074     if (val === 0 || isNaN(val)) {
1075       return baja.Integer.DEFAULT;
1076     }
1077     else if (val >= baja.Integer.MAX_VALUE.valueOf() || val === Number.MAX_VALUE) {
1078       return baja.Integer.MAX_VALUE;
1079     }
1080     else if (val <= baja.Integer.MIN_VALUE.valueOf() || val === Number.MIN_VALUE) {
1081       return baja.Integer.MIN_VALUE;
1082     }
1083       
1084     // Ensure we don't have a floating point
1085     val = Math.floor(val);
1086     
1087     if (val === 0) {
1088       return baja.Integer.DEFAULT;
1089     }
1090 
1091     return new baja.Integer(val);
1092   };
1093   
1094   /**
1095    * Make an Integer.
1096    *
1097    * @param {Number} num  the Integer for the Simple.
1098    * @returns {baja.Integer} the Integer.
1099    */
1100   baja.Integer.prototype.make = function (val) {
1101     return baja.Integer.make(val);
1102   };
1103     
1104   /**
1105    * Decode a String to an Integer.
1106    *
1107    * @param {String} str
1108    * @returns {baja.Integer}
1109    */
1110   baja.Integer.prototype.decodeFromString = function (str) {
1111     if (str === "0") {
1112       return baja.Integer.DEFAULT;
1113     }
1114     // Min and max limits
1115     else if (str === "max") {
1116       return baja.Integer.MAX_VALUE;
1117     }
1118     else if (str === "min") {
1119       return baja.Integer.MIN_VALUE;
1120     }
1121           
1122     return baja.Integer.make(Number(str));
1123   };
1124   
1125   /**
1126    * Encode an Integer to a String.
1127    *
1128    * @returns {String}
1129    */
1130   baja.Integer.prototype.encodeToString = function () {          
1131     // Check range limits and NAN
1132     if (isNaN(this.$val)) {
1133       return "0";
1134     }
1135     else if (this.$val >= baja.Integer.MAX_VALUE.valueOf()) {
1136       return "max";
1137     }
1138     else if (this.$val <= baja.Integer.MIN_VALUE.valueOf()) {
1139       return "min";
1140     }
1141     
1142     return Math.floor(this.$val).toFixed(0);
1143   };
1144   
1145   /**
1146    * Default Integer instance.
1147    */
1148   baja.Integer.DEFAULT = uncacheConstantEncodeDecode(new baja.Integer(0));
1149   
1150   /**
1151    * Integer Max Value.
1152    */
1153   baja.Integer.MAX_VALUE = uncacheConstantEncodeDecode(new baja.Integer(2147483647));
1154   
1155   /**
1156    * Integer Min Value.
1157    */
1158   baja.Integer.MIN_VALUE = uncacheConstantEncodeDecode(new baja.Integer(-2147483648));
1159   
1160   baja.Integer.prototype.encodeToString = cacheEncode(baja.Integer.prototype.encodeToString);
1161   baja.Integer.prototype.decodeFromString = cacheDecode(baja.Integer.prototype.decodeFromString);
1162   
1163   // Register Type  
1164   baja.Integer.registerType("baja:Integer");
1165   
1166   /**
1167    * Equality test.
1168    *
1169    * @function
1170    *
1171    * @param obj
1172    * @returns {Boolean}
1173    */
1174   baja.Integer.prototype.equals = valueOfEquals;
1175   
1176   /**
1177    * Return the Number encapsulated in the Integer.
1178    *
1179    * @returns {Number} Number for the Integer.
1180    */
1181   baja.Integer.prototype.valueOf = function () {
1182     return this.$val;
1183   };
1184   
1185   /**
1186    * Returns a string representation of the integer.
1187    * @returns {String}
1188    */
1189   baja.Integer.prototype.toString = function () {
1190     return String(this.valueOf());
1191   };
1192   
1193   /**
1194    * Return the Symbol used for encoding this data type (primarily used for facets).
1195    *
1196    * @returns {String} the Symbol used for encoding this data type (primarily used for facets).
1197    */
1198   baja.Integer.prototype.getDataTypeSymbol = function () {
1199     return "i";
1200   };
1201   
1202   // TODO: Long can't really be represented in JavaScript. This needs more investigation
1203   
1204   /**
1205    * @class Represents a baja:Long in BajaScript.
1206    * <p>
1207    * Boxes JavaScript Number to represent a baja:Long.
1208    * <p>
1209    * When creating a Simple, always use the 'make' method instead of creating a new Object.
1210    *
1211    * @name baja.Long
1212    * @extends baja.Simple
1213    */
1214   baja.Long = function (val) {
1215     baja.Long.$super.apply(this, arguments); 
1216     this.$val = strictArg(val, Number);
1217   }.$extend(baja.Simple);
1218       
1219   /**
1220    * Make a Long.
1221    *
1222    * @param {Number} num  the Long for the Simple.
1223    * @returns {baja.Long} the Long.
1224    */
1225   baja.Long.make = function (val) {  
1226     if (val === 0 || isNaN(val)) {
1227       return baja.Long.DEFAULT;
1228     }
1229     else if (val >= baja.Long.MAX_VALUE.valueOf() || val === Number.MAX_VALUE) {
1230       return baja.Long.MAX_VALUE;
1231     }
1232     else if (val <= baja.Long.MIN_VALUE.valueOf() || val === Number.MIN_VALUE) {
1233       return baja.Long.MIN_VALUE;
1234     }
1235   
1236     val = Math.floor(val);
1237     
1238     if (val === 0) {
1239       return baja.Long.DEFAULT;
1240     }
1241   
1242     return new baja.Long(val);
1243   };
1244   
1245   /**
1246    * Make a Long.
1247    *
1248    * @param {Number} num  the Long for the Simple.
1249    * @returns {baja.Long} the Long.
1250    */
1251   baja.Long.prototype.make = function (val) {
1252     return baja.Long.make(val);
1253   };
1254     
1255   /**
1256    * Decode a Long from a String.
1257    * 
1258    * @param {String} str
1259    * @returns {baja.Long}
1260    */
1261   baja.Long.prototype.decodeFromString = function (str) {
1262     if (str === "0") {
1263       return baja.Long.DEFAULT;
1264     }
1265     // Min and max limits
1266     if (str === "max") {
1267       return baja.Long.MAX_VALUE;
1268     }
1269     else if (str === "min") {
1270       return baja.Long.MIN_VALUE;
1271     }
1272           
1273     return baja.Long.make(Number(str));
1274   };
1275   
1276   /**
1277    * Encode the Long to a String.
1278    *
1279    * @returns {String}
1280    */
1281   baja.Long.prototype.encodeToString = function () {
1282         
1283     // Check range limits and NAN
1284     if (isNaN(this.$val)) {
1285       return "0";
1286     }
1287     else if (this.$val >= baja.Long.MAX_VALUE.valueOf()) {
1288       return "max";
1289     }
1290     else if (this.$val <= baja.Long.MIN_VALUE.valueOf()) {
1291       return "min";
1292     }
1293         
1294     return Math.floor(this.$val).toFixed(0);
1295   };
1296   
1297   /**
1298    * Default Simple instance.
1299    */
1300   baja.Long.DEFAULT = uncacheConstantEncodeDecode(new baja.Long(0));
1301   
1302   /**
1303    * Long Max Value.
1304    */
1305   baja.Long.MAX_VALUE = uncacheConstantEncodeDecode(new baja.Long(9223372036854775807));
1306   
1307   /**
1308    * Long Min Value.
1309    */
1310   baja.Long.MIN_VALUE = uncacheConstantEncodeDecode(new baja.Long(-9223372036854775808));
1311   
1312   baja.Long.prototype.decodeFromString = cacheDecode(baja.Long.prototype.decodeFromString);
1313   baja.Long.prototype.encodeToString = cacheEncode(baja.Long.prototype.encodeToString);
1314   
1315   // Register Type  
1316   baja.Long.registerType("baja:Long");
1317   
1318   /**
1319    * Equality test.
1320    *
1321    * @function
1322    *
1323    * @param obj
1324    * @returns {Boolean}
1325    */
1326   baja.Long.prototype.equals = valueOfEquals;
1327   
1328   /**
1329    * Return the Number encapsulated in the Long.
1330    *
1331    * @returns {Number} Number for the Long.
1332    */
1333   baja.Long.prototype.valueOf = function () {
1334     return this.$val;
1335   };
1336   
1337   /**
1338    * Return the Symbol used for encoding this data type (primarily used for facets).
1339    *
1340    * @returns {String} the Symbol used for encoding this data type (primarily used for facets).
1341    */
1342   baja.Long.prototype.getDataTypeSymbol = function () {
1343     return "l";
1344   };
1345   
1346   /**
1347    * Returns a string representation of the integer.
1348    *
1349    * @returns {String}
1350    */
1351   baja.Long.prototype.toString = function () {
1352     return String(this.valueOf());
1353   };
1354         
1355   ////////////////////////////////////////////////////////////////
1356   // Facets
1357   //////////////////////////////////////////////////////////////// 
1358   
1359   /**
1360    * @class Represents a BFacets in BajaScript.
1361    * <p>
1362    * BFacets is a map of name/value pairs used to annotate a
1363    * BComplex's Slot or to just provide additional metadata
1364    * about something.  The values of facets may only be
1365    * BIDataValues which are a predefined subset of simples.
1366    * <p>
1367    * When creating a Simple, always use the 'make' method instead of creating a new Object.
1368    *
1369    * @name baja.Facets
1370    * @extends baja.Simple
1371    */
1372   baja.Facets = function (keys, vals) {
1373     baja.Facets.$super.apply(this, arguments);    
1374     this.$map = new baja.OrderedMap();
1375     strictArg(keys, Array, "Keys array required"); 
1376     strictArg(vals, Array, "Values array required");    
1377     
1378     if (keys.length !== vals.length) {
1379       throw new Error("baja.Facets Constructor must have an equal number of keys and values");
1380     } 
1381     if (keys.length === 0) {
1382       return;
1383     }
1384     
1385     // Iterate through key, value pair arguments and add to the internal Map
1386     var dvt = baja.lt("baja:IDataValue");
1387     var key, i;   
1388     for (i = 0; i < keys.length; ++i) {
1389       if (typeof keys[i] !== 'string') {
1390         throw new Error("Facets keys must be a String");
1391       }
1392       
1393       baja.SlotPath.verifyValidName(keys[i]);
1394         
1395       // Check everything being added here is a DataValue
1396       if (!vals[i].getType) {
1397         throw new Error("Facet value has no Baja Type associated with it");
1398       }
1399       if (!vals[i].getType().is(dvt)) {
1400         throw new Error("Can only add baja:IDataValue Types to BFacets: " + vals[i].getType().getTypeSpec());
1401       }
1402       
1403       this.$map.put(keys[i], vals[i]);
1404     }
1405   }.$extend(baja.Simple);
1406   
1407   /**
1408    * Default Facets instance.
1409    */
1410   var facetsDefault = baja.Facets.DEFAULT = new baja.Facets([], []);
1411   
1412   /**
1413    * NULL Facets instance.
1414    */
1415   baja.Facets.NULL = facetsDefault;
1416   
1417   function facetsExtend(orig, toAdd) {
1418     var obj = {}, newKeys = [], newValues = [];
1419     
1420     baja.iterate(orig.getKeys(), function (key) {
1421       obj[key] = orig.get(key);
1422     });
1423     
1424     //overwrite any existing values with ones from toAdd
1425     baja.iterate(toAdd.getKeys(), function (key) {
1426       obj[key] = toAdd.get(key); 
1427     });
1428    
1429     baja.iterate(obj, function (value, key) {
1430       newKeys.push(key);
1431       newValues.push(value);
1432     });
1433     
1434     return baja.Facets.make(newKeys, newValues);
1435   }
1436   
1437   function facetsFromObj(obj) {
1438     var keys = [], 
1439         values = [];
1440     
1441     baja.iterate(obj, function (v, k) {
1442       keys.push(k);
1443       values.push(v);
1444     });
1445     
1446     return baja.Facets.make(keys, values);
1447   }
1448   
1449   /**
1450    * Make a Facets object. This function can either take two Array objects
1451    * for keys and values, two Facets or two Object Literals. In the latter two cases, a new
1452    * Facets object will be returned containing a combination of keys and values
1453    * from both input Facets objects. If a key exists on both Facets objects,
1454    * the value from the second Facets will take precedence.
1455    *
1456    * @param {Array|baja.Facets|Object} keys an array of keys for the facets. The keys 
1457    * must be Strings. (This may also be a Facets (or Object Literal) object whose values will be
1458    * combined with the second parameter. Values in this object will be
1459    * overwritten by corresponding values from the other).
1460    * @param {Array|baja.Facets|Object} values an array of values for the facets. The 
1461    * values must be BajaScript Objects whose Type implements BIDataValue.
1462    * (This may also be a Facets (or Object Literal) object whose values will be combined with the
1463    * first parameter. Values in this object will overwrite corresponding values
1464    * on the other.)
1465    * @returns {baja.Facets} the Facets
1466    */
1467   baja.Facets.make = function (keys, values) {
1468     // If there are no arguments are defined then return the default  
1469     if (arguments.length === 0) {
1470       return facetsDefault;
1471     }
1472     
1473     // Throw an error if no keys
1474     if (!keys) {
1475       throw new Error("Keys required");
1476     }
1477     
1478     // If the keys are an Object then convert to Facets
1479     if (keys.constructor === Object) {
1480       keys = facetsFromObj(keys);
1481     }
1482     
1483     // If the values are an Object then convert to Facets
1484     if (values && values.constructor === Object) {
1485       values = facetsFromObj(values);
1486     }
1487             
1488     if (keys instanceof baja.Facets) {
1489       // If keys and values are facets then merge
1490       if (values && values instanceof baja.Facets) {
1491         return facetsExtend(keys, values);
1492       }
1493       
1494       // If just the keys are facets then just return them
1495       return keys;
1496     }
1497     else {
1498       // If we've got here then we assume the keys and values are arrays...
1499       if (keys.length === 0) {
1500         return facetsDefault;
1501       }
1502       
1503       // Note: I could do more argument checking here but I don't want to slow this down
1504       // more than necessary.
1505       
1506       return new baja.Facets(keys, values);
1507     }
1508   };
1509   
1510   /**
1511    * Make a Facets.
1512    *
1513    * @param {Array} keys  an array of keys for the facets. The keys must be Strings.
1514    * @param {Array} values  an array of values for the facets. The values must be BajaScript Objects whose
1515    *                        Type implements BIDataValue.
1516    * @returns {baja.Facets} the Facets.
1517    */
1518   baja.Facets.prototype.make = function (args) {
1519     return baja.Facets.make.apply(baja.Facets, arguments);
1520   };
1521    
1522   /**
1523    * Decode a String to a Facets.
1524    *
1525    * @param {String} str
1526    * @returns {baja.Facets}
1527    */
1528   baja.Facets.prototype.decodeFromString = function (str) {    
1529     if (str.length === 0) {
1530       return facetsDefault;
1531     }
1532         
1533     var a = str.split("|"), // array
1534         a2, // inner array
1535         keys = [],
1536         vals = [],
1537         dv, // decoded values
1538         v; // decoded value
1539     
1540     // TODO: Since we haven't yet implemented all of the datatypes for Facets
1541     // we are hacking around the issue by creating the getDataTypeSymbol methods on a
1542     // DefaultSimple on the fly. This will have to do until the other Simples needed
1543     // for the other datatypes have been created
1544     function addDataType(v, dt) {
1545       v.getDataTypeSymbol = function () {
1546         return dt;
1547       };
1548       
1549       if (dt === "u") {
1550         /** 
1551          * @ignore - get JsDoc to ignore this symbols so it's not picked up accidently.
1552          */
1553         v.getType = function () {
1554           return baja.lt("baja:Unit");
1555         };
1556       }
1557       else if (dt === "z") {
1558         /** 
1559          * @ignore - get JsDoc to ignore this symbols so it's not picked up accidently.
1560          */
1561         v.getType = function () {
1562           return baja.lt("baja:TimeZone");
1563         };
1564       }
1565     }
1566     
1567     // Find the first separator and then split the String
1568     function splitFirst(str, sep) {
1569       var ind = str.indexOf(sep);
1570       var a = [];
1571       if (ind === -1) {
1572         a.push(str);
1573       }
1574       else {
1575         a.push(str.substring(0, ind));
1576         if (ind < (str.length - 1)) {
1577           a.push(str.substring(ind + 1, str.length));
1578         }
1579       }
1580       return a;
1581     }
1582     
1583     var i;
1584     for (i = 0; i < a.length; ++i) {
1585     
1586       a2 = splitFirst(a[i], "=");
1587       
1588       baja.SlotPath.verifyValidName(a2[0]);
1589       
1590       keys.push(a2[0]);
1591       
1592       // Unmarshal data value from String
1593       dv = splitFirst(a2[1], ":");
1594       
1595       if (dv.length === 1) {
1596         dv.push("");
1597       }
1598      
1599       if (dv[0] === "s") {
1600         v = baja.SlotPath.unescape(dv[1]);
1601       }
1602       else if (dv[0] === "b") {
1603         v = Boolean.DEFAULT.decodeFromString(dv[1]);
1604       }
1605       else if (dv[0] === "i") {
1606         v = baja.Integer.DEFAULT.decodeFromString(dv[1]);
1607       }
1608       else if (dv[0] === "l") {
1609         v = baja.Long.DEFAULT.decodeFromString(dv[1]);
1610       }
1611       else if (dv[0] === "f") {
1612         v = baja.Float.DEFAULT.decodeFromString(dv[1]);
1613       }
1614       else if (dv[0] === "d") {
1615         v = Number.DEFAULT.decodeFromString(dv[1]);
1616       }
1617       else if (dv[0] === "a") {
1618         v = baja.AbsTime.DEFAULT.decodeFromString(dv[1]);
1619       }
1620       else if (dv[0] === "E") {
1621         v = baja.EnumRange.DEFAULT.decodeFromString(dv[1]);
1622       }
1623       else if (dv[0] === "e") {
1624         v = baja.DynamicEnum.DEFAULT.decodeFromString(dv[1]);
1625       }
1626       else if (dv[0] === "r") {
1627         v = baja.RelTime.DEFAULT.decodeFromString(dv[1]);
1628       }
1629       else {
1630         // TODO: Need to get around to decoding BUnits and other essential data types.
1631         // Currently this is one big hack
1632         
1633         v = new baja.DefaultSimple(dv[1]);        
1634         addDataType(v, dv[0]);
1635       }
1636       
1637       vals.push(v);
1638     }
1639     
1640     return baja.Facets.make(keys, vals);
1641   };
1642   baja.Facets.prototype.decodeFromString = cacheDecode(baja.Facets.prototype.decodeFromString);
1643   
1644   /**
1645    * Encode Facets to a String.
1646    *
1647    * @returns {String}
1648    */
1649   baja.Facets.prototype.encodeToString = function () {
1650     var s = "", // TODO: This needs more work for data encoding
1651         k = this.$map.getKeys(),
1652         v, i;
1653     for (i = 0; i < k.length; ++i) {
1654       if (i > 0) {
1655         s += "|";
1656       }
1657       
1658       v = this.$map.get(k[i]);
1659             
1660       if (v.getDataTypeSymbol === undefined) {
1661         throw new Error("Cannot encode data type as 'getDataTypeSymbol' is not defined: " + v.getType());
1662       }
1663       
1664       // If a String then escape it
1665       if (v.getDataTypeSymbol() === "s") {
1666         v = baja.SlotPath.escape(v);
1667       }
1668       
1669       s += k[i] + "=" + v.getDataTypeSymbol() + ":" + v.encodeToString();
1670     } 
1671     return s;
1672   };
1673   baja.Facets.prototype.encodeToString = cacheEncode(baja.Facets.prototype.encodeToString);
1674     
1675   // Register Type
1676   baja.Facets.registerType("baja:Facets");  
1677     
1678   /**
1679    * Return a value from the map for the given key
1680    *
1681    * @param {String} key  the key used to look up the data value
1682    * @param [def] if defined, this value is returned if the key can't be found.
1683    * @returns the data value for the key (null if not found)
1684    */
1685   baja.Facets.prototype.get = function (key, def) {
1686     strictArg(key, String);    
1687     def = bajaDef(def, null);
1688     var v = this.$map.get(key);
1689     return v === null ? def : v;
1690   };
1691   
1692   /**
1693    * Return a copy of the Facets keys.
1694    *
1695    * @returns {Array} all of the keys (String) used in the Facets
1696    */
1697   baja.Facets.prototype.getKeys = function () {
1698     return this.$map.getKeys();
1699   };  
1700   
1701   /**
1702    * Return a String representation of the Facets.
1703    *
1704    * @returns {String}
1705    */
1706   baja.Facets.prototype.toString = function () {
1707     var str = "",
1708         keys = this.$map.getKeys(),
1709         i;
1710     
1711     for (i = 0; i < keys.length; ++i) {
1712       if (i > 0) {
1713         str += ",";
1714       }
1715       str += keys[i] + "=" + this.$map.get(keys[i]).toString(); 
1716     }
1717     return str;
1718   }; 
1719   
1720   /**
1721    * Return the value of the Facets.
1722    * @function
1723    *
1724    * @returns {String}
1725    */
1726   baja.Facets.prototype.valueOf = baja.Facets.prototype.toString; 
1727   
1728   /**
1729    * Return the facets for the given object.
1730    * <p>
1731    * If the facets can't be found then baja.Facets.DEFAULT is returned.
1732    * 
1733    * @param obj
1734    * @returns {baja.Facets}
1735    */
1736   baja.Facets.getFacetsFromObject = function (obj) {
1737     var val = facetsDefault;
1738     
1739     if (obj.getType().is("baja:Facets")) {
1740       val = obj;
1741     }
1742     else if (obj.getType().isComplex()) {
1743       if (obj.has("facets")) {
1744         val = obj.getFacets();
1745       }
1746       else if (obj.getParent()) {
1747         val = obj.getParent().getFacets(obj.getPropertyInParent());
1748       }
1749     }
1750     
1751     return val;
1752   };
1753 
1754   ////////////////////////////////////////////////////////////////
1755   // Enums
1756   ////////////////////////////////////////////////////////////////   
1757   
1758   /**
1759    * @class Represents a BEnumRange in BajaScript.
1760    * <p>
1761    * An EnumRange stores a range of ordinal/name pairs for Enumerations.
1762    * <p>
1763    * When creating a Simple, always use the 'make' method instead of creating a new Object.
1764    *
1765    * @name baja.EnumRange
1766    * @extends baja.Simple
1767    */
1768   baja.EnumRange = function (frozen, dynamic, byOrdinal, byTag, options) {
1769     baja.EnumRange.$super.apply(this, arguments);  
1770     this.$frozen = frozen;
1771     this.$dynamic = strictArg(dynamic, Array);
1772     this.$byOrdinal = strictArg(byOrdinal, Object);
1773     this.$byTag = strictArg(byTag, Object);
1774     this.$options = strictArg(options, baja.Facets);
1775   }.$extend(baja.Simple);
1776   
1777   /**
1778    * Make an EnumRange.
1779    * <p>
1780    * The TypeSpec for a FrozenEnum can be used as the first argument. If other arguments
1781    * are required then an Object Literal is used to to specify the method's arguments.
1782    * <pre>
1783    *   // For example...
1784    *   var er = baja.EnumRange.make({
1785    *     ordinals: [0, 1, 2],
1786    *     tags: ["A", "B", "C"]
1787    *   });
1788    * </pre>
1789    * <p>
1790    *
1791    * @param {Object} [obj] the Object Literal that holds the method's arguments.
1792    * @param {String|Type} [frozen] the Type or TypeSpec for the FrozenEnum.
1793    * @param {Array} [ordinals] an array of numbers that specify the dynamic enum ordinals.
1794    * @param {Array} [tags] an array of strings that specify the dynamic enum tags.
1795    * @param {baja.Facets} [facets] optional facets.
1796    * @returns {baja.EnumRange} the EnumRange.
1797    */
1798   baja.EnumRange.make = function (obj) {    
1799     obj = objectify(obj, "frozen");
1800     
1801     var frozen = bajaDef(obj.frozen, null),
1802         ordinals = obj.ordinals,
1803         tags = obj.tags,
1804         count = obj.count,
1805         options = obj.options;
1806       
1807     // Support String typespec as well as type
1808     if (typeof frozen === "string") {
1809       frozen = baja.lt(frozen);
1810     }
1811     
1812     if (ordinals === undefined) {
1813       ordinals = [];
1814     }
1815     if (tags === undefined) {
1816       tags = [];
1817     }
1818     if (count === undefined && ordinals instanceof Array) {
1819       count = ordinals.length;
1820     }
1821     if (options === undefined) {
1822       options = facetsDefault;
1823     }
1824    
1825     strictAllArgs([ordinals, tags, count, options], [Array, Array, Number, baja.Facets]);
1826     
1827     if (ordinals.length !== tags.length) {
1828       throw new Error("Ordinals and tags arrays must match in length");
1829     }
1830     
1831     // optimization
1832     if (count === 0 && options === facetsDefault) {
1833       if (frozen === null) {
1834         return baja.EnumRange.DEFAULT;
1835       }
1836       else {
1837         return new baja.EnumRange(frozen, [], {}, {}, options);
1838       }
1839     }
1840     
1841     var byOrdinal = {},
1842         byTag = {},
1843         dynaOrdinals = [],
1844         o, t, i;
1845     
1846     for (i = 0; i < count; ++i) {
1847       o = ordinals[i];
1848       t = tags[i];
1849       
1850       // Check for undefined due to JavaScript loading sequence
1851       if (baja.SlotPath !== undefined) {      
1852         baja.SlotPath.verifyValidName(t);
1853       }
1854       
1855       // check against frozen
1856       if (frozen !== null && frozen.isOrdinal(o)) {
1857         continue;
1858       }  
1859               
1860       // check duplicate ordinal
1861       if (byOrdinal.hasOwnProperty(o)) {
1862         throw new Error("Duplicate ordinal: " + t + "=" + o);
1863       }
1864 
1865       // check duplicate tag
1866       if (byTag.hasOwnProperty(t) || 
1867           (frozen && frozen.isTag(t))) {
1868         throw new Error("Duplicate tag: " + t + "=" + o);
1869       }
1870         
1871       // put into map
1872       byOrdinal[o] = t;
1873       byTag[t] = o;
1874       dynaOrdinals.push(o);
1875     }
1876       
1877     return new baja.EnumRange(frozen, dynaOrdinals, byOrdinal, byTag, options);
1878         
1879   };
1880   
1881   /**
1882    * Make an EnumRange.
1883    * <p>
1884    * The TypeSpec for a FrozenEnum can be used as the first argument. If other arguments
1885    * are required then an Object Literal is used to to specify the method's arguments.
1886    * <pre>
1887    *   // For example...
1888    *   var er = baja.$("baja:EnumRange").make({
1889    *     ordinals: [0, 1, 2],
1890    *     tags: ["A", "B", "C"]
1891    *   });
1892    * </pre>
1893    * <p>
1894    *
1895    * @param {Object} [obj] the Object Literal that holds the method's arguments.
1896    * @param {String|Type} [frozen] the Type or TypeSpec for the FrozenEnum.
1897    * @param {Array} [ordinals] an array of numbers that specify the dynamic enum ordinals.
1898    * @param {Array} [tags] an array of strings that specify the dynamic enum tags.
1899    * @param {baja.Facets} [facets] optional facets.
1900    * @returns {baja.EnumRange} the EnumRange .
1901    */
1902   baja.EnumRange.prototype.make = function (obj) {    
1903     return baja.EnumRange.make.apply(baja.EnumRange, arguments);
1904   };
1905   
1906   function splitFrozenDynamic(s) {       
1907     var frozen = null;
1908     var dynamic = null;
1909     var plus = s.indexOf('+');
1910     if (plus < 0) {
1911       if (s.indexOf("{") === 0) {
1912         dynamic = s;
1913       }
1914       else {
1915         frozen = s;
1916       }
1917     }
1918     else {
1919       if (s.indexOf("{") === 0) {
1920         dynamic = s.substring(0, plus);
1921         frozen = s.substring(plus + 1);   
1922       }
1923       else {
1924         frozen = s.substring(0, plus);
1925         dynamic = s.substring(plus + 1);   
1926       }
1927     }    
1928     return [frozen, dynamic];
1929   }
1930   
1931   function parseDynamic(s, ordinals, tags) {
1932     var count = 0,
1933         ordinal,
1934         st = s.split(/[=,]/),
1935         i;
1936     
1937     for (i = 0; i < st.length; ++i) {
1938       tags[count] = st[i];
1939       ordinal = parseInt(st[++i], 10);
1940       if (isNaN(ordinal)) {
1941         throw new Error("Invalid ordinal: " + st[i]);
1942       }
1943       ordinals[count] = ordinal;
1944       count++;
1945     }   
1946     return count;
1947   }
1948   
1949   /**
1950    * Decode an EnumRange from a String.
1951    *
1952    * @param {String} str
1953    * @returns {baja.EnumRange}
1954    */
1955   baja.EnumRange.prototype.decodeFromString = function (str) {    
1956     if (str === "{}") {
1957       return baja.EnumRange.DEFAULT;
1958     }
1959     
1960     // split body from options (there can't be a question mark 
1961     // anywhere until after the frozen type or dynamic {}
1962     var options = facetsDefault,
1963         question = str.indexOf('?');
1964     if (question > 0) {
1965       options = facetsDefault.decodeFromString(str.substring(question + 1));
1966       str = str.substring(0, question);
1967     }
1968     
1969      // split dynamic and frozen tokens
1970     var split = splitFrozenDynamic(str),
1971         frozenStr = split[0],
1972         dynamicStr = split[1],
1973         frozen = null;        
1974     
1975     // get frozen
1976     if (frozenStr !== null) {
1977       frozen = baja.lt(frozenStr);
1978       if (frozen === null) {
1979         throw new Error("Invalid frozen EnumRange spec: " + frozenStr);
1980       }
1981     }
1982     
1983     if (dynamicStr === null) {
1984       return this.make({
1985         "frozen": frozen, 
1986         "options": options
1987       });
1988     }
1989 
1990     // check for required braces on dynamic
1991     if (dynamicStr.charAt(0) !== "{") {
1992       throw new Error("Missing {");
1993     }
1994     if (dynamicStr.charAt(dynamicStr.length - 1) !== "}") {
1995       throw new Error("Missing }");
1996     }      
1997     dynamicStr = dynamicStr.substring(1, dynamicStr.length - 1);
1998                                                   
1999     // get dynamic                
2000     var ordinals = [],
2001         tags = [],
2002         count = parseDynamic(dynamicStr, ordinals, tags);
2003     
2004     return this.make({
2005       "frozen": frozen, 
2006       "ordinals": ordinals, 
2007       "tags": tags, 
2008       "count": count, 
2009       "options": options
2010     });
2011   };
2012   
2013   /**
2014    * Encode an EnumRange to a String.
2015    *
2016    * @returns {String}
2017    */
2018   baja.EnumRange.prototype.encodeToString = function () {        
2019     // Optimization for encoding
2020     if (this === baja.EnumRange.DEFAULT) {
2021       return "{}";
2022     }
2023   
2024     var s = "",
2025         key,
2026         tag, i;
2027     
2028     if (this.$frozen !== null) {
2029       s += this.$frozen.getTypeSpec();
2030     }
2031     
2032     if (this.$dynamic.length > 0 || this.$frozen === null) {
2033       if (s.length > 0) {
2034         s += "+";
2035       }
2036       
2037       s += "{";
2038       for (i = 0; i < this.$dynamic.length; ++i) {
2039         key = this.$dynamic[i];
2040         tag = this.$byOrdinal[key];
2041         if (i > 0) {
2042           s += ",";
2043         }
2044         s += tag + "=" + key;
2045       }
2046       s += "}";
2047     }     
2048 
2049     if (this.$options !== facetsDefault) {
2050       s += "?" + this.$options.encodeToString();
2051     }
2052     
2053     return s;
2054   };
2055   
2056   /**
2057    * Default EnumRange instance.
2058    */   
2059   baja.EnumRange.DEFAULT = uncacheConstantEncodeDecode(new baja.EnumRange(null, [], {}, {}, facetsDefault));
2060   
2061   baja.EnumRange.prototype.decodeFromString = cacheDecode(baja.EnumRange.prototype.decodeFromString);
2062   baja.EnumRange.prototype.encodeToString = cacheEncode(baja.EnumRange.prototype.encodeToString);
2063      
2064   // Register Type   
2065   baja.EnumRange.registerType("baja:EnumRange");
2066   
2067   /**
2068    * Return the data type symbol.
2069    *
2070    * @returns {String} data type symbol.
2071    */
2072   baja.EnumRange.prototype.getDataTypeSymbol = function () {
2073     return "E";
2074   };
2075   
2076   /**
2077    * Return all of the ordinals for the EnumRange.
2078    * <p>
2079    * The returned array contains both frozen and enum ordinals.
2080    *
2081    * @returns {Array} an array of numbers that represents the ordinals for this EnumRange.
2082    */
2083   baja.EnumRange.prototype.getOrdinals = function () {
2084     var ordinals, i;
2085     if (this.$frozen !== null) {
2086       ordinals = this.$frozen.getOrdinals();
2087     }
2088     else {
2089       ordinals = [];
2090     }
2091     for (i = 0; i < this.$dynamic.length; ++i) {
2092       ordinals.push(this.$dynamic[i]);
2093     }
2094     return ordinals;
2095   };
2096   
2097   /**
2098    * Return true if the ordinal is valid in this EnumRange.
2099    *
2100    * @param {Number} ordinal
2101    * @returns {Boolean} true if valid
2102    */
2103   baja.EnumRange.prototype.isOrdinal = function (ordinal) {
2104     strictArg(ordinal, Number);
2105     if (this.$frozen !== null && this.$frozen.isOrdinal(ordinal)) {
2106       return true;
2107     }
2108     return this.$byOrdinal.hasOwnProperty(ordinal);
2109   };  
2110   
2111   /**
2112    * Return the tag for the specified ordinal.
2113    * <p>
2114    * If the ordinal isn't valid then the ordinal is returned 
2115    * as a String.
2116    *
2117    * @param {Number} ordinal
2118    * @returns {String} tag
2119    */
2120   baja.EnumRange.prototype.getTag = function (ordinal) {
2121     strictArg(ordinal, Number);
2122     if (this.$byOrdinal.hasOwnProperty(ordinal)) {
2123       return this.$byOrdinal[ordinal];
2124     }
2125       
2126     if (this.$frozen !== null && this.$frozen.isOrdinal(ordinal)) {
2127       return this.$frozen.getTag(ordinal);
2128     }
2129     
2130     return String(ordinal);
2131   };  
2132   
2133   /**
2134    * Return true if the tag is used within the EnumRange.
2135    *
2136    * @param {String} tag
2137    * @returns {Boolean} true if valid.
2138    */
2139   baja.EnumRange.prototype.isTag = function (tag) {     
2140     strictArg(tag, String);  
2141     if (this.$frozen !== null && this.$frozen.isTag(tag)) {
2142       return true;
2143     }
2144     
2145     return this.$byTag.hasOwnProperty(tag);
2146   }; 
2147   
2148   /**
2149    * Convert the tag to its ordinal within the EnumRange.
2150    *
2151    * @param {String} tag
2152    * @returns {Number} ordinal for the tag.
2153    * @throws error if the tag is invalid.
2154    */
2155   baja.EnumRange.prototype.tagToOrdinal = function (tag) {
2156     strictArg(tag, String);  
2157     if (this.$frozen !== null && this.$frozen.isTag(tag)) {
2158       return this.$frozen.tagToOrdinal(tag);
2159     }
2160         
2161     if (this.$byTag.hasOwnProperty(tag)) {
2162       return this.$byTag[tag];
2163     }
2164     else {
2165       throw new Error("Invalid tag: " + tag);
2166     }
2167   }; 
2168   
2169   /**
2170    * Get the enum for the specified tag or ordinal.
2171    * <p>
2172    * This method is used to access an enum based upon a tag or ordinal.
2173    *
2174    * @param {String|Number} arg a tag or ordinal.
2175    * @returns {baja.DynamicEnum|baja.FrozenEnum|Boolean} the enum for the tag or ordinal.
2176    * @throws error if the tag or ordinal is invalid.
2177    */
2178   baja.EnumRange.prototype.get = function (arg) {      
2179       
2180     if (typeof arg === "string") {
2181       if (this === baja.EnumRange.BOOLEAN_RANGE) {
2182         return arg === "false" ? false : true;
2183       }
2184     
2185       // Look up via tag name
2186       if (this.$frozen !== null && this.$frozen.isTag(arg)) {
2187         return this.$frozen.getFrozenEnum(arg);
2188       }
2189     
2190       if (this.$byTag.hasOwnProperty(arg)) {
2191         return baja.DynamicEnum.make({"ordinal": this.$byTag[arg], "range": this});
2192       }
2193     }
2194     else if (typeof arg === "number") {
2195       if (this === baja.EnumRange.BOOLEAN_RANGE) {
2196         return arg === 0 ? false : true;
2197       }
2198     
2199       // Look up via ordinal
2200       if (this.$frozen !== null && this.$frozen.isOrdinal(arg)) {
2201         return this.$frozen.getFrozenEnum(arg);
2202       }
2203     
2204       if (this.isOrdinal(arg)) {
2205         return baja.DynamicEnum.make({"ordinal": arg, "range": this}); 
2206       }
2207     }
2208     
2209     throw new Error("Unable to access enum");
2210   }; 
2211   
2212   /**
2213    * Return true if the ordinal is a valid ordinal in the frozen range.
2214    *
2215    * @param {Number} ordinal
2216    * @returns {Boolean} true if valid.
2217    */
2218   baja.EnumRange.prototype.isFrozenOrdinal = function (ordinal) {
2219     strictArg(ordinal, Number);
2220     return this.$frozen !== null && this.$frozen.isOrdinal(ordinal);
2221   };
2222   
2223   /**
2224    * Return true if the ordinal is a valid ordinal in the dynamic range.
2225    *
2226    * @param {Number} ordinal
2227    * @returns {Boolean} true if valid
2228    */
2229   baja.EnumRange.prototype.isDynamicOrdinal = function (ordinal) {
2230     strictArg(ordinal, Number);
2231     return this.$byOrdinal.hasOwnProperty(ordinal);
2232   };
2233   
2234   /**
2235    * Return the Type used for the frozen enum range or null if this range
2236    * has no frozen ordinal/tag pairs.
2237    *
2238    * @returns {Type} the Type for the FrozenEnum or null.
2239    */
2240   baja.EnumRange.prototype.getFrozenType = function () {
2241     return this.$frozen;
2242   };
2243   
2244   /**
2245    * Get the options for this range stored as a Facets instance.
2246    *
2247    * @returns {baja.Facets} facets
2248    */
2249   baja.EnumRange.prototype.getOptions = function () {
2250     return this.$options;
2251   };
2252   
2253   /**
2254    * Boolean EnumRange.
2255    */
2256   baja.EnumRange.BOOLEAN_RANGE = baja.EnumRange.make({
2257     "ordinals": [0, 1], 
2258     "tags": ["false", "true"]
2259   });      
2260     
2261   ////////////////////////////////////////////////////////////////
2262   // EnumSet
2263   ////////////////////////////////////////////////////////////////   
2264   
2265   /**
2266    * @class Represents a baja:EnumSet in BajaScript.
2267    * <p>
2268    * An EnumSet contains an EnumRange and an array of ordinals.
2269    * <p>
2270    * When creating a Simple, always use the 'make' method instead of creating a new Object.
2271    *
2272    * @name baja.EnumSet
2273    * @extends baja.Simple
2274    */
2275   baja.EnumSet = function (ordinals, range) {
2276     baja.EnumSet.$super.apply(this, arguments);  
2277     this.$ordinals = strictArg(ordinals);
2278     this.$range = strictArg(range, Object);
2279   }.$extend(baja.Simple);
2280   
2281   /**
2282    * Make an EnumSet. An EnumSet can be created using either an array of
2283    * ordinals (in which case the range will be set to 
2284    * <code>baja.EnumRange.DEFAULT</code>), or, to specify a range as well, an 
2285    * object literal with <code>ordinals</code> and <code>range</code> 
2286    * properties.
2287    * <pre>
2288    *   // For example...
2289    *   var defaultRange = baja.EnumSet.make([0, 1, 2]);
2290    *   var customRange = baja.EnumSet.make({
2291    *     ordinals: [0, 2, 4],
2292    *     range: baja.EnumRange.make({
2293    *       ordinals: [0, 1, 2, 3, 4],
2294    *       tags: ['a', 'b', 'c', 'd', 'e']
2295    *     })
2296    *   });
2297    * </pre>
2298    * <p>
2299    *
2300    * @param {Object|Array} obj the Object Literal that holds the method's arguments
2301    * (or an array of ordinals).
2302    * @param {Array} [obj.ordinals] an array of ordinals.
2303    * @param {baja.EnumRange} [obj.range] the EnumRange to assign the EnumSet.
2304    * @returns {baja.EnumSet} the EnumSet.
2305    */
2306   baja.EnumSet.make = function (obj) {    
2307     obj = objectify(obj, "ordinals");
2308     
2309     var ordinals = obj.ordinals,
2310         range = obj.range,
2311         count;
2312     
2313     if (ordinals === undefined) {
2314       ordinals = [];
2315     }
2316     
2317     if (range === undefined) {
2318       range = baja.EnumRange.DEFAULT;
2319     }
2320    
2321     strictArg(ordinals, Array);
2322     strictArg(range, baja.EnumRange);
2323     
2324     // optimization
2325     if (ordinals.length === 0 && range === baja.EnumRange.DEFAULT) {
2326       return baja.EnumSet.NULL;
2327     }
2328     
2329     return new baja.EnumSet(ordinals, range);
2330   };
2331   
2332   /**
2333    * Make an EnumSet. Same as static method <code>baja.EnumSet.make</code>.
2334    * 
2335    * @see baja.EnumSet.make
2336    */
2337   baja.EnumSet.prototype.make = function (obj) {    
2338     return baja.EnumSet.make.apply(baja.EnumSet, arguments);
2339   };
2340 
2341   /**
2342    * Decode an EnumSet from a String.
2343    *
2344    * @param {String} str
2345    * @returns {baja.EnumSet}
2346    */
2347   baja.EnumSet.prototype.decodeFromString = cacheDecode(function decodeFromString(str) {
2348     // parse range if specified
2349     var ordinals = [],
2350         ordinal,
2351         range,
2352         at = str.indexOf('@'),
2353         split,
2354         count,
2355         i;
2356     
2357     if (at >= 0) {
2358       range = baja.EnumRange.DEFAULT.decodeFromString(str.substring(at + 1));
2359       str = str.substring(0, at);
2360     }                       
2361 
2362     if (str.length) {
2363       split = str.split(',');
2364       count = split.length;
2365       for (i = 0; i < count; i++) {
2366         ordinal = parseInt(split[i], 10);
2367         if (isNaN(ordinal)) {
2368           throw new Error("Invalid ordinal: " + split[i]);
2369         }
2370         ordinals.push(ordinal);
2371       }
2372     }
2373     
2374     return this.make({
2375       ordinals: ordinals,
2376       range: range
2377     });
2378   });
2379   
2380   /**
2381    * Encode an EnumSet to a String.
2382    *
2383    * @returns {String}
2384    */
2385   baja.EnumSet.prototype.encodeToString = cacheEncode(function encodeToString() {  
2386     var ordinals = this.$ordinals,
2387         range = this.$range,
2388         s = ordinals.join(','),
2389         i;
2390     
2391     if (range && (range !== baja.EnumRange.DEFAULT)) {
2392       s += '@' + range.encodeToString();
2393     }
2394     return s;
2395   });
2396   
2397   /**
2398    * Return the data type symbol (E).
2399    *
2400    * @returns {String} data type symbol
2401    */
2402   baja.EnumSet.prototype.getDataTypeSymbol = function () {
2403     return "E";
2404   };
2405   
2406   /**
2407    * Return all of the ordinals for the EnumSet
2408    *
2409    * @returns {Array} an array of numbers that represents the ordinals for this EnumSet.
2410    */
2411   baja.EnumSet.prototype.getOrdinals = function () {
2412     return this.$ordinals;
2413   };
2414   
2415   baja.EnumSet.prototype.getRange = function () {
2416     return this.$range;
2417   };
2418   
2419   /**
2420    * Default EnumSet instance.
2421    */   
2422   baja.EnumSet.DEFAULT = new baja.EnumSet([], baja.EnumRange.DEFAULT);
2423   baja.EnumSet.NULL = baja.EnumSet.DEFAULT;
2424    
2425   // Register Type   
2426   baja.EnumSet.registerType("baja:EnumSet");
2427 
2428   /**
2429    * @class Represents a baja:Enum in BajaScript.
2430    *
2431    * @name baja.Enum
2432    * @extends baja.Simple
2433    */
2434   baja.Enum = function () { 
2435     baja.Enum.$super.apply(this, arguments);  
2436   }.$extend(baja.Simple);
2437   
2438   /**
2439    * Return the enum from a BIEnum.
2440    *
2441    * @return resolved enum value
2442    */
2443   baja.Enum.getEnumFromIEnum = function (enumVal) {
2444     var val = enumVal;
2445     
2446     if (enumVal.getType().isComplex() && enumVal.has("out") && enumVal.getOut().getType().is("baja:StatusValue")) {
2447       val = enumVal.getOut().getValue();
2448     }
2449     else if (enumVal.getType().is("baja:StatusValue")) {
2450       val = enumVal.getValue();
2451     }
2452     
2453     return val;
2454   };
2455         
2456   /**
2457    * @class Represents a baja:DynamicEnum in BajaScript.
2458    * <p>
2459    * BDynamicEnum stores an ordinal state variable as 
2460    * a Number.  An instance of BEnumRange may be used to specify the range.
2461    * <p>
2462    * When creating a Simple, always use the 'make' method instead of creating a new Object.
2463    *
2464    * @name baja.DynamicEnum
2465    * @extends baja.Enum
2466    */
2467   baja.DynamicEnum = function (ordinal, range) {
2468     baja.DynamicEnum.$super.apply(this, arguments);
2469     this.$ordinal = strictArg(ordinal, Number);
2470     this.$range = strictArg(range || baja.EnumRange.DEFAULT, baja.EnumRange);    
2471   }.$extend(baja.Enum);
2472   
2473   /**
2474    * Make a DynamicEnum
2475    * <p>
2476    * An ordinal or an Object Literal can be used for the method's arguments...
2477    * <pre>
2478    *   var de1 = baja.DynamicEnum.make(0); // Just with an ordinal
2479    *
2480    *   //... or with an Object Literal...
2481    *
2482    *   var de2 = baja.DynamicEnum.make({ordinal: 0, range: enumRange});
2483    *   
2484    *   //...or create from another enumeration...
2485    *
2486    *   var de3 = baja.DynamicEnum.make({en: anEnum});
2487    * </pre>
2488    *
2489    * @param {Object|Number} [obj] the Object Literal for the method's arguments or an ordinal.
2490    * @param {Number} [obj.ordinal] the ordinal for the enum.
2491    * @param {baja.EnumRange} [obj.range] the range for the enum.
2492    * @param {baja.DynamicEnum|baja.FrozenEnum|Boolean|String} [obj.en] if defined, this enum will be used for the ordinal and range. 
2493    *                                                                   As well as an enum, this can also be a TypeSpec 
2494    *                                                                   String (moduleName:typeName) for a FrozenEnum.
2495    * @returns {baja.DynamicEnum} the DynamicEnum. 
2496    */
2497   baja.DynamicEnum.make = function (obj) {
2498     obj = objectify(obj, "ordinal");  
2499     
2500     var ordinal = bajaDef(obj.ordinal, 0);
2501     var range = bajaDef(obj.range, baja.EnumRange.DEFAULT);
2502     
2503     // Create from another enumeration...
2504     var en;
2505     if (obj.en !== undefined) {
2506       en = obj.en;
2507     
2508       if (!bajaHasType(en)) {
2509         throw new Error("Invalid Enum Argument");
2510       }
2511       
2512       if (en.getType().is("baja:DynamicEnum")) {
2513         return en;
2514       }
2515       
2516       // If a type spec is passed in then resolve it to the enum instance
2517       if (typeof en === "string") {
2518         en = baja.$(en);
2519       }
2520       
2521       if (en.getType().isFrozenEnum() || en.getType().is("baja:Boolean")) {
2522         ordinal = en.getOrdinal();
2523         range = en.getRange();
2524       }
2525       else {
2526         throw new Error("Argument must be an Enum");
2527       }
2528     }
2529     
2530     // Check for default
2531     if (ordinal === 0 && range === baja.EnumRange.DEFAULT) {
2532       return baja.DynamicEnum.DEFAULT;
2533     }
2534     
2535     return new baja.DynamicEnum(ordinal, range);
2536   };
2537   
2538   /**
2539    * Make a DynamicEnum.
2540    * <p>
2541    * An ordinal or an Object Literal can be used for the method's arguments...
2542    * <pre>
2543    *   var de1 = baja.$("baja:DynamicEnum").make(0); // Just with an ordinal
2544    *
2545    *   //... or with an Object Literal...
2546    *
2547    *   var de2 = baja.$("baja:DynamicEnum").make({ordinal: 0, range: enumRange});
2548    *   
2549    *   //...or create from another enumeration...
2550    *
2551    *   var de3 = baja.$("baja:DynamicEnum").make({en: anEnum});
2552    * </pre>
2553    *
2554    * @param {Object|Number} [obj] the Object Literal for the method's arguments or an ordinal.
2555    * @param {Number} [obj.ordinal] the ordinal for the enum.
2556    * @param {baja.EnumRange} [obj.range] the range for the enum.
2557    * @param {baja.DynamicEnum|baja.FrozenEnum|Boolean|String} [obj.en] if defined, this enum will be used for the ordinal and range. 
2558    *                                                                   As well as an enum, this can also be a TypeSpec 
2559    *                                                                   String (moduleName:typeName) for a FrozenEnum.
2560    * @returns {baja.DynamicEnum} the DynamicEnum.
2561    */
2562   baja.DynamicEnum.prototype.make = function (obj) {
2563     return baja.DynamicEnum.make.apply(baja.DynamicEnum, arguments);
2564   };
2565     
2566   /**
2567    * Decode a DynamicEnum from a String.
2568    *                    
2569    * @param {String} str  
2570    * @returns {baja.DynamicEnum}
2571    */  
2572   baja.DynamicEnum.prototype.decodeFromString = function (str) {   
2573     var o = str;
2574     var r = null;
2575     
2576     var at = str.indexOf("@");
2577     if (at > 0) { 
2578       o = str.substring(0, at); 
2579       r = str.substring(at + 1);
2580     }
2581         
2582     var ordinal = parseInt(o, 10);
2583     var range = baja.EnumRange.DEFAULT;
2584     if (r !== null) {
2585       range = range.decodeFromString(r);
2586     }
2587     
2588     return this.make({"ordinal": ordinal, "range": range});
2589   };
2590   baja.DynamicEnum.prototype.decodeFromString = cacheDecode(baja.DynamicEnum.prototype.decodeFromString);
2591   
2592   /**
2593    * Encode a DynamicEnum to a String.
2594    *
2595    * @name baja.DynamicEnum#encodeToString
2596    * @function
2597    *                     
2598    * @returns {String}
2599    */  
2600   baja.DynamicEnum.prototype.encodeToString = function () {       
2601     var s = "";
2602     s += this.$ordinal;
2603     if (this.$range !== baja.EnumRange.DEFAULT) {
2604       s += "@" + this.$range.encodeToString();
2605     }
2606     
2607     return s;
2608   };
2609   baja.DynamicEnum.prototype.encodeToString = cacheEncode(baja.DynamicEnum.prototype.encodeToString);
2610   
2611   /**
2612    * Default DynamicEnum instance.
2613    */   
2614   baja.DynamicEnum.DEFAULT = new baja.DynamicEnum(0, baja.EnumRange.DEFAULT);
2615    
2616   // Register Type   
2617   baja.DynamicEnum.registerType("baja:DynamicEnum");        
2618   
2619   /**
2620    * Return the data type symbol.
2621    *
2622    * @returns {String} the data type symbol.
2623    */
2624   baja.DynamicEnum.prototype.getDataTypeSymbol = function () {
2625     return "e";
2626   };
2627   
2628   /**
2629    * Return whether the enum is action or not.
2630    *
2631    * @returns {Boolean} true if active.
2632    */
2633   baja.DynamicEnum.prototype.isActive = function () {
2634     return this.$ordinal !== 0;
2635   };
2636   
2637   /**
2638    * Return the ordinal.
2639    *
2640    * @returns {Number} the ordinal.
2641    */
2642   baja.DynamicEnum.prototype.getOrdinal = function () {
2643     return this.$ordinal;
2644   };
2645   
2646   /**
2647    * Return the range.
2648    *
2649    * @returns {baja.EnumRange} the enum range.
2650    */
2651   baja.DynamicEnum.prototype.getRange = function () {
2652     return this.$range;
2653   };
2654   
2655   /**
2656    * Return the tag for the ordinal.
2657    *
2658    * @returns {String} the tag.
2659    */
2660   baja.DynamicEnum.prototype.getTag = function () {
2661     return this.$range.getTag(this.$ordinal);
2662   };
2663   
2664   /**
2665    * Return the String representation of the DynamicEnum.
2666    *
2667    * @returns {String}
2668    */
2669   baja.DynamicEnum.prototype.toString = function () {   
2670     return this.getTag();  
2671   };
2672   
2673   /**
2674    * Equals comparison via tag or ordinal.
2675    *
2676    * @param {String|Number|baja.DynamicEnum} arg the enum, tag or ordinal used for comparison.
2677    * @returns {Boolean} true if equal.
2678    */
2679   baja.DynamicEnum.prototype.is = function (arg) {  
2680     var tof = typeof arg;
2681     if (tof === "number") {
2682       return arg === this.$ordinal;
2683     }    
2684     else if (tof === "string") {
2685       return arg === this.getTag();
2686     }
2687     else {
2688       return this.equals(arg);
2689     }
2690   };
2691   
2692   /**
2693    * @class Represents a baja:FrozenEnum in BajaScript.
2694    * <p>
2695    * In Niagara, a BFrozenEnum is a hard coded set of enumerations and is immutable.
2696    * <p>
2697    * FrozenEnum objects are treated in a special way in BajaScript. A FrozenEnum's range (also
2698    * known as a Contract in BajaScript) is lazily requested and stored within the BajaScript 
2699    * registry. Therefore, this object shouldn't be used directly. 
2700    * Here's the preferred way of access an FrozenEnum...
2701    * <pre>
2702    *   var en = baja.$("baja:Weekday").get("monday");
2703    * </pre>
2704    *
2705    * @name baja.FrozenEnum
2706    * @extends baja.Enum
2707    *
2708    * @see baja.FrozenEnum#get
2709    */
2710    var FrozenEnum = function () {
2711     // This Constructor should be considered private
2712     FrozenEnum.$super.apply(this, arguments);  
2713     this.$ordinal = 0;
2714   }.$extend(baja.Enum);
2715   
2716   /**
2717    * Called once a Contract has been loaded onto a FrozenEnum.
2718    * <p>
2719    * This method is designed to be overriden.
2720    *
2721    * @name baja.FrozenEnum#contractCommitted
2722    * @function
2723    * @private
2724    */
2725   FrozenEnum.prototype.contractCommitted = function () { 
2726   };
2727   
2728   /**
2729    * Make a FrozenEnum.
2730    *
2731    * @name baja.FrozenEnum#make
2732    * @function
2733    * @private
2734    *
2735    * @param {String|Number} arg the tag or ordinal of the frozen enum we want to access.
2736    *
2737    * @see baja.FrozenEnum#make
2738    *
2739    * @returns {baja.FrozenEnum} the frozen enum.
2740    */  
2741   FrozenEnum.prototype.make = function (arg) {
2742     return this.get(arg);
2743   };
2744   
2745   /**
2746    * Decode a FrozenEnum from a String.
2747    *
2748    * @name baja.FrozenEnum#decodeFromString
2749    * @function
2750    *
2751    * @param {String} str
2752    * @returns {baja.FrozenEnum}
2753    */   
2754   FrozenEnum.prototype.decodeFromString = function (str) {   
2755     return this.get(str);  
2756   };
2757   
2758   /**
2759    * Encode the FrozenEnum to a String.
2760    *
2761    * @name baja.FrozenEnum#encodeToString
2762    * @function
2763    *
2764    * @returns {String}
2765    */ 
2766   FrozenEnum.prototype.encodeToString = function () {    
2767     return this.getTag();
2768   };
2769   
2770   /**
2771    * Default FrozenEnum instance.
2772    *
2773    * @name baja.FrozenEnum.DEFAULT
2774    * @private
2775    * @inner
2776    */   
2777   FrozenEnum.DEFAULT = new FrozenEnum();
2778     
2779   // Register Type
2780   FrozenEnum.registerType("baja:FrozenEnum");        
2781     
2782   /**
2783    * Return true if the enum is active.
2784    *
2785    * @name baja.FrozenEnum#isActive
2786    * @function
2787    *
2788    * @returns {Boolean} true if active.
2789    */
2790   FrozenEnum.prototype.isActive = function () {
2791     return this.$ordinal !== 0;
2792   };
2793   
2794   /**
2795    * Return the ordinal for the frozen enum.
2796    *
2797    * @name baja.FrozenEnum#getOrdinal
2798    * @function
2799    *
2800    * @returns {Number} ordinal.
2801    */
2802   FrozenEnum.prototype.getOrdinal = function () {
2803     return this.$ordinal;
2804   };
2805   
2806   /**
2807    * Return the range for the frozen enum.
2808    *
2809    * @name baja.FrozenEnum#getRange
2810    * @function
2811    *
2812    * @returns {baja.EnumRange} range.
2813    */
2814   FrozenEnum.prototype.getRange = function () {
2815     return this.getType().getRange();
2816   };
2817   
2818   /**
2819    * Return the tag for the FrozenEnum.
2820    *
2821    * @name baja.FrozenEnum#getTag
2822    * @function
2823    *
2824    * @returns {String} tag
2825    */
2826   FrozenEnum.prototype.getTag = function () {
2827     return this.getType().getTag(this.$ordinal);
2828   };
2829   
2830   /**
2831    * Return the display tag for the FrozenEnum.
2832    * <p>
2833    * A display tag is a translated human friendly readable version of a tag.
2834    *
2835    * @name baja.FrozenEnum#getDisplayTag
2836    * @function
2837    *
2838    * @returns {String} display tag.
2839    */
2840   FrozenEnum.prototype.getDisplayTag = function () {
2841     return this.getType().getDisplayTag(this.$ordinal);
2842   };
2843   
2844   /**
2845    * Return the String representation of a FrozenEnum.
2846    *
2847    * @name baja.FrozenEnum#toString
2848    * @function
2849    *
2850    * @returns {String} string
2851    */
2852   FrozenEnum.prototype.toString = function () {   
2853     return this.getDisplayTag();  
2854   };
2855   
2856   /**
2857    * Get a FrozenEnum.
2858    * <p>
2859    * This is the primary way to access an enum...
2860    * <pre>
2861    *   var en = baja.$("baja:Weekday").get("monday");
2862    * </pre>
2863    *
2864    * @name baja.FrozenEnum#get
2865    * @function
2866    *
2867    * @param {String|Number} arg the tag of ordinal of the enum to get.
2868    * @returns {baja.FrozenEnum} the frozen enum.
2869    */
2870   FrozenEnum.prototype.get = function (arg) {   
2871     return this.getRange().get(arg);  
2872   };
2873   
2874   /**
2875    * Equals comparison via tag or ordinal.
2876    * <p>
2877    * This is a convenient way to compare enums...
2878    * <pre>
2879    *   var monday = baja.$("baja:Weekday").get("monday");
2880    *   baja.outln("Is the day Tuesday?: " + monday.is("tuesday"));
2881    * </pre>
2882    *
2883    * @name baja.FrozenEnum#is
2884    * @function
2885    *
2886    * @param {String|Number|baja.FrozenEnum} arg the enum, tag or ordinal used for comparison.
2887    * @returns {Boolean} true if equal.
2888    */
2889   FrozenEnum.prototype.is = function (arg) {  
2890     var tof = typeof arg;
2891     if (tof === "string" || tof === "number") {
2892       return this.equals(this.getRange().get(arg)); 
2893     }
2894     else {
2895       return this.equals(arg);
2896     }    
2897   };
2898   
2899   /**
2900    * Return the Frozen Enum as a Dynamic Enum.
2901    * 
2902    * @name baja.FrozenEnum#asDynamic
2903    * @function
2904    * @see baja.DynamicEnum#make
2905    * 
2906    * @returns {baja.DynamicEnum}
2907    */
2908   FrozenEnum.prototype.asDynamic = function () {
2909     return baja.DynamicEnum.make({en: this});
2910   };
2911   
2912   ////////////////////////////////////////////////////////////////
2913   // Status
2914   ////////////////////////////////////////////////////////////////  
2915   
2916   /**
2917    * @class Represents a baja:Status in BajaScript.
2918    * <p>
2919    * Status provides a bit mask for various standardized 
2920    * status flags in the Baja control architecture. Plus
2921    * it provides for arbitrary extensions using BFacets.
2922    * <p>
2923    * When creating a Simple, always use the 'make' method instead of creating a new Object.
2924    *   
2925    * @name baja.Status
2926    * @extends baja.Simple
2927    */
2928   baja.Status = function (bits, facets) {
2929     // Constructor should be considered private
2930     baja.Status.$super.apply(this, arguments);  
2931     this.$bits = bits;
2932     this.$facets = facets;
2933   }.$extend(baja.Simple);
2934   
2935   /**
2936    * Make a Status
2937    * <p>
2938    * The bits (Number) or (for more arguments) an Object Literal can be used to specify the method's arguments.
2939    * <pre>
2940    *   var st1 = baja.Status.make(baja.Status.DOWN | baja.Status.FAULT);
2941    *   
2942    *   // ... or for more arguments...
2943    *   
2944    *   var st2 = baja.Status.make({
2945    *     bits: baja.Status.DOWN,
2946    *     facets: facets
2947    *   });
2948    * </pre>
2949    * The make method can also be used to create a new status with its state changed...
2950    * <pre>
2951    *   var newStatus = baja.Status.make({
2952    *     orig: oldStatus, 
2953    *     bits: baja.Status.OVERRIDDEN, 
2954    *     state: true
2955    *   }};
2956    * </pre>
2957    *
2958    * @param {Object|Number} obj the Object Literal that specifies the method's arguments or Status bits.
2959    * @param {Number} obj.bits the Status bits.
2960    * @param {baja.Facets} [obj.facets] the facets for the Status.
2961    * @param {baja.Status} [obj.orig] if defined, obj.state must also be defined. This is used to create
2962    *                                 a new Status with one of it's bit states changed (see example above).
2963    * @param {Boolean} [obj.state] the state of the bit to change (used in conjunction with obj.orig).
2964    * @returns {baja.Status} the status.
2965    */   
2966   baja.Status.make = function (obj) {
2967     obj = objectify(obj, "bits");
2968   
2969     var orig = obj.orig,
2970         bits = obj.bits,
2971         state = obj.state,
2972         facets = obj.facets,
2973         nullLookup = baja.Status.$nullLookup;
2974     
2975     // If the bits are a baja.Status then get the bits
2976     if (bajaHasType(bits) && bits.getType().equals(baja.Status.DEFAULT.getType())) {
2977       bits = bits.getBits();
2978     }
2979     
2980     strictArg(bits, Number);
2981     
2982     // Make with original bits...
2983     if (orig !== undefined) {
2984       strictAllArgs([orig, state], [baja.Status, Boolean]);
2985     
2986       bits = state ? (orig.$bits | bits) : (orig.$bits & ~bits);
2987       if (bits === 0 && orig.$facets === facetsDefault) {
2988         return baja.Status.ok;
2989       }   
2990       if (orig.$bits === bits) {
2991         return orig;
2992       }   
2993       facets = orig.$facets;
2994     }
2995     
2996     // Standard make...
2997     facets = bajaDef(facets, facetsDefault);
2998     strictArg(facets, baja.Facets);
2999     
3000     if (facets === facetsDefault) {
3001       if (bits === 0) {
3002         return baja.Status.DEFAULT;
3003       }
3004       
3005       if (bits <= 256) {
3006         if (!(nullLookup.hasOwnProperty(bits))) {
3007           nullLookup[bits] = new baja.Status(bits, facets);
3008         }
3009         return nullLookup[bits];
3010       }
3011     }
3012         
3013     return new baja.Status(bits, facets);
3014   };
3015    
3016   /**
3017    * Make a Status.
3018    * <p>
3019    * The bits (Number) or (for more arguments) an Object Literal can be used to specify the method's arguments.
3020    * <pre>
3021    *   var st1 = baja.Status.make(baja.Status.DOWN | baja.Status.FAULT);
3022    *   
3023    *   // ... or for more arguments...
3024    *   
3025    *   var st2 = baja.$("baja:Status").make({
3026    *     bits: baja.Status.DOWN,
3027    *     facets: facets
3028    *   });
3029    * </pre>
3030    * The make method can also be used to create a new status with its state changed...
3031    * <pre>
3032    *   var newStatus = baja.$("baja:Status").make({
3033    *     orig: oldStatus, 
3034    *     bits: baja.Status.OVERRIDDEN, 
3035    *     state: true
3036    *   }};
3037    * </pre>
3038    *
3039    * @param {Object|Number} obj the Object Literal that specifies the method's arguments or Status bits.
3040    * @param {Number} obj.bits the Status bits.
3041    * @param {baja.Facets} [obj.facets] the facets for the Status.
3042    * @param {baja.Status} [obj.orig] if defined, obj.state must also be defined. This is used to create
3043    *                                 a new Status with one of it's bit states changed (see example above).
3044    * @param {Boolean} [obj.state] the state of the bit to change (used in conjunction with obj.orig).
3045    * @returns {baja.Status} the status.
3046    */     
3047   baja.Status.prototype.make = function (obj) {
3048     return baja.Status.make.apply(baja.Status, arguments);
3049   };
3050   
3051   /**
3052    * Decode a Status from a String.
3053    *
3054    * @param {String} str
3055    * @returns {baja.Status}
3056    */   
3057   baja.Status.prototype.decodeFromString = function (str) {
3058     var x,
3059         semi = str.indexOf(';');
3060     if (semi < 0) {
3061       x = baja.Status.make(parseInt(str, 16));
3062     }
3063     else {
3064       var bits = parseInt(str.substring(0, semi), 16);
3065       x = baja.Status.make({"bits": bits, "facets": facetsDefault.decodeFromString(str.substring(semi + 1))});
3066     }
3067     return x;
3068   };
3069   baja.Status.prototype.decodeFromString = cacheDecode(baja.Status.prototype.decodeFromString);
3070   
3071   /**
3072    * Encode the Status to a String.
3073    *
3074    * @returns {String}
3075    */  
3076   baja.Status.prototype.encodeToString = function () {
3077     var s = this.$bits.toString(16);
3078     if (this.$facets !== facetsDefault) {
3079       s += ";" + this.$facets.encodeToString();
3080     }
3081     return s;
3082   };
3083   baja.Status.prototype.encodeToString = cacheEncode(baja.Status.prototype.encodeToString);
3084   
3085   /**
3086    * Equality test.
3087    *
3088    * @param obj
3089    * @returns {Boolean}
3090    */
3091   baja.Status.prototype.equals = function (obj) {
3092     if (bajaHasType(obj) && obj.getType().equals(this.getType())) {
3093       if (this.$facets === facetsDefault) {
3094         return obj.$bits === this.$bits;
3095       } 
3096       else {
3097         return obj.$bits === this.$bits && this.$facets.equals(obj.$facets);
3098       }
3099     }
3100     else {
3101       return false;
3102     }
3103   };
3104   
3105   /**
3106    * Default Status instance.
3107    */
3108   baja.Status.DEFAULT = new baja.Status(0, facetsDefault);
3109   
3110   // Register Type
3111   baja.Status.registerType("baja:Status"); 
3112     
3113   // If the facets are null then the Status is interned into this Object map
3114   var statusNullLookup = baja.Status.$nullLookup = {};
3115   
3116   /**
3117    * Bit for disabled.
3118    */
3119   baja.Status.DISABLED = 0x0001;
3120   
3121   /**
3122    * Bit for fault.
3123    */
3124   baja.Status.FAULT = 0x0002;
3125   
3126   /**
3127    * Bit for down.
3128    */
3129   baja.Status.DOWN = 0x0004;
3130   
3131   /**
3132    * Bit for alarm.
3133    */
3134   baja.Status.ALARM = 0x0008;
3135   
3136   /**
3137    * Bit for stale.
3138    */
3139   baja.Status.STALE = 0x0010;
3140   
3141   /**
3142    * Bit for overriden.
3143    */
3144   baja.Status.OVERRIDDEN = 0x0020;
3145   
3146   /**
3147    * Bit for null.
3148    */
3149   baja.Status.NULL = 0x0040;
3150   
3151   /**
3152    * Bit for unacked alarm.
3153    */
3154   baja.Status.UNACKED_ALARM = 0x0080;
3155   
3156   /**
3157    * String used in a Facets for identifying the active priority level of a writable point.
3158    */
3159   baja.Status.ACTIVE_LEVEL = "activeLevel";
3160   
3161   /**
3162    * Status for ok (null facets).
3163    */   
3164   baja.Status.ok = baja.Status.DEFAULT;
3165   
3166   /**
3167    * Status for disabled (null facets).
3168    */ 
3169   statusNullLookup[baja.Status.DISABLED] = baja.Status.disabled = new baja.Status(baja.Status.DISABLED, facetsDefault);
3170   
3171   /**
3172    * Status for fault (null facets).
3173    */ 
3174   statusNullLookup[baja.Status.FAULT] = baja.Status.fault = new baja.Status(baja.Status.FAULT, facetsDefault);
3175   
3176   /**
3177    * Status for down (null facets).
3178    */ 
3179   statusNullLookup[baja.Status.DOWN] = baja.Status.down = new baja.Status(baja.Status.DOWN, facetsDefault);
3180   
3181   /**
3182    * Status for alarm (null facets).
3183    */ 
3184   statusNullLookup[baja.Status.ALARM] = baja.Status.alarm = new baja.Status(baja.Status.ALARM, facetsDefault);
3185   
3186   /**
3187    * Status for stale (null facets).
3188    */ 
3189   statusNullLookup[baja.Status.STALE] = baja.Status.stale = new baja.Status(baja.Status.STALE, facetsDefault);
3190   
3191   /**
3192    * Status for overriden (null facets).
3193    */ 
3194   statusNullLookup[baja.Status.OVERRIDDEN] = baja.Status.overridden = new baja.Status(baja.Status.OVERRIDDEN, facetsDefault);
3195   
3196   /**
3197    * Status for null status (null facets).
3198    */ 
3199   statusNullLookup[baja.Status.NULL] = baja.Status.nullStatus = new baja.Status(baja.Status.NULL, facetsDefault);
3200   
3201   /**
3202    * Status for unacked alarm (null facets).
3203    */ 
3204   statusNullLookup[baja.Status.UNACKED_ALARM] = baja.Status.unackedAlarm = new baja.Status(baja.Status.UNACKED_ALARM, facetsDefault);
3205   
3206   /**
3207    * Return the facets for the Status.
3208    *
3209    * @returns {baja.Facets} status facets
3210    */   
3211   baja.Status.prototype.getFacets = function () {    
3212     return this.$facets;
3213   };
3214   
3215   /**
3216    * Return a value from the status facets.
3217    *
3218    * @param {String} name the name of the value to get from the status facets.
3219    * @param [def] if defined, this value is returned if the name can't be found in 
3220    *              the status facets.
3221    * @returns the value from the status facets (null if def is undefined and name can't be found).
3222    */
3223   baja.Status.prototype.get = function (name, def) {    
3224     return this.$facets.get(name, def);
3225   };
3226   
3227   /**
3228    * Return the Status bits.
3229    *
3230    * @returns {Number} status bits.
3231    */
3232   baja.Status.prototype.getBits = function () {    
3233     return this.$bits;
3234   };
3235   
3236   /**
3237    * Return true if the Status is not disabled, fault, down
3238    * stale or null.
3239    *
3240    * @returns {Boolean} true if valid.
3241    */
3242   baja.Status.prototype.isValid = function () {    
3243     return (((this.$bits & baja.Status.DISABLED) === 0) &&
3244             ((this.$bits & baja.Status.FAULT) === 0) && 
3245             ((this.$bits & baja.Status.DOWN)  === 0) && 
3246             ((this.$bits & baja.Status.STALE) === 0) && 
3247             ((this.$bits & baja.Status.NULL)  === 0));  
3248   };
3249   
3250   /**
3251    * Return true if the Status is ok.
3252    *
3253    * @returns {Boolean}
3254    */
3255   baja.Status.prototype.isOk = function () {
3256     return this.$bits === 0;  
3257   };
3258   
3259   /**
3260    * Return true if the Status is disabled.
3261    *
3262    * @returns {Boolean}
3263    */
3264   baja.Status.prototype.isDisabled = function () {
3265     return (this.$bits & baja.Status.DISABLED) !== 0;  
3266   };
3267   
3268   /**
3269    * Return true if the Status is in fault.
3270    *
3271    * @returns {Boolean}
3272    */
3273   baja.Status.prototype.isFault = function () {
3274     return (this.$bits & baja.Status.FAULT) !== 0;  
3275   };
3276   
3277   /**
3278    * Return true if the Status is down.
3279    *
3280    * @returns {Boolean}
3281    */
3282   baja.Status.prototype.isDown = function () {
3283     return (this.$bits & baja.Status.DOWN) !== 0;  
3284   };
3285   
3286   /**
3287    * Return true if the Status is in alarm.
3288    * 
3289    * @name baja.Status#isAlarm
3290    * @function
3291    *
3292    * @returns {Boolean}
3293    */
3294   baja.Status.prototype.isAlarm = function () {
3295     return (this.$bits & baja.Status.ALARM) !== 0;  
3296   };
3297 
3298   /**
3299    * Return true if the Status is stale.
3300    *
3301    * @returns {Boolean}
3302    */
3303   baja.Status.prototype.isStale = function () {
3304     return (this.$bits & baja.Status.STALE) !== 0;  
3305   };
3306 
3307   /**
3308    * Return true if the Status is overriden.
3309    *
3310    * @returns {Boolean}
3311    */
3312   baja.Status.prototype.isOverridden = function () {
3313     return (this.$bits & baja.Status.OVERRIDDEN) !== 0;  
3314   };
3315   
3316   /**
3317    * Return true if the Status is null.
3318    *
3319    * @returns {Boolean}
3320    */
3321   baja.Status.prototype.isNull = function () {
3322     return (this.$bits & baja.Status.NULL) !== 0;  
3323   };
3324   
3325   /**
3326    * Return true if the Status is unacked alarm.
3327    *
3328    * @returns {Boolean}
3329    */
3330   baja.Status.prototype.isUnackedAlarm = function () {
3331     return (this.$bits & baja.Status.UNACKED_ALARM) !== 0;  
3332   };  
3333   
3334   /**
3335    * Return the status from a BIStatus.
3336    *
3337    * @return resolved status value
3338    */
3339   baja.Status.getStatusFromIStatus = function (statusVal) {
3340     var status = statusVal;
3341     
3342     if (statusVal.getType().isComplex() && statusVal.has("out") && statusVal.getOut().getType().is("baja:StatusValue")) {
3343       status = statusVal.getOut().getStatus();
3344     }
3345     else if (statusVal.getType().is("baja:StatusValue")) {
3346       status = statusVal.getStatus();
3347     }
3348         
3349     return status;
3350   };
3351   
3352   ////////////////////////////////////////////////////////////////
3353   // Date and Time
3354   ////////////////////////////////////////////////////////////////  
3355     
3356   (function dateAndTime() {
3357   
3358     ////////////////////////////////////////////////////////////////
3359     // Date and Time Text Formatting
3360     ////////////////////////////////////////////////////////////////
3361     
3362     /**
3363      * @namespace Date and Time formatting.
3364      */
3365     baja.TimeFormat = {};
3366     var SHOW_DATE    = baja.TimeFormat.SHOW_DATE = 0x01,
3367         SHOW_TIME    = baja.TimeFormat.SHOW_TIME = 0x02,
3368         SHOW_SECONDS = baja.TimeFormat.SHOW_SECONDS = 0x04,
3369         SHOW_MILLIS  = baja.TimeFormat.SHOW_MILLIS  = 0x08,
3370         SHOW_ZONE = 0x10; // TODO: Implement timezones. Currently this is unsupported.
3371   
3372     // In the first version, this implementation is currently limited
3373     var patternCache = {},
3374         SHOW = [
3375           0,            // 0  blank
3376           SHOW_DATE,    // 1  YEAR_2
3377           SHOW_DATE,    // 2  YEAR_4
3378           SHOW_DATE,    // 3  MON_1
3379           SHOW_DATE,    // 4  MON_2
3380           SHOW_DATE,    // 5  MON_TAG
3381           SHOW_DATE,    // 6  DAY_1
3382           SHOW_DATE,    // 7  DAY_2
3383           SHOW_TIME,    // 8  HOUR_12_1
3384           SHOW_TIME,    // 9  HOUR_12_2
3385           SHOW_TIME,    // 10 HOUR_24_1
3386           SHOW_TIME,    // 11 HOUR_24_2
3387           SHOW_TIME,    // 12 MIN
3388           SHOW_TIME,    // 13 AM_PM
3389           SHOW_SECONDS | SHOW_MILLIS, // 14 SEC
3390           SHOW_ZONE,    // 15 ZONE_TAG
3391           SHOW_DATE,    // 16  WEEK_1
3392           SHOW_DATE,    // 17  WEEK_2
3393           SHOW_DATE,    // 18  MON
3394           SHOW_ZONE,    // 19  ZONE_OFFSET
3395           SHOW_DATE     // 20  WEEK_YEAR
3396         ],
3397         YEAR_2    = 1,   // YY   two digit year
3398         YEAR_4    = 2,   // YYYY four digit year
3399         MON_1     = 3,   // M    one digit month
3400         MON_2     = 4,   // MM   two digit month
3401         MON_TAG   = 5,   // MMM  short tag month
3402         MON       = 18,  // MMMM  long tag month
3403         DAY_1     = 6,   // D    one digit day of month
3404         DAY_2     = 7,   // DD   two digit day of month
3405         HOUR_12_1 = 8,   // h    one digit 12 hour
3406         HOUR_12_2 = 9,   // hh   two digit 12 hour
3407         HOUR_24_1 = 10,  // H    one digit 24 hour
3408         HOUR_24_2 = 11,  // HH   two digit 24 hour
3409         MIN       = 12,  // mm   two digit minutes
3410         AM_PM     = 13,  // a    AM PM marker
3411         SEC       = 14,  // ss   two digit seconds and millis
3412         ZONE_TAG  = 15,  // z    timezone
3413         WEEK_1    = 16,  // W    short tag day of week
3414         WEEK_2    = 17,  // WW   day of week
3415         ZONE_OFFSET = 19,// Z    timezone offest (RFC 822)
3416         WEEK_YEAR = 20;  // w    week of year
3417     
3418     function toCode(c, count) {
3419       switch(c) {
3420         case "Y": return count <= 2 ? YEAR_2 : YEAR_4;
3421         case "M": 
3422           switch(count) {
3423             case 1: return MON_1;
3424             case 2: return MON_2;
3425             case 3: return MON_TAG;
3426             default: return MON;
3427           }
3428         case "D": return count === 1 ? DAY_1 : DAY_2;
3429         case "h": return count === 1 ? HOUR_12_1 : HOUR_12_2;
3430         case "H": return count === 1 ? HOUR_24_1 : HOUR_24_2;
3431         case "m": return MIN;
3432         case "s": return SEC;
3433         case "a": return AM_PM;
3434         case "z": return ZONE_TAG;
3435         case "Z": return ZONE_OFFSET;
3436         case "W": return count === 1 ? WEEK_1 : WEEK_2;
3437         case "w": return WEEK_YEAR;
3438         default:  return c.charCodeAt(0);
3439       }
3440     }
3441     
3442     function buildPattern(textPattern) {
3443       // Allocate a pattern array
3444       var len = textPattern.length,
3445           pattern = [],
3446           last = textPattern.charAt(0),
3447           count = 1,
3448           i,
3449           c;
3450         
3451       // Parse text pattern into pattern codes
3452       for (i = 1; i < len; ++i) {
3453         c = textPattern.charAt(i);
3454         if (last === c) { 
3455           count++;
3456           continue; 
3457         }
3458         pattern.push(toCode(last, count));
3459         last = c;
3460         count = 1;
3461       }
3462       pattern.push(toCode(last, count));
3463       return pattern;
3464     }
3465     
3466     function pad(s, num) {
3467       if (num < 10) {
3468         s += "0";
3469       }
3470       s += num;
3471       return s;
3472     } 
3473     
3474     /**
3475      * Create a formatting date/time String.
3476      *
3477      * @private
3478      * @ignore
3479      *
3480      * @param {Object} obj the Object Literal for the method's arguments.
3481      * @param {String} [obj.textPattern]
3482      * @param {baja.AbsTime} [obj.absTime]
3483      * @param {Number} [obj.year]
3484      * @param {baja.FrozenEnum} [obj.month]
3485      * @param {Number} [obj.day] days 1 to 31
3486      * @param {Number} [obj.hour]
3487      * @param {Number} [obj.min]
3488      * @param {Number} [obj.sec]
3489      * @param {Number} [obj.ms]
3490      * @param [obj.timeZone]
3491      * @param {Number} [obj.show] 
3492      */
3493     function toDateTimeString(obj) {    
3494       // Get the pattern code
3495       var pattern;
3496       if (patternCache.hasOwnProperty(obj.textPattern)) {
3497         pattern = patternCache[obj.textPattern];
3498       }
3499       else {
3500         pattern = patternCache[obj.textPattern] = buildPattern(obj.textPattern);      
3501       }
3502       
3503       var s = "",
3504           sep1 = -1,
3505           sep2 = -1,
3506           shownCount = 0,
3507           c,
3508           i;
3509 
3510       // walk thru the pattern
3511       for (i = 0; i < pattern.length; ++i) {
3512         // get the code
3513         c = pattern[i];
3514 
3515         // if the code is a separator, save it away and move on
3516         if (c >= SHOW.length) {
3517           if (sep1 === -1) {
3518             sep1 = c;
3519           }
3520           else if (sep2 === -1) {
3521             sep2 = c;
3522           }
3523           continue;
3524         }
3525 
3526         // if we shouldn't show this field, then clear
3527         // the pending separator and move on
3528         if ((SHOW[c] & obj.show) === 0) { 
3529           sep1 = sep2 = -1;
3530           continue;
3531         }
3532 
3533         // we are now going to show this field, so update our show count
3534         shownCount++;
3535 
3536         // if we have a pending separator then write the separator;
3537         // note we don't show the separator if this is the first field
3538         if (shownCount > 1 && sep1 !== -1) {
3539           s += String.fromCharCode(sep1);
3540           if (sep2 !== -1) {
3541             s += String.fromCharCode(sep2);
3542           }
3543           sep1 = sep2 = -1;
3544         }
3545 
3546         // output the field according to the pattern code
3547         shownCount++;
3548         switch(c) {
3549           case YEAR_2:
3550             //issue 12377
3551             //old code -> pad(s, year >= 2000 ? year-2000 : year-1900);
3552             //fix below
3553             s = pad(s, obj.year % 100);
3554             break;
3555           case YEAR_4:
3556             s += obj.year;
3557             break;
3558           case MON_1:
3559             s += obj.month.getOrdinal() + 1;
3560             break;
3561           case MON_2:
3562             s = pad(s, obj.month.getOrdinal() + 1);
3563             break;
3564           case MON_TAG:
3565             s += baja.lex("baja").get(obj.month.getTag() + ".short");
3566             break;
3567           case MON:
3568             s.append(obj.month.getDisplayTag());
3569             break;
3570           case DAY_1:
3571             s += obj.day;
3572             break;
3573           case DAY_2:
3574             s = pad(s, obj.day);
3575             break;
3576           case HOUR_12_1:
3577             if (obj.hour === 0) {
3578               s += "12";
3579             }
3580             else {
3581               s += obj.hour > 12 ? obj.hour - 12 : obj.hour;
3582             }
3583             break;
3584           case HOUR_12_2:
3585             if (obj.hour === 0) {
3586               s += "12";
3587             }  
3588             else {
3589               s = pad(s, obj.hour > 12 ? obj.hour - 12 : obj.hour);
3590             }
3591             break;
3592           case HOUR_24_1:
3593             s += obj.hour;
3594             break;
3595           case HOUR_24_2:
3596             s = pad(s, obj.hour);
3597             break;
3598           case MIN:
3599             s = pad(s, obj.min);
3600             break;
3601           case AM_PM:
3602             s += obj.hour < 12 ? "AM" : "PM";
3603             break;
3604           case SEC:
3605             s = pad(s, obj.sec);
3606             if ((obj.show & SHOW_MILLIS) === 0) {
3607               break;
3608             }
3609             s += ".";
3610             if (obj.ms < 10) {
3611               s += "0";
3612             }
3613             if (obj.ms < 100) {
3614               s += "0";
3615             }
3616             s += obj.ms;
3617             break;
3618           case ZONE_TAG:
3619             // TODO: Timezones
3620             s += "*** Zone tag not supported ***";
3621             break;
3622           case ZONE_OFFSET:
3623             // TODO: Timezones to get UTC offset
3624             var offset = obj.absTime.getOffset();
3625 
3626             if (offset === 0) {
3627               s += "Z";
3628             }
3629             else {
3630               var hrOff = Math.abs(offset / (1000 * 60 * 60)),
3631                   minOff = Math.abs((offset % (1000 * 60 * 60)) / (1000 * 60));
3632 
3633               if (offset < 0) {
3634                 s += "-";
3635               }
3636               else {
3637                 s += "+";
3638               }
3639 
3640               if (hrOff < 10) {
3641                 s += "0";
3642               }
3643               s += hrOff;
3644 
3645               s += ":";
3646               if (minOff < 10) {
3647                 s += "0";
3648               }
3649               s += minOff;
3650             }                    
3651             break;  
3652           case WEEK_1:
3653             s += baja.lex("baja").get(obj.absTime.getDate().getWeekday().getTag() + ".short");
3654             break;
3655           case WEEK_2:
3656             s += obj.absTime.getDate().getWeekday().getDisplayTag();
3657             break;
3658           case WEEK_YEAR:
3659             // TODO: Week year
3660             s += "*** Week year not supported ***";
3661             break;
3662         }
3663 
3664         // clear separators
3665         sep1 = sep2 = -1;
3666       }
3667       return s;
3668     }
3669     
3670    
3671     // These will get set as properties on RelTime, left free floating for
3672     // better minification.
3673     var MILLIS_IN_SECOND = 1000,
3674         MILLIS_IN_MINUTE = MILLIS_IN_SECOND * 60,
3675         MILLIS_IN_HOUR = MILLIS_IN_MINUTE * 60,
3676         MILLIS_IN_DAY = MILLIS_IN_HOUR * 24;
3677     
3678     /**
3679      * @class Represents a baja:Time in BajaScript.
3680      * <p>
3681      * Time stores a time of day which is independent 
3682      * of any date in the past or future.
3683      * <p>
3684      * When creating a Simple, always use the 'make' method instead of creating a new Object.
3685      *
3686      * @name baja.Time
3687      * @extends baja.Simple
3688      */
3689     baja.Time = function (hour, min, sec, ms) {
3690       // Constructor should be considered private
3691       baja.Time.$super.apply(this, arguments); 
3692       this.$hour = hour;
3693       this.$min = min;
3694       this.$sec = sec;
3695       this.$ms = ms;        
3696     }.$extend(baja.Simple);
3697     
3698     /**
3699      * Make a Time.
3700      * <p>
3701      * An Object Liternal is used for the method's arguments...
3702      * <pre>
3703      *   var t1 = baja.Time.make({
3704      *     hour: 23,
3705      *     min: 12,
3706      *     sec: 15,
3707      *     ms: 789
3708      *   });
3709      *   // ...or use a baja.RelTime to specify hour, min, sec and ms...
3710      *   var t2 = baja.Time.make({
3711      *     relTime: myRelTime
3712      *   });
3713      *   // ...or pass in milliseconds past midnight...
3714      *   var t3 = baja.Time.make(12345);
3715      * </pre>
3716      *
3717      * @param {Object} obj the Object Liternal used for the method's arguments.
3718      * @param {Number} [obj.hour] hours (0-23).
3719      * @param {Number} [obj.min] minutes (0-59).
3720      * @param {Number} [obj.sec] seconds (0-59).
3721      * @param {Number} [obj.ms] milliseconds (0-999).
3722      * @param {baja.RelTime} [obj.relTime] if defined, this is the milliseconds 
3723      *                                     since the start of the day. This overrides the 
3724      *                                     other hour, min, sec and ms arguments.
3725      * @returns {baja.Time} the time.
3726      */   
3727     baja.Time.make = function (obj) {
3728       var hour, min, sec, ms;
3729           
3730       function processMillis(millis) {
3731         baja.strictArg(millis, Number);
3732         millis = millis % MILLIS_IN_DAY;
3733         hour = Math.floor(millis / MILLIS_IN_HOUR);
3734         millis = millis % MILLIS_IN_HOUR;
3735         min = Math.floor(millis / MILLIS_IN_MINUTE);
3736         millis = millis % MILLIS_IN_MINUTE;
3737         sec = Math.floor(millis / MILLIS_IN_SECOND);
3738         ms = Math.floor(millis % MILLIS_IN_SECOND);
3739       }
3740 
3741       obj = objectify(obj, "milliseconds");
3742       
3743       if (typeof obj.milliseconds === "number") {
3744         // Create from a number of milliseconds...
3745         processMillis(obj.milliseconds);
3746       } 
3747       else if (bajaHasType(obj.relTime) && obj.relTime.getType().is("baja:RelTime")) {
3748         // Build from rel time (overrides other hour, min, sec and ms on object)...
3749         processMillis(obj.relTime.getMillis());
3750       } 
3751       else {
3752         // Attempt to get time from Object Literal...
3753         hour = bajaDef(obj.hour, 0);
3754         min = bajaDef(obj.min, 0);
3755         sec = bajaDef(obj.sec, 0);
3756         ms = bajaDef(obj.ms, 0);
3757       }
3758     
3759       // Ensure we're dealing with numbers
3760       strictAllArgs([hour, min, sec, ms], [Number, Number, Number, Number]);
3761           
3762       if (hour < 0 || hour > 23 ||
3763           min < 0 || min > 59 ||
3764           sec < 0 || sec > 59 ||
3765           ms < 0 || ms > 999) {
3766         throw new Error("Invalid time: " + hour + ":" + min + ":" + sec + "." + ms);
3767       }
3768       
3769       if (hour === 0 && min === 0 && sec === 0 && ms === 0) {
3770         return baja.Time.DEFAULT;
3771       }
3772       else {
3773         return new baja.Time(hour, min, sec, ms);
3774       }
3775     };
3776     
3777     /**
3778      * Make a Time.
3779      * <p>
3780      * An Object Liternal is used for the method's arguments...
3781      * <pre>
3782      *   var t1 = baja.$("baja:Time").make({
3783      *     hour: 23,
3784      *     min: 12,
3785      *     sec: 15,
3786      *     ms: 789
3787      *   });
3788      *   // ...or use a baja.RelTime to specify hour, min, sec and ms...
3789      *   var t2 = baja.$("baja:Time").make({
3790      *     relTime: timeOfDayMillis 
3791      *   });
3792      *   // ...or pass in milliseconds past midnight...
3793      *   var t3 = baja.Time.make(12345);
3794      * </pre>
3795      *
3796      * @param {Object} obj the Object Liternal used for the method's arguments.
3797      * @param {Number} [obj.hour] hours (0-23).
3798      * @param {Number} [obj.min] minutes (0-59).
3799      * @param {Number} [obj.sec] seconds (0-59).
3800      * @param {Number} [obj.ms] milliseconds (0-999).
3801      * @param {baja.RelTime} [obj.relTime] if defined, this is the milliseconds 
3802      *                                     since the start of the day. This overrides the 
3803      *                                     other hour, min, sec and ms arguments.
3804      * @returns {baja.Time} the time.
3805      */   
3806     baja.Time.prototype.make = function (obj) {
3807       return baja.Time.make.apply(baja.Time, arguments);
3808     };
3809     
3810     /**
3811      * Decode a Time from a String.
3812      *
3813      * @param {String} str
3814      * @returns {baja.Time}
3815      */   
3816     baja.Time.prototype.decodeFromString = function (str) {
3817       // Time ISO 8601 format hh:mm:ss.mmm
3818       var res = /^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])\.([0-9]{3})$/.exec(str); 
3819       
3820       if (res === null) {
3821         throw new Error("Failed to decode time: " + str);
3822       }
3823       
3824       var t = baja.Time.make({
3825         hour: parseInt(res[1], 10),
3826         min: parseInt(res[2], 10),
3827         sec: parseInt(res[3], 10),
3828         ms: parseInt(res[4], 10)
3829       });  
3830           
3831       return t;
3832     };
3833     baja.Time.prototype.decodeFromString = cacheDecode(baja.Time.prototype.decodeFromString);
3834     
3835     // Util for padding out zeros into Strings
3836     function zeroPad(num, n) {
3837       var str = num.toString(10),
3838           zeros = n - str.length, i;
3839       for (i = 0; i < zeros; ++i) {
3840         str = "0" + str;
3841       }
3842       return str;
3843     }
3844     
3845     /**
3846      * Encode a Time to a String.
3847      *
3848      * @returns {String}
3849      */
3850     baja.Time.prototype.encodeToString = function () {
3851       return zeroPad(this.$hour, 2) + ":" + zeroPad(this.$min, 2) + ":" + zeroPad(this.$sec, 2) + "." +  zeroPad(this.$ms, 3);
3852     };
3853     baja.Time.prototype.encodeToString = cacheEncode(baja.Time.prototype.encodeToString);
3854     
3855     /**
3856      * Equality test.
3857      *
3858      * @param obj
3859      * @returns {Boolean}
3860      */
3861     baja.Time.prototype.equals = function (obj) {
3862       if (bajaHasType(obj) && obj.getType().equals(this.getType())) {
3863         return this.getTimeOfDayMillis() === obj.getTimeOfDayMillis();
3864       }
3865       else {
3866         return false;
3867       }
3868     };
3869     
3870     /**
3871      * Default Time instance.
3872      */
3873     baja.Time.DEFAULT = new baja.Time(0, 0, 0, 0);
3874     
3875     /**
3876      * Midnight Time.
3877      */
3878     baja.Time.MIDNIGHT = baja.Time.DEFAULT;
3879     
3880     // Register Type
3881     baja.Time.registerType("baja:Time"); 
3882     
3883     /**
3884      * Return hours (0-23).
3885      *
3886      * @returns {Number}
3887      */
3888     baja.Time.prototype.getHour = function () {
3889       return this.$hour;
3890     };
3891     
3892     /**
3893      * Return minutes (0-59).
3894      *
3895      * @returns {Number}
3896      */
3897     baja.Time.prototype.getMinute = function () {
3898       return this.$min;
3899     };
3900     
3901     /**
3902      * Return seconds (0-59).
3903      *
3904      * @returns {Number}
3905      */
3906     baja.Time.prototype.getSecond = function () {
3907       return this.$sec;
3908     };
3909     
3910     /**
3911      * Return milliseconds (0-999).
3912      *
3913      * @returns {Number}
3914      */
3915     baja.Time.prototype.getMillisecond = function () {
3916       return this.$ms;
3917     };
3918     
3919     /**
3920      * Return the milliseconds since the start of the day.
3921      *
3922      * @returns {Number}
3923      */
3924     baja.Time.prototype.getTimeOfDayMillis = function () {
3925       if (this.$timeOfDayMs === undefined) {
3926         var ret = this.$hour * MILLIS_IN_HOUR;
3927         ret += this.$min * MILLIS_IN_MINUTE;
3928         ret += this.$sec * MILLIS_IN_SECOND;
3929         ret += this.$ms;
3930         this.$timeOfDayMs = ret;
3931       }
3932       return this.$timeOfDayMs;
3933     };
3934     
3935     /**
3936      * Return a new time of day by adding the specified
3937      * duration. If the result goes past midnight, then roll
3938      * into the next day.
3939      *
3940      * @param {baja.RelTime|baja.Time|Number} duration - RelTime or number of millis
3941      * @returns {baja.Time} the new time with the duration added on.
3942      */
3943     baja.Time.prototype.add = function (duration) {
3944       strictArg(duration);
3945       
3946       if (typeof duration.getMillis === 'function') {
3947         duration = duration.getMillis();
3948       } else if (typeof duration.getTimeOfDayMillis === 'function') {
3949         duration = duration.getTimeOfDayMillis();
3950       }
3951       
3952       strictArg(duration, Number);
3953       
3954       return baja.Time.make(MILLIS_IN_DAY 
3955                             + this.getTimeOfDayMillis() 
3956                             + duration);
3957     };
3958 
3959     /**
3960      * Return true if the specified time is before this time.
3961      *
3962      * @param {baja.Time} time
3963      * @returns {Boolean}
3964      */
3965     baja.Time.prototype.isBefore = function (time) {
3966       strictArg(time, baja.Time);
3967       return this.getTimeOfDayMillis() < time.getTimeOfDayMillis();
3968     };
3969     
3970     /**
3971      * Return true if the specified time is after this time.
3972      *
3973      * @param {baja.Time} time
3974      * @returns {Boolean}
3975      */
3976     baja.Time.prototype.isAfter = function (time) {
3977       strictArg(time, baja.Time);
3978       return this.getTimeOfDayMillis() > time.getTimeOfDayMillis();
3979     };
3980   
3981    /**
3982     * Return a String representation of the time.
3983     * 
3984     * @param {Object} [obj] the Object Literal for the method's arguments.
3985     * @params {String} [obj.textPattern] the text pattern to use for formatting.
3986     *                                    If not specified, then the user's default
3987     *                                    time format text pattern will be used.
3988     * @param {Number} [obj.show] flags used to format the time. For more information,
3989     *                            please see {@link baja.TimeFormat}.
3990     * @returns {String}
3991     */
3992     baja.Time.prototype.toString = function (obj) {
3993       var textPattern = (obj && obj.textPattern) || baja.getTimeFormatPattern(),
3994           show = ((obj && obj.show) || 0) | SHOW_TIME;
3995     
3996       // Filter out invalid flags
3997       show &= ~SHOW_DATE;
3998     
3999       return toDateTimeString({
4000         show: show,
4001         textPattern: textPattern,
4002         hour: this.$hour,
4003         min: this.$min,
4004         sec: this.$sec,
4005         ms: this.$ms
4006       });
4007     };
4008       
4009     /**
4010      * @class Represents a baja:Date in BajaScript.
4011      * <p>
4012      * Date represents a specific day, month, and year.
4013      * <p>
4014      * When creating a Simple, always use the 'make' method instead of creating a new Object.
4015      *
4016      * @name baja.Date
4017      * @extends baja.Simple
4018      */
4019     baja.Date = function (year, month, day) {
4020       // Constructor should be considered private
4021       baja.Date.$super.apply(this, arguments); 
4022       this.$year = year;
4023       this.$month = month; // Zero indexed
4024       this.$day = day;       
4025     }.$extend(baja.Simple);
4026     
4027     /**
4028      * Make a Date.
4029      * <p>
4030      * An Object Literal is used to for the method's arguments...
4031      * <pre>
4032      *   var d1 = baja.Date.make({
4033      *     year: 2008,
4034      *     month: baja.$("baja:Month").get("december"),
4035      *     day: 24
4036      *   });
4037      *   // ...or from a JavaScript Date...
4038      *   var d2 = baja.Date.make({
4039      *     jsDate: date
4040      *   });
4041      * </pre>
4042      *
4043      * @param {Object} obj the Object Literal.
4044      * @param {Number} obj.year
4045      * @param {Number|baja.FrozenEnum} obj.month Number (0-11) or a baja:Month FrozenEnum for the month of the year.
4046      * @param {Number} obj.day (1-31).
4047      * @param {Date} [obj.jsDate] A JavaScript Date used to specify the year, month and day.
4048      *                            If defined, this will override the year, month and day arguments.
4049      * @returns {baja.Date}                          
4050      */
4051     baja.Date.make = function (obj) {
4052       obj = objectify(obj);
4053       
4054       var year,
4055           month,
4056           day;
4057       
4058       // Create baja.Date from a JavaScript date
4059       if (obj.jsDate && obj.jsDate instanceof Date) {
4060         year = obj.jsDate.getFullYear();
4061         month = obj.jsDate.getMonth(); // zero index based
4062         day = obj.jsDate.getDate();
4063       }
4064       else {
4065         year = obj.year;
4066         month = obj.month; // If a number, this should be zero index based
4067         day = obj.day;
4068         
4069         // If the month is a baja:Month then get its ordinal as zero index based
4070         if (bajaHasType(month) && month.getType().is("baja:Month")) {
4071           month = month.getOrdinal();
4072         }
4073       }
4074          
4075       // Validate we have these specified
4076       strictAllArgs([year, month, day], [Number, Number, Number]);
4077       
4078       if (year < 0 || month < 0 || month > 11 || day < 1 || day > 31) {
4079         throw new Error("Invalid date range");
4080       }
4081       
4082       // Check to see if we should return the default instance
4083       var d = baja.Date.DEFAULT;
4084       if (year === d.$year && month === d.$month && day === d.$day) {
4085         return d;
4086       }
4087       
4088       return new baja.Date(year, month, day);
4089     };
4090     
4091     /**
4092      * Make a Date.
4093      * <p>
4094      * An Object Literal is used to for the method's arguments...
4095      * <pre>
4096      *   var d1 = baja.$("baja:Date").make({
4097      *     year: 2008,
4098      *     month: baja.$("baja:Month").get("december"),
4099      *     day: 24
4100      *   });
4101      *   // ...or from a JavaScript Date...
4102      *   var d2 = baja.$("baja:Date").make({
4103      *     jsDate: date
4104      *   });
4105      * </pre>
4106      *
4107      * @param {Object} obj the Object Literal.
4108      * @param {Number} obj.year
4109      * @param {Number|baja.FrozenEnum} obj.month Number (0-11) or a baja:Month FrozenEnum for the month of the year.
4110      * @param {Number} obj.day (1-31).
4111      * @param {Date} [obj.jsDate] A JavaScript Date used to specify the year, month and day.
4112      *                            If defined, this will override the year, month and day arguments.
4113      * @returns {baja.Date}                          
4114      */
4115     baja.Date.prototype.make = function (obj) {
4116       return baja.Date.make.apply(baja.Date, arguments);
4117     };
4118     
4119     /**
4120      * Decode a Date from a String.
4121      * 
4122      * @name baja.Date#decodeFromString
4123      * @function
4124      *
4125      * @param {String} str
4126      * @returns {baja.Date}
4127      */   
4128     baja.Date.prototype.decodeFromString = function (str) {
4129       // Decode ISO 8601 encoding yyyy-mm-dd
4130       var res = /^([0-9]{4,})\-([0-9]{2})\-([0-9]{2})$/.exec(str);
4131       
4132       if (res === null) {
4133         throw new Error("Could not decode baja.Date: " + str);
4134       }
4135       
4136       function parse(s) {
4137         return parseInt(s, 10);
4138       }
4139       
4140       var d = baja.Date.make({
4141         year: parse(res[1]), 
4142         month: parse(res[2]) - 1, 
4143         day: parse(res[3])
4144       });
4145       
4146       return d;
4147     };
4148     baja.Date.prototype.decodeFromString = cacheDecode(baja.Date.prototype.decodeFromString);
4149     
4150     /**
4151      * Encode the Date to a String.
4152      * 
4153      * @name baja.Date#encodeToString
4154      * @function
4155      *
4156      * @returns {String}
4157      */ 
4158     baja.Date.prototype.encodeToString = function () {    
4159       var s = zeroPad(this.$year, 4) + "-" + zeroPad((this.$month + 1), 2) + "-" + zeroPad(this.$day, 2);
4160       return s;
4161     };
4162     baja.Date.prototype.encodeToString = cacheEncode(baja.Date.prototype.encodeToString);
4163     
4164     var dateCompareTo = function (date) {
4165       strictArg(date, baja.Date);
4166       if (this.$year !== date.$year) { 
4167         return this.$year - date.$year;
4168       }      
4169       if (this.$month !== date.$month) {
4170         return this.$month - date.$month;
4171       }
4172       if (this.$day !== date.$day) {
4173         return this.$day - date.$day;
4174       }
4175       return 0;
4176     };
4177     
4178     /**
4179      * Equality test.
4180      *
4181      * @param obj
4182      * @returns {Boolean}
4183      */ 
4184     baja.Date.prototype.equals = function (obj) {
4185       if (bajaHasType(obj) && obj.getType().equals(this.getType())) {
4186         return dateCompareTo.call(this, obj) === 0;
4187       }
4188       else {
4189         return false;
4190       } 
4191     };
4192     
4193     /**
4194      * Default Date instance.
4195      */
4196     baja.Date.DEFAULT = new baja.Date(1970, 0, 1);
4197     
4198     // Register Type
4199     baja.Date.registerType("baja:Date"); 
4200     
4201     /**
4202      * Return a Date that maps to the current day.
4203      * 
4204      * @name baja.Date#today
4205      * @function
4206      *
4207      * @returns {baja.Date}
4208      */
4209     baja.Date.today = function () {
4210       return baja.Date.make({jsDate: new Date()});
4211     };
4212     
4213     /**
4214      * Return the year.
4215      *
4216      * @returns {Number}
4217      */
4218     baja.Date.prototype.getYear = function () {
4219       return this.$year;
4220     };
4221     
4222     /**
4223      * Return the month.
4224      *
4225      * @returns {baja.FrozenEnum} a baja:Month FrozenEnum
4226      */
4227     baja.Date.prototype.getMonth = function () {
4228       return baja.$("baja:Month").get(this.$month);
4229     };
4230     
4231     /**
4232      * Return the day (1-31).
4233      *
4234      * @returns {Number}
4235      */
4236     baja.Date.prototype.getDay = function () {
4237       return this.$day;
4238     };
4239     
4240     /**
4241      * Return a new JavaScript Date using this date's year, month and day.
4242      *
4243      * @returns {Date}
4244      */   
4245     baja.Date.prototype.getJsDate = function () {
4246       // JavaScript Date is mutable therefore we have to return a new instance of Date each time
4247       return new Date(this.$year, 
4248                       this.$month, 
4249                       this.$day, 
4250                       /*hours*/0, 
4251                       /*minutes*/0, 
4252                       /*seconds*/0, 
4253                       /*ms*/0);
4254     };
4255     
4256     var getCachedJsDate = function () {
4257       // Lazily create and return an immutable cached version of the JavaScript Date
4258       if (this.$jsDate === undefined) {
4259         this.$jsDate = this.getJsDate();
4260       }
4261       return this.$jsDate;
4262     };
4263     
4264     /**
4265      * Return the weekday as a baja:Weekday FrozenEnum.
4266      *
4267      * @returns {baja.FrozenEnum} a baja:Weekday FrozenEnum.
4268      */
4269     baja.Date.prototype.getWeekday = function () {
4270       return baja.$("baja:Weekday").get(getCachedJsDate.call(this).getDay());
4271     };
4272     
4273    /**
4274     * Return true if the specified date is before this date.
4275     * 
4276     * @returns {Boolean}
4277     */
4278     baja.Date.prototype.isBefore = function (date) {
4279       return dateCompareTo.call(this, date) < 0;
4280     };
4281     
4282    /**
4283     * Return true if the specified date is after this date.
4284     * 
4285     * @returns {Boolean}
4286     */
4287     baja.Date.prototype.isAfter = function (date) {
4288       return dateCompareTo.call(this, date) > 0;
4289     };
4290         
4291    /**
4292     * Return a String representation of the Date.
4293     * 
4294     * @param {Object} [obj] the Object Literal for the method's arguments.
4295     * @params {String} [obj.textPattern] the text pattern to use for formatting.
4296     *                                    If not specified, then the user's default
4297     *                                    time format text pattern will be used.
4298     * @param {Number} [obj.show] flags used to format the time. For more information,
4299     *                            please see {@link baja.TimeFormat}.
4300     * @returns {String}
4301     */
4302     baja.Date.prototype.toString = function (obj) {
4303       var textPattern = (obj && obj.textPattern) || baja.getTimeFormatPattern(),
4304           show = ((obj && obj.show) || 0) | SHOW_DATE;
4305     
4306       // Filter out invalid flags
4307       show &= ~SHOW_TIME;
4308       show &= ~SHOW_SECONDS;
4309       show &= ~SHOW_MILLIS;
4310     
4311       return toDateTimeString({
4312         show: show,
4313         textPattern: textPattern,
4314         year: this.getYear(),
4315         month: this.getMonth(),
4316         day: this.getDay()
4317       });
4318     };
4319     
4320     // TODO: getDayOfYear, isLeapYear, add, subtract, delta, nextDay, prevDay, nextMonth, prevMonth, nextYear, prevYear, next, prev
4321     
4322     /**
4323      * @class Represents a baja:AbsTime in BajaScript.
4324      * <p>
4325      * AbsTime encapsulates an absolute point in time
4326      * relative to a given time zone.
4327      * <p>
4328      * When creating a Simple, always use the 'make' method instead of creating a new Object.
4329      *
4330      * @name baja.AbsTime
4331      * @extends baja.Simple
4332      */
4333     baja.AbsTime = function (date, time, offset) {
4334       // Constructor should be considered private
4335       baja.AbsTime.$super.apply(this, arguments);        
4336       this.$date = date;
4337       this.$time = time;
4338       this.$offset = offset;
4339     }.$extend(baja.Simple);
4340     
4341     /**
4342      * Make an AbsTime.
4343      * <p>
4344      * An Object Literal is used for the method's arguments...
4345      * <pre>
4346      *   var at1 = baja.AbsTime.make({
4347      *     date: baja.Date.make({year: 1981, month: 5, day: 17}),
4348      *     time: baja.Time.make({hour: 15, min: 30}),
4349      *     offset: 0
4350      *   });
4351      *   // ...or from a JavaScript Date (UTC offset is ignored)...
4352      *   var at2 = baja.AbsTime.make({jsDate: new Date()});
4353      * </pre>
4354      *
4355      * @param {Object} [obj] the Object Literal used for the method's arguments.
4356      * @param {baja.Date} [obj.date]
4357      * @param {baja.Time} [obj.time]
4358      * @param {Number} [obj.offset] UTC offset in milliseconds.
4359      * @param {Date} [jsDate] if defined, this date is used for the date and time.
4360      *                        The UTC offset from this date is currently ignored 
4361      *                        (the offset argument is used) This will override the
4362      *                        date and time arguments.
4363      * @returns baja.AbsTime
4364      */
4365     baja.AbsTime.make = function (obj) {
4366       obj = objectify(obj);
4367       
4368       var date = bajaDef(obj.date, baja.Date.DEFAULT),
4369           time = bajaDef(obj.time, baja.Time.DEFAULT),
4370           offset = bajaDef(obj.offset, 0),  // TODO: Should we use the browser's timezone offset by default?
4371           jsDate;
4372           
4373       if (obj.jsDate !== undefined) {
4374         // Get information from JavaScript Date
4375         jsDate = obj.jsDate;
4376         
4377         if (!(jsDate instanceof Date)) {
4378           throw new Error("jsDate must be a JavaScript Date");
4379         }
4380         
4381         date = baja.Date.make({
4382           "year": jsDate.getFullYear(), 
4383           "month": jsDate.getMonth(), 
4384           "day": jsDate.getDate()
4385         });
4386         
4387         time = baja.Time.make({
4388           "hour": jsDate.getHours(), 
4389           "min": jsDate.getMinutes(), 
4390           "sec": jsDate.getSeconds(), 
4391           "ms": jsDate.getMilliseconds()
4392         });
4393       }
4394       
4395       // The year, month and day must always be specified for this to be valid
4396       strictAllArgs([date, time, offset], [baja.Date, baja.Time, Number]);
4397       
4398       if (date === baja.Date.DEFAULT && time === baja.Time.DEFAULT && offset === 0) {
4399         return baja.AbsTime.DEFAULT;
4400       }
4401       
4402       return new baja.AbsTime(date, time, offset);
4403     };
4404     
4405     /**
4406      * Make an AbsTime.
4407      * <p>
4408      * An Object Literal is used for the method's arguments...
4409      * <pre>
4410      *   var at1 = baja.$("baja:AbsTime").make({
4411      *     date: baja.$("baja:Date").make({year: 1981, month: 5, day: 17}),
4412      *     time: baja.$("baja:Time").make({hour: 15, min: 30}),
4413      *     offset: 0
4414      *   });
4415      *   // ...or from a JavaScript Date...
4416      *   var at2 = baja.$("baja:AbsTime").make({jsDate: new Date(), offset: 5});
4417      * </pre>
4418      *
4419      * @param {Object} [obj] the Object Literal used for the method's arguments.
4420      * @param {baja.Date} [obj.date]
4421      * @param {baja.Time} [obj.time]
4422      * @param {Number} [offset]
4423      * @param {Date} [jsDate] if defined, this date is used for the date and time.
4424      *                        The UTC offset from this date is currently ignored 
4425      *                        (the offset argument is used) This will override the
4426      *                        date and time arguments.
4427      * @returns baja.AbsTime
4428      */
4429     baja.AbsTime.prototype.make = function (obj) {
4430       return baja.AbsTime.make.apply(baja.AbsTime, arguments);
4431     };
4432     
4433     /**
4434      * Decode an AbsTime from a String.
4435      *
4436      * @param {String} str
4437      * @returns {baja.AbsTime}
4438      */   
4439     baja.AbsTime.prototype.decodeFromString = function (str) {
4440       // Decode ISO 8601 encoding that BAbsTime creates
4441       var res = /^([0-9]{4,})\-([0-9]{2})\-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]{3})(?:(Z)|(?:(\-|\+)([0-9]{2}):([0-9]{2})))$/.exec(str);
4442       
4443       if (res === null) {
4444         throw new Error("Could not decode AbsTime: " + str);
4445       }
4446       
4447       function parse(s) {
4448         return parseInt(s, 10);
4449       }
4450       
4451       var date = baja.Date.make({
4452         "year": parse(res[1]),
4453         "month": parse(res[2]) - 1, // Zero indexed based
4454         "day": parse(res[3])
4455       });
4456       
4457       var time = baja.Time.make({
4458         "hour": parse(res[4]),
4459         "min": parse(res[5]),
4460         "sec": parse(res[6]),
4461         "ms": parse(res[7])
4462       });
4463           
4464       var offset = 0;
4465       
4466       if (res[8] !== "Z") {
4467         // Parse hour and minutes and convert to millis
4468         offset = parse(res[10]) * (60 * 60 * 1000) + parse(res[11]) * (60 * 1000);
4469         
4470         // Apply sign
4471         if (res[9] === "-") {
4472           offset *= -1;
4473         }
4474       }
4475       
4476       return baja.AbsTime.make({
4477         "date": date, 
4478         "time": time,
4479         "offset": offset
4480       });
4481     };
4482     baja.AbsTime.prototype.decodeFromString = cacheDecode(baja.AbsTime.prototype.decodeFromString);
4483     
4484     /**
4485      * Encode the AbsTime to a String.
4486      *
4487      * @returns {String}
4488      */   
4489     baja.AbsTime.prototype.encodeToString = function () {     
4490       var s = this.$date.encodeToString() + "T" + this.$time.encodeToString();
4491           
4492       if (this.$offset === 0) {
4493         s += "Z";
4494       }
4495       else {
4496         var hrOff = Math.floor(Math.abs(this.$offset / (1000 * 60 * 60)));
4497         var minOff = Math.floor(Math.abs((this.$offset % (1000 * 60 * 60)) / (1000 * 60)));
4498 
4499         if (this.$offset < 0) {
4500           s += "-";
4501         }
4502         else {
4503           s += "+";
4504         }
4505 
4506         s += zeroPad(hrOff, 2) + ":";
4507         s += zeroPad(minOff, 2);
4508       }
4509       
4510       return s;
4511     };
4512     baja.AbsTime.prototype.encodeToString = cacheEncode(baja.AbsTime.prototype.encodeToString);
4513     
4514     /**
4515      * Equality test.
4516      *
4517      * @param obj
4518      * @returns {Boolean}
4519      */
4520     baja.AbsTime.prototype.equals = function (obj) {  
4521       if (bajaHasType(obj) && obj.getType().equals(this.getType())) {
4522         return obj.$date.equals(this.$date) &&
4523                obj.$time.equals(this.$time) &&
4524                obj.$offset === this.$offset;
4525       }
4526       else {
4527         return false;
4528       }  
4529     };
4530     
4531     /**
4532      * Default AbsTime instance - maps to Java Epoch.
4533      */
4534     baja.AbsTime.DEFAULT = new baja.AbsTime(baja.Date.DEFAULT, baja.Time.DEFAULT, 0);
4535     
4536     // Register Type
4537     baja.AbsTime.registerType("baja:AbsTime");
4538     
4539     /**
4540      * Return the data type symbol.
4541      *
4542      * @returns {String}
4543      */   
4544     baja.AbsTime.prototype.getDataTypeSymbol = function () {
4545       return "a";
4546     };
4547     
4548     /**
4549      * Return the time.
4550      *
4551      * @returns {baja.Time}
4552      */   
4553     baja.AbsTime.prototype.getTime = function () {
4554       return this.$time;
4555     };
4556     
4557     /**
4558      * Return the date.
4559      *
4560      * @returns {baja.Date}
4561      */ 
4562     baja.AbsTime.prototype.getDate = function () {
4563       return this.$date;
4564     };
4565     
4566     /**
4567      * Return the UTC offset.
4568      *
4569      * @returns {Number}
4570      */ 
4571     baja.AbsTime.prototype.getOffset = function () {
4572       return this.$offset;
4573     };
4574     
4575     /** 
4576      * Make an AbsTime with the current date and time.
4577      *
4578      * @returns {baja.AbsTime}
4579      */
4580     baja.AbsTime.now = function () {
4581       // TODO: Should we use the browser's timezone?
4582       return baja.AbsTime.make({ jsDate: new Date() });
4583     };
4584     
4585     /**
4586      * Return a new JavaScript Date based on this AbsTime.
4587      * <p>
4588      * Please, note the AbsTime UTC offset is ignored.
4589      *
4590      * @name baja.AbsTime#getJsDate
4591      * @function
4592      *
4593      * @returns {Date}
4594      */
4595     baja.AbsTime.prototype.getJsDate = function () {
4596       // Create a JavaScript Date and return it (warning has no timezone offset)
4597       return new Date(this.$date.getYear(), 
4598                       this.$date.getMonth().getOrdinal(), 
4599                       this.$date.getDay(), 
4600                       this.$time.getHour(), 
4601                       this.$time.getMinute(), 
4602                       this.$time.getSecond(), 
4603                       this.$time.getMillisecond());
4604     };
4605         
4606    /**
4607     * Return a String representation of the AbsTime.
4608     * 
4609     * @param {Object} [obj] the Object Literal for the method's arguments.
4610     * @params {String} [obj.textPattern] the text pattern to use for formatting.
4611     *                                    If not specified, then the user's default
4612     *                                    time format text pattern will be used.
4613     * @param {Number} [obj.show] flags used to format the time. For more information,
4614     *                            please see {@link baja.TimeFormat}.
4615     * @returns {String}
4616     */
4617     baja.AbsTime.prototype.toString = function (obj) {
4618       var textPattern = (obj && obj.textPattern) || baja.getTimeFormatPattern(),
4619           show = (obj && obj.show) || (SHOW_DATE | SHOW_TIME | SHOW_SECONDS | SHOW_MILLIS),
4620           time = this.$time,
4621           date = this.$date;
4622         
4623       return toDateTimeString({
4624         show: show,
4625         textPattern: textPattern,
4626         year: date.getYear(),
4627         month: date.getMonth(),
4628         day: date.getDay(),
4629         hour:  time.getHour(), 
4630         min: time.getMinute(), 
4631         sec: time.getSecond(), 
4632         ms: time.getMillisecond()
4633       });
4634     };
4635         
4636     /**
4637      * @class Represents a baja:RelTime in BajaScript.
4638      * <p>
4639      * RelTime is a Simple type for managing
4640      * a relative amount of time. 
4641      * <p>
4642      * When creating a Simple, always use the 'make' method instead of creating a new Object.
4643      *
4644      * @name baja.RelTime
4645      * @extends baja.Simple
4646      */
4647     baja.RelTime = function (ms) {
4648       baja.RelTime.$super.apply(this, arguments); 
4649       this.$ms = parseInt(ms, 10);
4650     }.$extend(baja.Simple);
4651     
4652     /**
4653      * Make a RelTime.
4654      * <p>
4655      * This method can take a number of milliseconds of an Object Literal with the method's argument...
4656      * <pre>
4657      *   var rt1 = baja.RelTime.make(1000); // One second
4658      *
4659      *   // ...or we can specify an Object Literal for more arguments...
4660      *
4661      *   // Create a RelTime with 2 days + 2 hours + 2 minutes + 2 seconds + 2 milliseconds...
4662      *   var rt2 = baja.RelTime.make({
4663      *     days: 2,
4664      *     hours: 2,
4665      *     minutes: 2,
4666      *     seconds: 2,
4667      *     ms: 2
4668      *   });
4669      * </pre>
4670      *
4671      * @param {Object|Number} [obj] the Object Literal or the number of milliseconds.
4672      * @param {Object} [obj.days] the number of days.
4673      * @param {Object} [obj.hours] the number of hours.
4674      * @param {Object} [obj.minutes] the number of minutes.
4675      * @param {Object} [obj.seconds] the number of seconds.
4676      * @param {Object} [obj.ms] the number of milliseconds.
4677      * @returns {baja.RelTime}
4678      */
4679     baja.RelTime.make = function (obj) {
4680       var ms = 0;
4681       
4682       if (typeof obj === "number") {
4683         ms = obj;
4684       }
4685       else {
4686         obj = objectify(obj, "ms");
4687         ms = bajaDef(obj.ms, 0);
4688         
4689         strictArg(ms, Number);
4690         
4691         if (typeof obj.days === "number") {
4692           ms += MILLIS_IN_DAY * obj.days;
4693         }
4694         if (typeof obj.hours === "number") {
4695           ms += MILLIS_IN_HOUR * obj.hours;
4696         }
4697         if (typeof obj.minutes === "number") {
4698           ms += MILLIS_IN_MINUTE * obj.minutes;
4699         }
4700         if (typeof obj.seconds === "number") {
4701           ms += MILLIS_IN_SECOND * obj.seconds;
4702         }
4703       }
4704           
4705       if (ms === 0) {
4706         return baja.RelTime.DEFAULT;
4707       }
4708       return new baja.RelTime(ms);
4709     };
4710     
4711     /**
4712      * Make a RelTime.
4713      * <p>
4714      * This method can take a number of milliseconds of an Object Literal with the method's argument...
4715      * <pre>
4716      *   var rt1 = baja.$("baja:RelTime").make(1000); // One second
4717      *
4718      *   // ...or we can specify an Object Literal for more arguments...
4719      *
4720      *   // Create a RelTime with 2 days + 2 hours + 2 minutes + 2 seconds + 2 milliseconds...
4721      *   var rt2 = baja.$("baja:RelTime").make({
4722      *     days: 2,
4723      *     hours: 2,
4724      *     minutes: 2,
4725      *     seconds: 2,
4726      *     ms: 2
4727      *   });
4728      * </pre>
4729      *
4730      * @param {Object|Number} [obj] the Object Literal or the number of milliseconds.
4731      * @param {Object} [obj.days] the number of days.
4732      * @param {Object} [obj.hours] the number of hours.
4733      * @param {Object} [obj.minutes] the number of minutes.
4734      * @param {Object} [obj.seconds] the number of seconds.
4735      * @param {Object} [obj.ms] the number of milliseconds.
4736      * @returns {baja.RelTime}
4737      */
4738     baja.RelTime.prototype.make = function (ms) {
4739       return baja.RelTime.make.apply(baja.RelTime, arguments);
4740     };
4741     
4742     /**
4743      * Decode a RelTime from a String.
4744      * 
4745      * @name baja.RelTime#decodeFromString
4746      * @function
4747      *
4748      * @param {String} str
4749      * @returns {baja.RelTime}
4750      */  
4751     baja.RelTime.prototype.decodeFromString = function (str) {
4752       // Parse number
4753       var n = Number(str);
4754       
4755       // If still not a number then throw an error
4756       if (isNaN(n)) {
4757         throw new Error("Unable to create RelTime: " + str);
4758       }
4759     
4760       return baja.RelTime.make(n);
4761     };
4762     baja.RelTime.prototype.decodeFromString = cacheDecode(baja.RelTime.prototype.decodeFromString);
4763     
4764     /**
4765      * Encode the RelTime to a String.
4766      * 
4767      * @name baja.RelTime#encodeToString
4768      * @function
4769      *
4770      * @returns {String}
4771      */ 
4772     baja.RelTime.prototype.encodeToString = function () {
4773       return this.$ms.toString();
4774     };
4775     baja.RelTime.prototype.encodeToString = cacheEncode(baja.RelTime.prototype.encodeToString);
4776     
4777     /**
4778      * Default RelTime instance.
4779      */   
4780     baja.RelTime.DEFAULT = new baja.RelTime(0);
4781     
4782     // Register Type
4783     baja.RelTime.registerType("baja:RelTime");
4784     
4785     /**
4786      * Milliseconds in a second (Number).
4787      */   
4788     baja.RelTime.MILLIS_IN_SECOND = MILLIS_IN_SECOND;
4789     
4790     /**
4791      * Milliseconds in a minute (Number).
4792      */  
4793     baja.RelTime.MILLIS_IN_MINUTE = MILLIS_IN_MINUTE;
4794     
4795     /**
4796      * Milliseconds in an hour (Number).
4797      */  
4798     baja.RelTime.MILLIS_IN_HOUR = MILLIS_IN_HOUR;
4799     
4800     /**
4801      * Milliseconds in a day (Number).
4802      */  
4803     baja.RelTime.MILLIS_IN_DAY = MILLIS_IN_DAY;
4804     
4805     /**
4806      * RelTime instance for a second.
4807      */  
4808     baja.RelTime.SECOND = baja.RelTime.make(MILLIS_IN_SECOND);
4809     
4810     /**
4811      * RelTime instance for a minute.
4812      */
4813     baja.RelTime.MINUTE = baja.RelTime.make(MILLIS_IN_MINUTE);
4814     
4815     /**
4816      * RelTime instance for an hour.
4817      */
4818     baja.RelTime.HOUR = baja.RelTime.make(MILLIS_IN_HOUR);
4819     
4820     /**
4821      * RelTime instance for a day.
4822      */
4823     baja.RelTime.DAY = baja.RelTime.make(MILLIS_IN_DAY);
4824     
4825     /**
4826      * Equality test.
4827      *
4828      * @function
4829      *
4830      * @param obj
4831      * @returns {Boolean}
4832      */
4833     baja.RelTime.prototype.equals = valueOfEquals;
4834     
4835     /**
4836      * Return the data type symbol.
4837      * 
4838      * @name baja.RelTime#getDataTypeSymbol
4839      * @function
4840      *
4841      * @returns {String}
4842      */   
4843     baja.RelTime.prototype.getDataTypeSymbol = function () {
4844       return "r";
4845     };
4846     
4847     /**
4848      * Return number of milliseconds.
4849      *
4850      * @returns {Number}
4851      */    
4852     baja.RelTime.prototype.valueOf = function () {
4853       return this.$ms;
4854     };
4855     
4856     /**
4857      * Return a String representation of a RelTime.
4858      *
4859      * @returns {String}
4860      */
4861     baja.RelTime.prototype.toString = function () {
4862       return this.$ms.toString();
4863     };
4864     
4865     /**
4866      * Return number of milliseconds.
4867      *
4868      * @returns {Number}
4869      */
4870     baja.RelTime.prototype.getMillis = function () {
4871       return this.$ms;
4872     };
4873     
4874     /**
4875      * Return the milliseconds part of this duration.
4876      * 
4877      * @returns {Number}
4878      */
4879     baja.RelTime.prototype.getMillisPart = function () {
4880       return this.$ms % 1000;
4881     };
4882     
4883     function truncateToInteger(num) {
4884       return Math[num < 0 ? 'ceil' : 'floor'](num);
4885     }
4886     /**
4887      * Return number of seconds.
4888      *
4889      * @returns {Number}
4890      */
4891     baja.RelTime.prototype.getSeconds = function () {
4892       return truncateToInteger(this.$ms / MILLIS_IN_SECOND);
4893     };
4894     
4895     /**
4896      * Return the seconds part of this duration.
4897      * 
4898      * @returns {Number}
4899      */
4900     baja.RelTime.prototype.getSecondsPart = function () {
4901       return this.getSeconds() % 60;
4902     };
4903     
4904     /**
4905      * Return number of minutes.
4906      *
4907      * @returns {Number}
4908      */
4909     baja.RelTime.prototype.getMinutes = function () {
4910       return truncateToInteger(this.$ms / MILLIS_IN_MINUTE);
4911     };
4912     
4913     /**
4914      * Return the minutes part of this duration.
4915      * 
4916      * @returns {Number}
4917      */
4918     baja.RelTime.prototype.getMinutesPart = function () {
4919       return this.getMinutes() % 60;
4920     };
4921     
4922     /**
4923      * Return number of hours.
4924      *
4925      * @returns {Number}
4926      */
4927     baja.RelTime.prototype.getHours = function () {
4928       return truncateToInteger(this.$ms / MILLIS_IN_HOUR);
4929     };
4930     
4931     /**
4932      * Return the hours part of this duration.
4933      * 
4934      * @returns {Number}
4935      */
4936     baja.RelTime.prototype.getHoursPart = function () {
4937       return this.getHours() % 24;
4938     };
4939     
4940     /**
4941      * Return number of days.
4942      *
4943      * @returns {Number}
4944      */
4945     baja.RelTime.prototype.getDays = function () {
4946       return truncateToInteger(this.$ms / MILLIS_IN_DAY);
4947     };
4948     
4949     /**
4950      * Return the days part of this duration.
4951      * 
4952      * @returns {Number}
4953      */
4954     baja.RelTime.prototype.getDaysPart = function () {
4955       return this.getDays();
4956     };
4957   
4958   }()); // dateAndTime
4959   
4960   ////////////////////////////////////////////////////////////////
4961   // Icon
4962   //////////////////////////////////////////////////////////////// 
4963   
4964   /**
4965    * @class A BajaScript Icon.
4966    * <p>
4967    * A BajaScript Icon may contain multiple Image references.
4968    * <p>
4969    * For maximum efficiency, all icons in BajaScript are completely interned.
4970    * <p>
4971    * This Constructor shouldn't be invoked directly. Please use the 'make' methods to create
4972    * an instance of an Icon.
4973    *
4974    * @name baja.Icon
4975    * @extends baja.Simple
4976    */
4977  
4978   var iconsCache = {};
4979   
4980   baja.Icon = function (ords) {
4981     baja.Icon.$super.apply(this, arguments); 
4982     this.$ords = strictArg(ords, Array);     
4983   }.$extend(baja.Simple);
4984   
4985   /**
4986    * Default Icon instance.
4987    */
4988   baja.Icon.DEFAULT = new baja.Icon([]);
4989   
4990   function encodeIcon(ords) {
4991     return ords.join("\n");
4992   }
4993     
4994   /**
4995    * Make an Icon.
4996    *
4997    * @param {Array|String} [ords] an array of Image ORDs or Strings or just a single ORD or String
4998    * @returns {baja.Icon}
4999    */
5000   baja.Icon.make = function (ords) {        
5001     if (arguments.length === 0) {
5002       return baja.Icon.DEFAULT;
5003     }
5004     
5005     // If a String is passed in then convert to an Array
5006     if (typeof ords === "string") {
5007       ords = [ords];    
5008     }
5009     // If an array is passed in then treat this as an array of ORDs
5010     if (ords && ords.constructor === Array) {
5011       var encodedStr = encodeIcon(ords);
5012       // If available from the cache then use it...
5013       if (iconsCache.hasOwnProperty(encodedStr)) {
5014         return iconsCache[encodedStr];
5015       }
5016     
5017       if (ords.length === 0) {
5018         return baja.Icon.DEFAULT;
5019       }
5020       if (ords.length === 1 && ords[0].toString() === "") {
5021         return baja.Icon.DEFAULT;
5022       }
5023     
5024       var newOrds = [], i;
5025       for (i = 0; i < ords.length; ++i) {
5026         newOrds.push(baja.Ord.make(ords[i].toString()));
5027       }
5028       
5029       var icon = new baja.Icon(newOrds);
5030       // Store icon in cache
5031       iconsCache[encodedStr] = icon;
5032       icon.$cEncStr = encodedStr;
5033       
5034       return icon;
5035     }
5036     else {
5037       throw new Error("Invalid argument for baja.Icon.make");
5038     }
5039   };
5040   
5041   /**
5042    * Make an Icon.
5043    *
5044    * @param {Array} [images] an array of Image ORDs or Strings
5045    * @returns {baja.Icon}
5046    */
5047   baja.Icon.prototype.make = function (args) {
5048     return baja.Icon.make.apply(baja.Icon, arguments);
5049   };
5050    
5051   /**
5052    * Decode a String to an Icon.
5053    *
5054    * @param {String} str
5055    * @returns {baja.Icon}
5056    */
5057   baja.Icon.prototype.decodeFromString = function (str) {  
5058     // If available from the cache then use it...
5059     if (iconsCache.hasOwnProperty(str)) {
5060       return iconsCache[str];
5061     }
5062   
5063     return baja.Icon.make(str.split("\n"));
5064   };
5065   
5066   /**
5067    * Encode Icon to a String.
5068    *
5069    * @returns {String}
5070    */
5071   baja.Icon.prototype.encodeToString = function () {
5072     if (!this.$cEncStr) {
5073       this.$cEncStr = encodeIcon(this.$ords);
5074     }
5075     return this.$cEncStr;
5076   };
5077     
5078   // Register Type
5079   baja.Icon.registerType("baja:Icon");  
5080   
5081   /**
5082    * Return a String representation of the Icon.
5083    *
5084    * @returns {String}
5085    */
5086   baja.Icon.prototype.toString = function () {
5087     return this.encodeToString();
5088   };
5089   
5090   /**
5091    * Return the inner value of the Icon.
5092    *
5093    * @returns {String}
5094    */
5095   baja.Icon.prototype.valueOf = function () {
5096     return this.toString();
5097   };
5098     
5099   /**
5100    * Return a copy of the image ORDs Array.
5101    * <p>
5102    * Please note, this is a copy of the Image ORDs and not the URIs to access the images.
5103    *
5104    * @see baja.Icon#getImageUris
5105    *
5106    * @returns {Array} an array of ORDs to the images.
5107    */
5108   baja.Icon.prototype.getImageOrds = function () {
5109     // Return a copy of the images array
5110     return Array.prototype.slice.call(this.$ords);
5111   };  
5112   
5113   /**
5114    * Return an array of URIs to the images.
5115    *
5116    * @returns {Array} an array of URI Strings to the images.
5117    */
5118   baja.Icon.prototype.getImageUris = function () {
5119     var regex = /^module\:\/\//, i;
5120     if (!this.$uris) {
5121       this.$uris = [];
5122       for (i = 0; i < this.$ords.length; ++i) {
5123         this.$uris.push(this.$ords[i].toString().replace(regex, "/module/"));
5124       }
5125     }
5126     return Array.prototype.slice.call(this.$uris);
5127   }; 
5128   
5129   /**
5130    * Return the standard Object Icon.
5131    *
5132    * @returns {baja.Icon}
5133    */
5134   var iconStdObj;
5135   baja.Icon.getStdObjectIcon = function () {
5136     if (!iconStdObj) {
5137       iconStdObj = baja.Icon.make(["module://icons/x16/object.png"]); 
5138     }
5139     return iconStdObj;
5140   };
5141   
5142   ////////////////////////////////////////////////////////////////
5143   // Format
5144   //////////////////////////////////////////////////////////////// 
5145   
5146   /**
5147    * @class Format is used to format Objects into Strings using
5148    * a standardized formatting pattern language.
5149    * <p>
5150    * Currently this implementation of Format is limited to processing Lexicon patterns.
5151    * <p>
5152    * This Constructor shouldn't be invoked directly. Please use the 'make' methods to create
5153    * an instance of a Format.
5154    *
5155    * @name baja.Format
5156    * @extends baja.Simple
5157    */
5158   baja.Format = function (pattern) {
5159     baja.Format.$super.apply(this, arguments); 
5160     this.$pattern = strictArg(pattern, String);     
5161   }.$extend(baja.Simple);
5162   
5163   /**
5164    * Default Format instance.
5165    */
5166   baja.Format.DEFAULT = new baja.Format("");
5167       
5168   /**
5169    * Make a Format.
5170    *
5171    * @param {String} [pattern] the Format Pattern String.
5172    * @returns {baja.Format}
5173    */
5174   baja.Format.make = function (pattern) {
5175     pattern = pattern || "";
5176     
5177     if (pattern === "") {
5178       return baja.Format.DEFAULT;
5179     }    
5180     
5181     baja.strictArg(pattern, String);
5182     
5183     return new baja.Format(pattern);    
5184   };
5185   
5186   /**
5187    * Make a Format.
5188    *
5189    * @param {String} [pattern] the Format Pattern String.
5190    * @returns {baja.Format}
5191    */
5192   baja.Format.prototype.make = function (args) {
5193     return baja.Format.make.apply(baja.Format, arguments);
5194   };
5195    
5196   /**
5197    * Decode a String to a Format.
5198    *
5199    * @param {String} str
5200    * @returns {baja.Format}
5201    */
5202   baja.Format.prototype.decodeFromString = function (str) {  
5203     return baja.Format.make(str);
5204   };
5205   
5206   /**
5207    * Encode Format to a String.
5208    *
5209    * @name baja.Format#encodeToString
5210    * @function
5211    *
5212    * @returns {String}
5213    */
5214   baja.Format.prototype.encodeToString = function () {
5215     return this.$pattern;
5216   };
5217     
5218   // Register Type
5219   baja.Format.registerType("baja:Format");  
5220   
5221   /**
5222    * Return a String representation of the Format.
5223    *
5224    * @returns {String}
5225    */
5226   baja.Format.prototype.toString = function () {
5227     return this.$pattern;
5228   };
5229   
5230   /**
5231    * Return the inner value of the Format.
5232    *
5233    * @function
5234    *
5235    * @returns {String}
5236    */
5237   baja.Format.prototype.valueOf = baja.Format.prototype.toString;
5238   
5239   // Scope some of the variables here...
5240   (function () {
5241     var formatMatches = [],
5242         formatErr = "error: ",
5243         lexiconRegex = /^lexicon\(([a-zA-Z0-9]+)\:([a-zA-Z0-9.\-_]+)\)$/;
5244     
5245     // Replace %% with %
5246     formatMatches.push({
5247       isMatch: function (content) {
5248         return content === "";
5249       },
5250       replace: function () {
5251         return "%";
5252       }
5253     });
5254     
5255     // Identity %.%
5256     formatMatches.push({
5257       isMatch: function (content) {
5258         return content === ".";
5259       },
5260       replace: function (content, obj) {
5261         var val, parent, slot = null, i, o;
5262         if (obj.target) {
5263           if (obj.target.container) {
5264             if (obj.target.propertyPath && obj.target.propertyPath.length > 0) {
5265               val = parent = obj.target.container;
5266               
5267               for (i = 0; i < obj.target.propertyPath.length; ++i) {
5268                 slot = obj.target.propertyPath[i];
5269                 parent = val;
5270                 val = val.get(slot);
5271               }
5272 
5273               return obj.display ? parent.getDisplay(slot) : parent.get(slot);
5274             }
5275             else {
5276               return obj.display ? obj.target.container.getDisplay(obj.target.slot) : obj.target.container.get(obj.target.slot);
5277             }
5278           }
5279           else {
5280             o = obj.target.getObject();
5281             return obj.display && bajaHasType(o) && o.getType().isComplex() ? o.getDisplay() : o;
5282           }
5283         } 
5284         else {
5285           o = obj.object;
5286           return obj.display && bajaHasType(o) && o.getType().isComplex() ? o.getDisplay() : o;
5287         }
5288       }
5289     });
5290     
5291     // Lexicon %.%
5292     formatMatches.push({
5293       isMatch: function (content) {
5294         return content.match(lexiconRegex);
5295       },
5296       replace: function (content, obj) {
5297         var result = lexiconRegex.exec(content),
5298             res = null;
5299         lexiconRegex.lastIndex = 0;
5300         try {
5301           res = baja.lex(result[1]).get(result[2]);
5302         }
5303         catch (ignore) {}
5304         return res;
5305       }
5306     });
5307         
5308     // Reflect Call
5309     formatMatches.push({
5310       isMatch: function (content) {
5311         // Always last in the list so this should always match
5312         return true;
5313       },
5314       replace: function (content, obj) {        
5315         var val, parent, slot = null, x,
5316             split = content.split(/\./g);
5317         
5318         // Process the format text
5319         val = parent = obj.object; 
5320         
5321         for (x = 0; x < split.length; ++x) {
5322           if (val === null || val === undefined) {
5323             return formatErr + content;
5324           }
5325         
5326           // First try looking for the Slot
5327           if (bajaHasType(val, "baja:Complex") && val.has(split[x])) {
5328             slot = val.getSlot(split[x]);
5329             parent = val;
5330             val = val.get(slot);
5331           }
5332           // If there's not Slot then see if a function exists
5333           else {
5334             // Nullify this since at this point we're no longer looking up a Slot chain
5335             slot = null;
5336             parent = null;
5337             
5338             if (typeof val["get" + split[x].capitalizeFirstLetter()] === "function") {
5339               val = val["get" + split[x].capitalizeFirstLetter()]();
5340             }
5341             else if (typeof val[split[x]] === "function") {
5342               val = val[split[x]]();
5343             }
5344             else {
5345               val = null;
5346             }
5347           }
5348         }
5349         
5350         if (slot && parent) {
5351           val = obj.display ? parent.getDisplay(slot) : parent.get(slot);
5352           return val;
5353         }
5354         else if (bajaHasType(val, "baja:Complex")) {
5355           parent = val.getParent();
5356           if (parent) {
5357             val = obj.display ? parent.getDisplay(val.getPropertyInParent()) : parent.get(val.getPropertyInParent());
5358             return val;
5359           }
5360         }
5361         // As a last resort, just call toString
5362         if (val !== null) {
5363           return val.toString();
5364         }
5365         return val;
5366       }
5367     });
5368       
5369     
5370     /**
5371      * Format the specified object using the format pattern.
5372      * <p>
5373      * This method can take an Object Literal or a single pattern String
5374      * argument.
5375      *
5376      * @param {Object} obj the Object Literal.
5377      * @param {String} obj.pattern the format pattern to process.
5378      * @param {Boolean} [obj.display] if true, the display string of a Property value is used.
5379      *                                If false, the toString version of a Property value is used.
5380      *                                By default, this value is true (in BajaScript, most of the time
5381      *                                we're dealing with mounted Components in a Proxy Component Space).  
5382      *
5383      * @returns {String}
5384      */
5385     baja.Format.format = function (obj) { 
5386 
5387       // TODO: Currently format processing doesn't work in exactly the same way as Niagara. 
5388       // Certainly it can never be 100% accurate. However, we can try to cover most common use cases
5389       // that we want to support.
5390     
5391       obj = objectify(obj, "pattern");
5392       var pattern = obj.pattern;
5393       obj.display = bajaDef(obj.display, true);
5394        
5395       // Format replace       
5396       var result = pattern.replace(/%[^%]*%/g, function (match) {
5397         // Remove '%' at start and end
5398         var content = match.substring(1, match.length - 1), i;
5399         
5400         // Match up with a format handler and process it
5401         for (i = 0; i < formatMatches.length; ++i) {
5402           if (formatMatches[i].isMatch(content)) {
5403             var res = formatMatches[i].replace(content, obj);
5404             return res === null ? formatErr + content : res;
5405           }
5406         }
5407         return formatErr + content;
5408       }); 
5409 
5410       return result;   
5411     };      
5412   }());
5413   
5414   /**
5415    * Format the specified object using the format pattern.
5416    *
5417    * @returns {String}
5418    */
5419   baja.Format.prototype.format = function (obj) {
5420     obj = objectify(obj);
5421     obj.pattern = this.$pattern;
5422     return baja.Format.format(obj);
5423   };
5424   
5425   ////////////////////////////////////////////////////////////////
5426   // Permissions
5427   //////////////////////////////////////////////////////////////// 
5428   
5429   /**
5430    * @class Permissions for a given security domain.
5431    * <p>
5432    * This Constructor shouldn't be invoked directly. Please use the 'make' methods to create
5433    * an instance of a Permissions Object. 
5434    *
5435    * @name baja.Permissions
5436    * @extends baja.Simple
5437    */
5438   baja.Permissions = function (mask) {
5439     baja.Permissions.$super.apply(this, arguments); 
5440     this.$mask = mask;
5441   }.$extend(baja.Simple);
5442     
5443   var OPERATOR_READ = baja.Permissions.OPERATOR_READ = 0x0001,
5444       OPERATOR_WRITE = baja.Permissions.OPERATOR_WRITE = 0x0002,
5445       OPERATOR_INVOKE = baja.Permissions.OPERATOR_INVOKE = 0x0004,
5446       ADMIN_READ = baja.Permissions.ADMIN_READ = 0x0010,
5447       ADMIN_WRITE = baja.Permissions.ADMIN_WRITE  = 0x0020,
5448       ADMIN_INVOKE = baja.Permissions.ADMIN_INVOKE = 0x0040,
5449       permissionsAll = OPERATOR_READ | OPERATOR_WRITE | OPERATOR_INVOKE | ADMIN_READ | ADMIN_WRITE | ADMIN_INVOKE,
5450       permissionsCache = {};
5451   
5452   permissionsCache[0] = baja.Permissions.none = baja.Permissions.DEFAULT = new baja.Permissions(0);
5453   permissionsCache[permissionsAll] = baja.Permissions.all = new baja.Permissions(permissionsAll);
5454   
5455   /**
5456    * Make a permissions object.
5457    *
5458    * @params {String|Number} perm the permissions to decode.
5459    * @returns {baja.Permissions}
5460    */
5461   baja.Permissions.prototype.make = function (perm) {
5462     var mask = 0,
5463         i,
5464         p;
5465         
5466     if (typeof perm === "string") {        
5467       for (i = 0; i < perm.length; ++i) {
5468         switch(perm.charAt(i)) {
5469           case "i": mask |= OPERATOR_INVOKE; break;
5470           case "r": mask |= OPERATOR_READ; break;
5471           case "w": mask |= OPERATOR_WRITE; break;
5472           case "I": mask |= ADMIN_INVOKE; break;
5473           case "R": mask |= ADMIN_READ; break;
5474           case "W": mask |= ADMIN_WRITE; break;
5475         }
5476       }
5477     }
5478     else {
5479       mask = perm;
5480     }
5481     
5482     // Get permissions from cache
5483     if (permissionsCache[mask]) {
5484       p = permissionsCache[mask];
5485     }
5486     else {
5487       p = permissionsCache[mask] = new baja.Permissions(mask);
5488     }
5489     return p;
5490   };
5491   
5492   /**
5493    * Make a permissions object.
5494    *
5495    * @params {String|Number} perm the permissions to decode.
5496    * @returns {baja.Permissions}
5497    */
5498   baja.Permissions.make = function (perm) {
5499     return baja.Permissions.DEFAULT.make.apply(baja.Permissions, arguments);
5500   };
5501   
5502   /**
5503    * Decode Permissions from a String.
5504    *
5505    * @name baja.Permissions#decodeFromString
5506    * @function
5507    *
5508    * @returns {baja.Permissions}
5509    */  
5510   baja.Permissions.prototype.decodeFromString = cacheDecode(function (s) {
5511     return this.make(s);
5512   });
5513   
5514   /**
5515    * Encode Permissions to a String.
5516    *
5517    * @name baja.Permissions#encodeToString
5518    * @function
5519    *
5520    * @returns {String}
5521    */  
5522   baja.Permissions.prototype.encodeToString = cacheEncode(function () {
5523     var s = "";
5524     if (this.hasOperatorInvoke()) {
5525       s += "i";
5526     }
5527     if (this.hasOperatorRead()) {
5528       s += "r";
5529     }
5530     if (this.hasOperatorWrite()) {
5531       s += "w";
5532     }
5533     if (this.hasAdminInvoke()) {
5534       s += "I";
5535     }
5536     if (this.hasAdminRead()) {
5537       s += "R";
5538     }
5539     if (this.hasAdminWrite()) {
5540       s += "W";
5541     }
5542     return s;
5543   });
5544   
5545   /**
5546    * Return a String representation of the Permissions.
5547    *
5548    * @returns {String}
5549    */
5550   baja.Permissions.prototype.toString = function () {
5551     return this.encodeToString();
5552   };
5553   
5554   // Register Type
5555   baja.Permissions.registerType("baja:Permissions");  
5556   
5557   /**
5558    * Is the operator read permission enabled?
5559    *
5560    * @returns {Boolean}
5561    */
5562   baja.Permissions.prototype.hasOperatorRead = function () {
5563     return (this.$mask & OPERATOR_READ) !== 0;
5564   };
5565   
5566   /**
5567    * Is the operator write permission enabled?
5568    *
5569    * @returns {Boolean}
5570    */
5571   baja.Permissions.prototype.hasOperatorWrite = function () {
5572     return (this.$mask & OPERATOR_WRITE) !== 0;
5573   };
5574   
5575   /**
5576    * Is the operator invoke permission enabled?
5577    *
5578    * @returns {Boolean}
5579    */
5580   baja.Permissions.prototype.hasOperatorInvoke = function () {
5581     return (this.$mask & OPERATOR_INVOKE) !== 0;
5582   };
5583   
5584   /**
5585    * Is the admin read permission enabled?
5586    *
5587    * @returns {Boolean}
5588    */
5589   baja.Permissions.prototype.hasAdminRead = function () {
5590     return (this.$mask & ADMIN_READ) !== 0;
5591   };
5592   
5593   /**
5594    * Is the admin write permission enabled?
5595    *
5596    * @returns {Boolean}
5597    */
5598   baja.Permissions.prototype.hasAdminWrite = function () {
5599     return (this.$mask & ADMIN_WRITE) !== 0;
5600   };
5601   
5602   /**
5603    * Is the admin invoke permission enabled?
5604    *
5605    * @returns {Boolean}
5606    */
5607   baja.Permissions.prototype.hasAdminInvoke = function () {
5608     return (this.$mask & ADMIN_INVOKE) !== 0;
5609   };
5610   
5611   /**
5612    * Return true if the specified permissions are enabled.
5613    *
5614    * @params {Number|baja.Permissions}
5615    * @returns {Boolean}
5616    */
5617   baja.Permissions.prototype.has = function (mask) {
5618     if (mask && mask instanceof baja.Permissions) {
5619       mask = mask.getMask();
5620     }
5621     return (this.$mask & mask) === mask;
5622   };
5623   
5624   /**
5625    * Return the mask for the permissions.
5626    *
5627    * @returns {Boolean}
5628    */
5629   baja.Permissions.prototype.getMask = function () {
5630     return this.$mask;
5631   };
5632             
5633 }(baja));