1 /**
  2  * @license Copyright 2010, Tridium, Inc. All Rights Reserved.
  3  */
  4 
  5 /**
  6  * Core System Architecture for BajaScript including Type and Registry System.
  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, 
 14   eqeqeq: true, bitwise: true, regexp: false, newcap: true, immed: true, strict: false, 
 15   indent: 2, vars: true, continue: true */
 16 
 17 /*global setTimeout, clearTimeout, setInterval, clearInterval, bajaJsPrint*/ 
 18 
 19 /**
 20  * @class BaseBajaObj
 21  * <p>
 22  * The base class for all BajaScript Objects.
 23  */
 24 var BaseBajaObj = function () {
 25   "use strict";
 26 };
 27 
 28 /**
 29  * @namespace the core BajaScript namespace.
 30  */
 31 var baja = new BaseBajaObj();
 32 
 33 ////////////////////////////////////////////////////////////////
 34 // Very common/generic methods
 35 //////////////////////////////////////////////////////////////// 
 36 
 37 (function common(baja, BaseBajaObj) {
 38   // Use ECMAScript 5 Strict Mode
 39   "use strict";
 40       
 41   /**
 42    * Indicates whether some other object is equal to this one.
 43    *
 44    * @param {Object} obj the reference object with which to compare.
 45    *
 46    * @returns {Boolean} true if this object is the same as the obj argument; false otherwise.
 47    */
 48   BaseBajaObj.prototype.equals = function (obj) {
 49     return this === obj;
 50   };
 51 
 52   /**
 53    * Return the inner value of object.
 54    * <p>
 55    * By default the object's instance is returned.
 56    *
 57    * @returns the inner value of the object or just the object's instance.
 58    */
 59   BaseBajaObj.prototype.valueOf = function () {
 60     return this;
 61   };
 62 
 63   ////////////////////////////////////////////////////////////////
 64   // Extra Function Methods
 65   //////////////////////////////////////////////////////////////// 
 66 
 67   /**
 68    * Extends an Object by setting up the prototype chain.
 69    * <p>
 70    * This method can have a Function or Object passed in as an argument.
 71    * If a Function is passed in, 'new' will be called on it to create an Object.
 72    * The new Object will then be set on the target Function's prototype.
 73    *
 74    * @param {Function|Object}  Parent  
 75    * @returns {Function}  the instance Function.
 76    */
 77   Function.prototype.$extend = function (Parent) {
 78     // So the 'super' class can be tracked, a '$super' property is
 79     // created and attached to the Function instance
 80     if (typeof Parent === 'function') {
 81       this.prototype = new Parent();
 82       this.$super = Parent;
 83     }
 84     else {
 85       this.prototype = Parent;
 86       this.$super = Parent.constructor;
 87     }
 88     this.prototype.constructor = this;
 89     return this;
 90   };
 91 
 92   ////////////////////////////////////////////////////////////////
 93   // Extra Array Methods
 94   //////////////////////////////////////////////////////////////// 
 95 
 96   if (typeof Array.prototype.contains !== "function") {
 97     if (typeof Array.prototype.indexOf === "function") {
 98       /** 
 99        * @ignore - get JsDoc to ignore this symbols so it's not picked up accidently.
100        */
101       Array.prototype.contains = function (o) {
102         return this.indexOf(o) > -1;
103       };
104     }
105     else {
106       Array.prototype.contains = function (o) {
107         var i;
108         for (i = 0; i < this.length; i++) {
109           if (this[i] === o) {
110             return true;
111           }
112         }
113         return false;
114       };
115     }
116   }
117  
118 }(baja, BaseBajaObj));
119 
120 ////////////////////////////////////////////////////////////////
121 // BajaScript
122 //////////////////////////////////////////////////////////////// 
123 
124 (function bajaNamespace(baja, BaseBajaObj) {
125   // Use ECMAScript 5 Strict Mode
126   "use strict";
127   
128   ////////////////////////////////////////////////////////////////
129   // Baja
130   //////////////////////////////////////////////////////////////// 
131   
132   /**
133    * BajaScript's version number (maj.min.build.patch).
134    */
135   baja.version = "1.0.0.0";
136   
137   var bsLexicons = {},           // BajaScript Lexicons
138       bsRegStorage = null,       // BajaScript Registry Storage
139       bsClockTimeouts = {},      // BajaScript ticket timeouts
140       bsClockIntervals = {};     // BajaScript ticket intervals
141 
142   ////////////////////////////////////////////////////////////////
143   // Debug
144   //////////////////////////////////////////////////////////////// 
145 
146   (function debug() {
147   
148     /**
149      * Print a message to BajaScript's debug output followed by a newline. 
150      * By default just looks for a <code>bajaJsPrint</code> global function and 
151      * passes the message to that.
152      *
153      * @param {String} msg  the message to output.
154      * 
155      * @returns baja
156      */
157     baja.outln = function (msg) {
158       // If BajaScript has stopped then don't output anything else...
159       if (baja.isStopping()) {
160         return this;
161       }
162       
163       if (typeof bajaJsPrint === "function") {
164         bajaJsPrint(msg);
165       }
166       return this;
167     };
168     
169     /**
170      * Attempt to clear BajaScript's debug output.
171      * 
172      * @returns baja
173      */
174     baja.clearOut = function () {
175       return this;
176     };
177        
178     /**
179      * Print out the error to the specified line printer function. This gets called
180      * by the default error handler.
181      * 
182      * @name baja.printError
183      * @private
184      * @inner
185      * 
186      * @see baja.error
187      * 
188      * @param {Error} e the error to be printed.
189      * @param {Function} print the print function to be called each time a line of 
190      *                         the error is to be printed out.
191      */
192     function printError(e, print) {      
193       var errorMsg = e.name + ": ";
194       
195       // Some JavaScript engines give errors a nice full stack trace...
196       if (e.stack) {
197         
198         if (e.message && 
199             typeof e.stack === "string" &&
200             e.stack.indexOf(errorMsg + e.message) !== 0) {
201           print(errorMsg + e.message);
202         }
203 
204         print(e.stack);
205         return;
206       }
207   
208       if (e.message) {
209         print(errorMsg + e.message);
210       }
211       else {
212         print(errorMsg + e);
213       }
214   
215       // Add a try/catch just in case because ECMAScript 5 disallows access to the callee and caller
216       try {
217         // Cater for IE and Safari and try to print out a pseudo stack trace...
218         var func,
219             fn,
220             args = arguments, 
221             stackSize = 0, 
222             maxStack = baja.stackTraceLimit || 20,
223             maxFuncLength = 200;
224             
225         if (args.callee && args.callee.caller) {
226           func = args.callee.caller;
227           while (typeof func === "function" && stackSize < maxStack) {
228             fn = func.name;
229             if (!fn) {
230               // Attempt to format the function into some sort of stack trace...
231               fn = func.toString().replace(/[\r\n]/g, " ");
232               
233               // Don't allow the string to get too big...
234               if (fn.length > maxFuncLength) {
235                 fn = fn.substring(0, maxFuncLength) + "...";
236               }
237             }
238 
239             print(" at " + fn);
240             func = func.caller;
241             stackSize++;
242           }
243           if (stackSize >= maxStack) {
244             print("Stack trace limit exceeded (" + maxStack + ")");
245           }
246         }
247       }
248       catch (ignore) {
249       }
250     }
251   
252     /**
253      * Print an error message in BajaScript's debug output. In IE/Safari the
254      * length of the stack trace will be limited to 20 records - you can
255      * change this by setting <code>baja.stackTraceLimit</code>.
256      * <p>
257      * By default, this method calls {@link baja.printError} using 
258      * {@link baja.outln} as the printer function.
259      *
260      * @param e  the error to output.
261      * 
262      * @returns baja
263      */    
264     baja.error = function (e) {
265       if (baja.isStopping()) {
266         return this;
267       }
268     
269       // Make sure we have an Error object
270       e = e instanceof Error ? e : new Error(e);
271       
272       // Print the error out using baja.outln. 
273       printError(e, baja.outln);
274       
275       // If this isn't a Server error then attempt to also log it in the Server (if specified)
276       if (baja.isStarted() && 
277           baja.isLogClientErrorsInServer() && 
278           e && !(e instanceof baja.comm.ServerError)) {
279         var error = "";
280         printError(e, function (line) {
281           error += line + "\n";
282         });  
283         
284         // Make the network call to log the error
285         baja.comm.error(error);
286       }
287       
288       return this;
289     };
290     
291     baja.stackTraceLimit = 20;
292   }());
293   
294   ////////////////////////////////////////////////////////////////
295   // Baja Util
296   //////////////////////////////////////////////////////////////// 
297   
298   /**
299    * The global default fail callback function.
300    * <p>
301    * Throughout BajaScript there are places where network calls may be made. In one of these cases,
302    * a developer has the option of specifying a 'fail' callback. If the fail callback isn't specifed
303    * by the developer, it will default back to this function.
304    * 
305    * @see baja.ok
306    */
307   baja.fail = function (err) {        
308     // Output error to the BajaScript Console...
309     err = err || new Error();
310     baja.error(err instanceof Error ? err : new Error(err));
311   };
312   
313   /**
314    * The global default ok callback function.
315    * <p>
316    * Throughout BajaScript there are places where network calls may be made. In one of these cases,
317    * a developer has the option of specifying an 'ok' callback. If the ok callback isn't specifed
318    * by the developer, it will default back to this function.
319    *
320    * @see baja.fail
321    */
322   baja.ok = function () {
323   };
324   
325   /**
326    * A function that does nothing (noop = no-operation).
327    *
328    * @name baja.noop
329    * @function
330    */
331   baja.noop = baja.ok;
332       
333   /**
334    * @class Lexicon is a map of name/value pairs for a specific locale.
335    *
336    * @see baja.lex
337    *
338    * @name Lexicon
339    * @extends BaseBajaObj
340    * @inner
341    * @public
342    */   
343   var Lexicon = function (moduleName, data) {  
344     baja.strictAllArgs([moduleName, data], [String, Object]);
345     this.$moduleName = moduleName;
346     this.$data = data;
347   }.$extend(BaseBajaObj);
348 
349   /**
350    * Return a value from the Lexicon for a given key.
351    * <p>
352    * The argument for this method can be either a String key or an Object Literal.
353    *
354    * @see baja.lex
355    *
356    * @param {Object|String} obj the Object Literal that contains the method's arguments or a String key.
357    * @param {String} obj.key the key to look up.
358    * @param {String} obj.def the default value to return if the key can't be found.
359    *                         By default this is null.
360    * @param {Array|String} obj.args arguments used for String formatting.
361    * 
362    * @returns {String} the value for the Lexicon or return def if can't be found.
363    */
364   Lexicon.prototype.get = function (obj) {
365     obj = baja.objectify(obj, "key");
366     obj.def = baja.def(obj.def, null);
367 
368     var val = this.$data.hasOwnProperty(obj.key) ? this.$data[obj.key] : obj.def;
369 
370     // TODO: Niagara uses Java's MessageFormat that does more than this. For now we'll stick
371     // with this use case as this should cover everything.
372 
373     // If we have some message formatting arguments them use them
374     if (obj.args && typeof val === "string") {
375       var args = obj.args;
376       if (args.constructor !== Array) {
377         args = [args];
378       }
379 
380       // Replace {number} with value from args
381       var regex = /\{[0-9]+\}/g;
382       val = val.replace(regex, function (entry) {
383         var i = parseInt(entry.substring(1, entry.length - 1), 10);
384         return args[i] !== undefined ? args[i] : entry;
385       });
386     }
387 
388     return val;
389   };
390 
391   /**
392    * Return the Lexicon's module name.
393    *
394    * @returns {String}
395    */
396   Lexicon.prototype.getModuleName = function () {
397     return this.$moduleName;
398   };
399   
400   (function util() {
401   
402     /**
403      * Strict Argument Check.
404      * <p>
405      * Checks a given argument to ensure it's not undefined. If a Constructor is specified
406      * then it matches the argument's Constructor against it.
407      * <pre>
408      * // Example...
409      * baja.strictArg(arg1, String);
410      * </pre>
411      * <p>
412      * The second argument can also be a String TypeSpec or a Type Object. This will ensure the 
413      * argument has the correct TypeSpec.
414      * <pre>
415      * // Example...
416      * baja.strictArg(arg1, "control:NumericWritable");
417      * 
418      * ...or...
419      *
420      * baja.strictArg(arg1, baja.lt("control:NumericWritable")); 
421      * </pre>
422      *
423      * @private
424      * 
425      * @param arg  the argument being tested.
426      * @param {Function|String|Type} [ctor]  optional Constructor function used to test the argument against. Can also be 
427      *                                       a String TypeSpec or a Type Object.
428      * @param {String} [errMsg] optional message to specify if argument check fails.
429      * @returns arg
430      */
431     baja.strictArg = function (arg, ctor, errMsg) {
432       if (arg === undefined) {
433         if (errMsg) {
434           throw new Error(errMsg);
435         } else {
436           var err = "Invalid argument (undefined)";
437           if (ctor) {
438             err += ". Expected " + ctor.name;
439           }      
440           throw new Error(err);
441         }
442       }
443       // Null arguments are acceptable (just like Java)
444       else if (arg === null) {
445         return arg;
446       }
447   
448       // Bail any type checking if we don't have a valid Constructor
449       if (ctor === undefined || ctor === null) {
450         return arg;
451       }
452       
453       var typeOfCtor = typeof ctor;
454       
455       // Do the type check
456       if (typeOfCtor === "function" && (arg.constructor === ctor || arg instanceof ctor)) {
457         return arg;
458       }
459   
460       // Handle if the ctor is a TypeSpec (String TypeSpec or Type Object)...
461       var type;
462       if (typeOfCtor === "string" || typeOfCtor === "object") {
463         type = baja.lt(ctor);
464         if (!baja.hasType(arg)) {
465           throw new Error("Invalid argument. Expected Type: " + type);
466         }
467         if (!arg.getType().is(type)) {
468           throw new Error("Invalid argument. Type " + arg.getType() + " is not expected Type: " + type);
469         }
470         return arg;
471       }
472     
473       // Ensure we have a valid names for the Constructors...
474       var nm = ctor.name;
475       if (!nm && ctor.$name) {
476         nm = ctor.$name;
477       }
478   
479       var cnm = arg.constructor.name;
480       if (!cnm && arg.constructor.$name) {
481         cnm = arg.constructor.$name;
482       }
483   
484       throw new Error("Invalid argument type. Expected " + nm + ", received " + cnm + " instead");
485     };
486       
487     /**
488      * Strict Arguments Check.
489      * <p>
490      * Checks all of the given arguments to ensure they're not undefined. An array of Constructors
491      * is passed in to ensure they match the argument's Constructors.
492      * <pre>
493      * // Example...
494      * baja.strictAllArgs([arg1, arg2], [String, Number]);
495      * </pre>
496      * Please note, the Constructors array can also hold a Type used for comparison 
497      * (in the form of either a String or Type).
498      *
499      * @private
500      *
501      * @see baja.strictArg
502      *
503      * @param {Array} args  an array of arguments being tested.
504      * @param {Array} ctors  an array of Constructors being used to test against the arguments.
505      */
506     baja.strictAllArgs = function (args, ctors) {
507       if (ctors.length !== args.length) {
508         throw new Error("Invalid number of arguments. Expected " + ctors.length +
509             ", received " + args.length + " instead.");
510       }
511       
512       var i;
513       for (i = 0; i < args.length; i++) {
514         baja.strictArg(args[i], ctors[i]);
515       }
516     };
517               
518     /**
519      * Define a default value for possibly undefined variables.
520      *
521      * @private
522      *
523      * @param val the value to be tested.
524      * @param defVal the default value to be returned if the value is undefined.
525      * @returns the default value if the value is undefined.
526      */
527     baja.def = function (val, defVal) {
528       return val === undefined ? defVal : val;
529     };
530     
531     // Create local for improved minification
532     var strictArg = baja.strictArg,
533         strictAllArgs = baja.strictAllArgs,
534         bajaDef = baja.def;
535   
536     /**
537      * A general utility method that tries to prevents a function from running
538      * more often than the specified interval. Due to the vagaries of Javascript 
539      * time management you may see the occasional drop of a few milliseconds but 
540      * on the whole execution of the given function should not be permitted more
541      * than once in the specified interval.
542      * 
543      * <p>If the function is called before the interval is up, it will still be
544      * executed once the remainder of the interval elapses - it will not be
545      * cancelled.
546      * 
547      * <p>However, if the function is called <i>multiple times</i> before the 
548      * interval has elapsed, only the <i>last</i>function call will execute -
549      * calls prior to that one will be cancelled.
550      * 
551      * <p>The function passed in will be run <i>asynchronously</i> via
552      * <code>setTimeout()</code>, so if you wish to process the results of the
553      * function call, you must do so via callback.
554      *  
555      * @param {Function} func a function to be executed - must take no parameters
556      * and may optionally return a value.
557      * @param {Object} obj an object literal with additional parameters -
558      * also, passing a Number in as the second parameter will use that as
559      * <code>obj.interval</code> and the other params will be ignored/false.
560      * @param {Number} [obj.interval] the interval in milliseconds - the 
561      * function will be prevented from executing more often than this (if 
562      * omitted, 100ms).
563      * @param {Function} [obj.ok] an optional callback function that will
564      * handle the return value from the throttled function.
565      * @param {Function} [obj.fail] an optional fail callback function that 
566      * will be called if the function throws any exceptions.
567      * @param {Boolean} [obj.drop] if drop is set to true, then additional
568      * function invocations that occur before the interval is up will be simply
569      * ignored rather than queued to execute at the end of the interval period.
570      * @returns {Function} a throttled function that can be executed the same
571      * way as the original function.
572      */
573     baja.throttle = function (func, obj) {
574       obj = baja.objectify(obj, 'interval');
575   
576       var interval = obj.interval || 100,
577       ok = obj.ok || baja.ok,
578       fail = obj.fail || baja.fail,
579       drop = obj.drop;
580   
581       var mustWaitUntil = 0,
582       waitingForTicket = baja.clock.expiredTicket;
583   
584       function getTimeToWait() {
585         var now = baja.clock.ticks(),
586         timeToWait = mustWaitUntil - now;
587         if (timeToWait <= 0) {
588           mustWaitUntil = now + interval;
589         }
590         return timeToWait;
591       }
592   
593       return function () {
594         var that = this,
595         args = Array.prototype.slice.call(arguments),
596         funcToRun,
597         timeToWait = getTimeToWait();
598   
599         if ((timeToWait > 0) && drop) {
600           return;
601         }
602   
603         funcToRun = function () {
604           try {
605             ok(func.apply(that, args));
606           }
607           catch (err) {
608             fail(err);
609           }
610           finally {
611             // once the function is done executing, always reset the next time it
612             // will be allowed to execute again
613             getTimeToWait();
614           }
615         };
616   
617         // don't allow function executions to pile up - only have one waiting at a time
618         waitingForTicket.cancel();
619   
620         if (timeToWait <= 0) {
621           baja.runAsync(funcToRun);
622         } 
623         else if (!drop) {
624           waitingForTicket = baja.clock.schedule(funcToRun, timeToWait);
625         }
626       };
627     };
628   
629     /**
630      * Test an Object to see if it's a Baja Object and has the 'getType' function.
631      * <p>
632      * Please note: this test excludes objects that may extend baja.Slot.
633      *
634      * @param {Object} obj  the Object to be tested.
635      * @param {String} [type] the type or (String type specification - module:typeName) to test object against. 
636      *                            Please note, Type.is(...) is used and not equals.
637      *
638      * @returns {Boolean} true if the given Object is a proper BajaScript Object
639      */
640     baja.hasType = function (obj, type) {
641       if (obj === null || obj === undefined) {
642         return false;
643       }
644       if (obj && typeof obj === "object" && obj instanceof baja.Slot) {
645         return false;
646       }
647   
648       if (typeof obj.getType === "function") {
649         if (type) {
650           return obj.getType().is(type);
651         }
652         return true;
653       }
654       else {
655         return false;
656       }
657     };
658   
659     /**
660      * This is a conveniance method used for working with functions that take 
661      * an Object Literal as an argument.
662      * <p>
663      * This method always ensures an Object is returned so its properties can be
664      * further validated.
665      * <p>
666      * In some cases, the function may take an object literal or a single argument.
667      * If the function can take a single argument that isn't an Object literal, then 
668      * the 'propName' can be specified. If this is specified, an Object is created and
669      * the value is assigned to the Object with the specified property name.
670      * <pre>
671      *   // For example, this function can take an Object literal or a Number
672      *   function foo(obj) {
673      *     obj = baja.objectify(obj, "num");
674      *     
675      *     // 'obj' will always point to an Object. We can now test and work with 'num'...
676      *     baja.strictArg(obj.num, Number);
677      *   }
678      *
679      *   // Both method invocations are valid...
680      *   foo(23.4); 
681      *   foo({num: 23.4});
682      * </pre>
683      *
684      * @private
685      *
686      * @param obj  the Object literal or a value to be added onto an Object if propName is specified.
687      * @param {String} [propName]  if the object isn't an Object, an Object is created and the value is assigned
688      *                             to the object with this property name.
689      *
690      * @returns {Object} an Object
691      */
692     baja.objectify = function (obj, propName) {
693       if (!(obj === undefined || obj === null)) {
694         if (obj.constructor === Object) {
695           return obj;
696         }
697         else if (typeof propName === "string") {
698           var o = {};
699           o[propName] = obj;
700           return o;
701         }
702       }
703       return {};
704     };
705     
706     /**
707      * Returns a Lexicon Object for a given module. The locale will be whatever the current
708      * user is set too.
709      * <p>
710      * Please note, if BajaScript has Web Storage enabled, the Lexicon will be permanently cached.
711      * <p>
712      * If the Lexicon for the given module isn't loaded then a network call will be made.
713      * <pre>
714      *   // Get a value from a Lexicon. A synchronous network call will be made if the Lexicon
715      *   // isn't available locally.
716      *   baja.lex("bajaui").get("dialog.ok");
717      *
718      *   // Get a value from a Lexicon. An asynchronous network call will be made if the Lexicon
719      *   // isn't available locally.
720      *   baja.lex({
721      *     module: "bajaui",
722      *     ok: function (lex) {
723      *       lex.get("dialog.ok");
724      *     }
725      *   });
726      * </pre>
727      *
728      * @see Lexicon
729      *
730      * @returns {Object}
731      */
732     baja.lex = function (obj) {
733       obj = baja.objectify(obj, "module");
734       
735       var module = obj.module,
736           cb = new baja.comm.Callback(obj.ok, obj.fail, obj.batch),
737           lx;
738   
739       // If already loaded then return the lexicon
740       if (bsLexicons.hasOwnProperty(module)) {
741         lx = bsLexicons[module];
742         cb.ok(lx);
743         return lx;
744       }
745   
746       // If the original ok handler wasn't defined then make a synchronous network call
747       var async = typeof obj.ok === "function";
748   
749       // Make a network call to get the lexicon
750       try {
751   
752         // Add intermediate callback
753         cb.addOk(function (ok, fail, lexData) {      
754           // If available, store this in web storage
755           if (bsRegStorage) {
756             bsRegStorage.lexicons[module] = lexData;
757           }
758   
759           // Create the Lexicon and add it to our cached list
760           lx = new Lexicon(module, lexData);
761           bsLexicons[module] = lx;
762           ok(lx);
763         });
764   
765         baja.comm.lex(module, cb, async);
766       }
767       catch (err) {
768         cb.fail(err);
769       }
770   
771       return lx;
772     };
773     
774     /**
775      * Run the specified Function asynchronously.
776      *
777      * @param {Function} fn  the Function to run asynchronously.
778      */
779     baja.runAsync = function (fn) {
780       baja.clock.schedule(fn, 0);
781     };
782   
783     (function iterate() {
784       function iterateArray(arr, start, end, func) {
785         var i, result;
786   
787         for (i = start; i < end; i++) {
788           result = func(arr[i], i);
789           if (result !== undefined) {
790             return result;
791           }
792         }
793       }
794   
795       function iterateCustomNext(obj, func, nextFunc) {
796         while (obj) {
797           var result = func(obj);
798           if (result !== undefined) {
799             return result;
800           } else {
801             obj = nextFunc(obj);
802           }
803         }
804       }
805   
806       function iterateJsProperties(obj, func) {
807         var name, result;
808         for (name in obj) {
809           if (obj.hasOwnProperty(name)) {
810             result = func(obj[name], name);
811             if (result !== undefined) {
812               return result;
813             }
814           }
815         }
816       }
817   
818       function iterateByIndex(start, end, func) {
819         var i, result;
820   
821         for (i = start; i < end; i++) {
822           result = func(i);
823           if (result !== undefined) {
824             return result;
825           }
826         }
827       }
828   
829       /**
830        * A iteration general utility method that performs the given function on every JavaScript 
831        * property in the given cursor or Javascript array. This function can be called 
832        * with a variety of parameter configurations.
833        * <pre>
834        * baja.iterate(array, function (arrayElement, arrayIndex))
835        * 
836        * baja.iterate(array, startIndex, function (arrayElement, arrayIndex))
837        * 
838        * baja.iterate(array, startIndex, endIndex, function (arrayElement, arrayIndex))
839        * 
840        * baja.iterate(numberOfTimesToIterate, function (index))
841        * 
842        * baja.iterate(iterationStartIndex, iterationEndIndex, function (index))
843        *
844        * baja.iterate(object, function (objectJsProperty, objectJsPropertyName))
845        * 
846        * baja.iterate(object, function doIterate(object), function getNext(object))
847        * </pre>
848        * 
849        * <p><code>iterate()</code> is compatible with arrays, but <i>not</i> with
850        * <code>arguments</code> objects - pass in
851        * <code>Array.prototype.slice.call(arguments)</code> instead.
852        *
853        * <p>In the last case with the <code>doIterate</code> and 
854        * <code>getNext</code> functions, <code>doIterate</code> performs the
855        * iterative action on the object, and <code>getNext</code> returns the
856        * next object to iterate (this will be passed directly back into
857        * <code>doIterate</code>). This is handy for walking up prototype chains,
858        * supertype chains, component hierarchies, etc.
859        * 
860        * <p>In all cases, if the function being executed ever returns a value
861        * other than <code>undefined</code>, iteration will be stopped at that 
862        * point and <code>iterate()</code> will return that value.
863        * 
864        * <p>For invocations of <code>iterate()</code> that include start or end
865        * indexes, note that start indexes are inclusive and end indexes are
866        * exclusive (e.g. <code>iterate(2, 5, function (i) { baja.outln(i); })</code>
867        * would print <code>2,3,4</code>).
868        * 
869        * @returns any non-undefined value that's returned from any function 
870        * or Cursor.
871        */
872       baja.iterate = function () {
873         var args = arguments,
874         arg0 = args[0],
875         arg1 = args[1],
876         arg2 = args[2],
877         arg3 = args[3],
878         typeString = Object.prototype.toString.call(arg0);
879   
880         if (arg0 === undefined || arg0 === null) {
881           throw new Error("undefined passed to baja.iterate()");
882         }
883   
884         if (typeString === '[object Array]') {
885           switch (arguments.length) {
886           case 2: //iterate(array, function (arrayElement, arrayIndex))
887             return iterateArray(arg0, 0, arg0.length, arg1);
888   
889           case 3: //iterate(array, startIndex, function (arrayElement, arrayIndex))
890             return iterateArray(arg0, arg1, arg0.length, arg2);
891   
892           case 4: //iterate(array, startIndex, endIndex, function (arrayElement, arrayIndex))
893             return iterateArray(arg0, arg1, arg2, arg3);
894           } 
895         } 
896         else if (typeString === '[object Object]') {   
897           if (arg0 instanceof baja.Cursor) {
898             //bajaScriptCursor.each(function ())
899             return arg0.each(arg1);
900           }      
901           else if (typeof arg2 === 'function') {
902             //iterate(object, function doIterate(object), function getNext(object))
903             return iterateCustomNext(arg0, arg1, arg2);
904           } 
905           else {
906             //iterate(object, function (objectJsProperty, objectJsPropertyName))
907             return iterateJsProperties(arg0, arg1);
908           }
909         }   
910         else if (typeString === '[object Number]') {
911           if (typeof arg1 === 'number') {
912             //iterate(iterationStartIndex, iterationEndIndex, function (index))
913             return iterateByIndex(arg0, arg1, arg2);
914   
915           } 
916           else {
917             //iterate(numberOfTimesToIterate, function (index))
918             return iterateByIndex(0, arg0, arg1);
919           }
920         } else if (typeString === '[object Arguments]') {
921           throw new Error("Arguments object not iterable (pass through " + 
922           "Array.prototype.slice first)");
923         }
924   
925         throw new Error(arg0 + " is not iterable");
926       };
927     }());
928         
929     ////////////////////////////////////////////////////////////////
930     // Ordered Map and Cursor - used as basis for ComplexSlotMap
931     //////////////////////////////////////////////////////////////// 
932     
933     var notImplementedStr = "Not implemented";
934     
935     /** 
936      * @class A generic cursor used for iteration.
937      */
938     baja.Cursor = function () {
939     };
940         
941     /**
942      * Return the current item.
943      * 
944      * @returns the cursor value (null if none available).
945      */
946     baja.Cursor.prototype.get = function () {
947       throw new Error(notImplementedStr);
948     };
949     
950     /**
951      * Iterate through the Cursor and call 'each' on every item.
952      * 
953      * @param {Function} func function called on every iteration with the 'value' being used as an argument.
954      */
955     baja.Cursor.prototype.each = function (func) {
956       throw new Error(notImplementedStr);
957     };
958     
959     /** 
960      * @class An asynchonrous generic cursor used for iteration.
961      * <p>
962      * An Async Cursor may fetch its results asynchronously (i.e. across a network).
963      *
964      * @extends baja.Cursor
965      */
966     baja.AsyncCursor = function () {
967     }.$extend(baja.Cursor);
968         
969     /** 
970      * @class A generic Synchronous cursor used for iteration.
971      *
972      * @extends baja.Cursor
973      */
974     baja.SyncCursor = function () {
975     }.$extend(baja.Cursor);
976     
977     /**
978      * Advance cursor and return true if successful.
979      * 
980      * @returns {Boolean}
981      */
982     baja.SyncCursor.prototype.next = function () {
983       throw new Error(notImplementedStr);
984     };
985         
986     /** 
987      * @class A filtered cursor used for iteration.
988      * <p>
989      * This Cursor is a generic Cursor used for iteration in a {@link baja.OrderedMap}.
990      *
991      * @see baja.OrderedMap
992      *
993      * @name baja.FilterCursor
994      * @extends baja.SyncCursor
995      */
996     baja.FilterCursor = function (context, orderedMap) {
997       baja.FilterCursor.$super.apply(this, arguments);
998       this.$context = context;
999       this.$orderedMap = orderedMap;
1000       this.$keys = orderedMap && orderedMap.getKeys();
1001       this.$filter = null;
1002       this.$index = -1;
1003     }.$extend(baja.SyncCursor);
1004 
1005     function filterNext(cursor) {
1006       if (cursor.$index < cursor.$keys.length) {
1007         ++cursor.$index;
1008         return cursor.$index !== cursor.$keys.length;
1009       }
1010       else {
1011         return false;
1012       }
1013     }
1014     
1015     /**
1016      * Advance cursor and return true if successful.
1017      *
1018      * @function
1019      * 
1020      * @returns {Boolean}
1021      */
1022     baja.FilterCursor.prototype.next = function () {
1023       var that = this;
1024     
1025       if (!that.$filter) {
1026         return filterNext(this);
1027       }
1028       else {
1029         // If a Constructor has been passed in then keep iterating
1030         // until we find a matching element
1031         do {
1032           if (!filterNext(this)) {
1033             return false;
1034           }
1035         }
1036         while (!that.$filter.call(that.$context, that.get()));
1037         return true;
1038       }
1039     };
1040 
1041     /**
1042      * Return the current item. If this is a SlotCursor, this will
1043      * return a Slot.
1044      * 
1045      * @returns the cursor value (null if none available).
1046      */
1047     baja.FilterCursor.prototype.get = function () {
1048       var x = this.$index,
1049           keys = this.$keys;
1050       
1051       if (x === -1 || x >= keys.length) {
1052         return null;
1053       }
1054       else {
1055         return this.$orderedMap.get(keys[x]);
1056       }
1057     };    
1058 
1059     /**
1060      * Return the current key.
1061      * <p>
1062      * This is a private method and shouldn't be used by non-Tridium developers.
1063      *
1064      * @private
1065      * 
1066      * @returns {String} the cursor key (null if none available).
1067      */
1068     baja.FilterCursor.prototype.getKey = function () {
1069       if (this.$index === -1) {
1070         return null;
1071       }
1072       else {
1073         return this.$keys[this.$index];
1074       }
1075     }; 
1076 
1077     /**
1078      * Return the current index
1079      * <p>
1080      * This is a private method and shouldn't be used by non-Tridium developers.
1081      *
1082      * @private
1083      * 
1084      * @returns {Number} the cursor index (null if none available).
1085      */
1086     baja.FilterCursor.prototype.getIndex = function () {
1087       return this.$index;
1088     }; 
1089 
1090     /**
1091      * Iterate through the Cursor and call 'each' on every item.
1092      * <p>
1093      * When the function is called, 'this' refers to the 'context' that
1094      * was passed in when the Cursor was created.
1095      * 
1096      * @param {Function} func function called on every iteration with the 'value' being used as an argument.
1097      *                        If this is a Slot Cursor the 'value' will be a Slot.
1098      */
1099     baja.FilterCursor.prototype.each = function (func) {
1100       strictArg(func, Function);
1101 
1102       var result;
1103       while (this.next()) {
1104         result = func.call(this.$context, this.get(), this.getIndex());
1105 
1106         if (result) {
1107           return result;
1108         }
1109       }
1110     };
1111         
1112     /**
1113      * Return true if the Cursor is completely empty (regardless of iterative state).
1114      *
1115      * @returns {Boolean}
1116      */
1117     baja.FilterCursor.prototype.isEmpty = function () {
1118       // Note the old cursor index
1119       var oldX = this.$index;
1120       
1121       // Set the cursor back to the start
1122       this.$index = -1;
1123       
1124       // See if we have any valid entries from the start
1125       var res = this.next(); 
1126       
1127       // Restore the old cursor index
1128       this.$index = oldX;
1129       
1130       // Return the result of our search
1131       return !res;
1132     };
1133         
1134     /**
1135      * Return an array of the cursor results (regardless of iterative state).
1136      * If this is a Slot Cursor, this will be an array of Slots.
1137      *
1138      * @returns {Array}
1139      */
1140     baja.FilterCursor.prototype.toArray = function () {
1141       // Note the old cursor index
1142       var oldX = this.$index,
1143           a = [];
1144           
1145       this.$index = -1;
1146       
1147       // Iterate through and fill up the array
1148       while (this.next()) {
1149         a.push(this.get());
1150       }
1151       
1152       // Restore the old cursor index
1153       this.$index = oldX;
1154       
1155       return a;
1156     };
1157     
1158     /**
1159      * Return an Object Map of keys with their corresponding values. 
1160      * If this is a Slot Cursor, this will be a Map of Slot names with their 
1161      * corresponding Slots (regardless of iterative state).
1162      *
1163      * @returns {Object}
1164      */  
1165     baja.FilterCursor.prototype.toMap = function () {
1166       var slots = this.toArray(),
1167           map = {},
1168           s,
1169           i;
1170       
1171       for (i = 0; i < slots.length; ++i) {
1172         s = slots[i];
1173         map[s.getName()] = s;
1174       }
1175               
1176       return map;
1177     };
1178         
1179     /**
1180      * Return the size of the cursor (regardless of iterative state).
1181      *
1182      * @returns {Number}
1183      */
1184     baja.FilterCursor.prototype.getSize = function () {
1185       // Note the old cursor index
1186       var oldX = this.$index,
1187           count = 0;
1188       
1189       this.$index = -1;
1190       
1191       // Iterate through and fill up the array
1192       while (this.next()) {
1193         ++count;
1194       }
1195       
1196       // Restore the old cursor index
1197       this.$index = oldX;
1198       
1199       return count;
1200     };
1201     
1202     /**
1203      * Add a filter function to the Cursor.
1204      *
1205      * @param {Function} filter used to filter the results of the Cursor.
1206      *                          When invoked, the first argument will be the item to filter (i.e. a Slot).
1207      *                          This function must return a true value for the item to be kept.                 
1208      * @returns {baja.FilterCursor} itself.
1209      */
1210     baja.FilterCursor.prototype.filter = function (flt) {
1211       if (!this.$filter) {
1212         this.$filter = flt;
1213       }
1214       else {
1215         var oldFilter = this.$filter;
1216         
1217         // Merge the filter functions together
1218         this.$filter = function (val) {
1219           return oldFilter.call(this, val) && flt.call(this, val);
1220         };
1221       }
1222       return this;
1223     };
1224     
1225     /**
1226      * Return the first item in the cursor (regardless of iterative state).
1227      * <p>
1228      * If this is being used as a Slot Cursor, the Slot will be returned.
1229      *
1230      * @returns first item found in the Cursor (or null if nothing found).
1231      */
1232     baja.FilterCursor.prototype.first = function () {
1233       // Note the old cursor index
1234       var oldX = this.$index,
1235           val = null;
1236           
1237       this.$index = -1;
1238       
1239       // Iterate through and fill up the array
1240       if (this.next()) {
1241         val = this.get();
1242       }
1243        
1244       // Restore the old cursor index
1245       this.$index = oldX;
1246       
1247       return val;
1248     };
1249     
1250     /**
1251      * Return the last item in the cursor (regardless of iterative state).
1252      * <p>
1253      * If this is being used as a Slot Cursor, the Slot will be returned.
1254      *
1255      * @returns last item found in the Cursor (or null if nothing found).
1256      */
1257     baja.FilterCursor.prototype.last = function () {
1258       // Note the old cursor index
1259       var oldX = this.$index,
1260           val = null;
1261           
1262       this.$index = this.$keys.length - 2;
1263       
1264       // Iterate through and fill up the array
1265       if (this.next()) {
1266         val = this.get();
1267       }
1268        
1269       // Restore the old cursor index
1270       this.$index = oldX;
1271       
1272       return val;
1273     };     
1274 
1275     /**
1276      * @class Maintains an ordered list of key/value pairs.
1277      * <p>
1278      * This object forms the basis of a Complex's Slot Map.
1279      *
1280      * @name baja.OrderedMap
1281      * @extends BaseBajaObj
1282      * @private
1283      */
1284     baja.OrderedMap = function () {
1285       baja.OrderedMap.$super.apply(this, arguments);
1286       this.$map = {}; // Normal unordered Map
1287       this.$array = []; // Array for maintaining order of keys
1288     }.$extend(BaseBajaObj);
1289 
1290     /**
1291      * Assign the value to the Map with the given key.
1292      *
1293      * @private
1294      *
1295      * @param {String} key  the key used for the entry in the Map
1296      * @param val  the value used to store in the Map
1297      */
1298     baja.OrderedMap.prototype.put = function (key, val) {
1299       if (!this.$map.hasOwnProperty(key)) {
1300         this.$map[key] = val;
1301         this.$array.push(key);
1302       }
1303       else {
1304         this.$map[key] = val;
1305       }
1306     };
1307 
1308     /**
1309      * Remove a value from the map and return it.
1310      *
1311      * @private
1312      *
1313      * @param {String} key  the key used to remove the value.
1314      * @returns the value removed (return null if nothing is found to be removed).
1315      */   
1316     baja.OrderedMap.prototype.remove = function (key) {
1317       strictArg(key, String);
1318       var v, i;
1319       if (this.$map.hasOwnProperty(key)) {
1320         v = this.$map[key];
1321 
1322         // Remove the element from the Map
1323         delete this.$map[key];
1324 
1325         // Find and remove the key from the array
1326         for (i = 0; i < this.$array.length; ++i) {
1327           if (this.$array[i] === key) {
1328             this.$array.splice(i, 1);
1329             break;
1330           }
1331         }
1332 
1333         return v;
1334       }
1335       else {
1336         return null;
1337       }
1338     };  
1339 
1340     /**
1341      * Query the Map to see if it contains the key.
1342      *
1343      * @private
1344      * 
1345      * @param {String} key
1346      * @returns {Boolean} a boolean value indicating if the Map contains the key.
1347      */
1348     baja.OrderedMap.prototype.contains = function (key) {
1349       strictArg(key, String);
1350       return this.$map.hasOwnProperty(key);
1351     };
1352 
1353     /**
1354      * Return the value for the key.
1355      *
1356      * @private
1357      * 
1358      * @param {String} key
1359      * @returns  the value for the key (return null if key is not found in Map).
1360      */
1361     baja.OrderedMap.prototype.get = function (key) {
1362       if (this.$map.hasOwnProperty(key)) {
1363         return this.$map[key];
1364       }
1365       else {
1366         return null;
1367       }     
1368     };
1369 
1370     /**
1371      * Rename an entry.
1372      *   
1373      * @private
1374      *
1375      * @param {String} oldName  the name of the existing entry to be renamed.
1376      * @param {String} newName  the new name of the entry.
1377      * @returns {Boolean} true if the entry was successfully renamed.
1378      */
1379     baja.OrderedMap.prototype.rename = function (oldName, newName) { 
1380       strictArg(oldName, String);
1381       strictArg(newName, String);
1382 
1383       if (!this.contains(oldName)) {
1384         return false;
1385       }
1386       if (this.contains(newName)) {
1387         return false;
1388       }
1389 
1390       // Get existing entry
1391       var entry = this.$map[oldName];
1392       delete this.$map[oldName];
1393 
1394       // Create new entry
1395       this.$map[newName] = entry;
1396 
1397       // Update array
1398       var i;
1399       for (i = 0; i < this.$array.length; ++i) {
1400         if (this.$array[i] === oldName) {
1401           this.$array[i] = newName;
1402           break;
1403         }
1404       }
1405 
1406       return true;
1407     };
1408 
1409     /**
1410      * Return the key's index.
1411      *
1412      * @private
1413      *
1414      * @param {String} key
1415      * @returns {Number} the index for the key (return -1 if key is not found in Map).
1416      */
1417     baja.OrderedMap.prototype.getIndex = function (key) {
1418       strictArg(key, String);
1419       if (this.$map.hasOwnProperty(key)) {
1420         var i;
1421         for (i = 0; i < this.$array.length; ++i) {
1422           if (this.$array[i] === key) {
1423             return i;
1424           }
1425         }  
1426       }
1427       return -1;    
1428     };
1429 
1430     /**
1431      * Return the value for the index.
1432      *
1433      * @private
1434      *
1435      * @param {Number} index
1436      * @returns the value for the index (return null if index is not found in Map).
1437      */
1438     baja.OrderedMap.prototype.getFromIndex = function (index) {
1439       strictArg(index, Number);
1440       var key = this.$array[index];
1441       if (typeof key === "string") {
1442         return this.$map[key];
1443       }
1444       return null;   
1445     };
1446 
1447     /**
1448      * Return an ordered array of keys for iteration.
1449      * <p>
1450      * Please note, a copy of the internal array will be returned.
1451      *
1452      * @private
1453      *
1454      * @returns {Array} an array of keys that can be used for iteration.
1455      */
1456     baja.OrderedMap.prototype.getKeys = function () {
1457       // Return a copy of the array
1458       return this.$array.slice(0);
1459     };
1460 
1461     /**
1462      * Return the size of the Map.
1463      * 
1464      * @private
1465      *
1466      * @returns {Number} the size of the Map.
1467      */
1468     baja.OrderedMap.prototype.getSize = function () {
1469       return this.$array.length;
1470     };
1471 
1472     /**
1473      * Sort the Map.
1474      * 
1475      * @private
1476      *
1477      * @see Array#sort
1478      *
1479      * @param {Function} sortFunc  Function used to sort the map. This definition of the Function
1480      *                             should be the same as the one passed into a JavaScript Array's 
1481      *                             sort method.
1482      */
1483     baja.OrderedMap.prototype.sort = function (sortFunc) {
1484       strictArg(sortFunc, Function);
1485       this.$array.sort(sortFunc);
1486     };
1487 
1488     /**
1489      * Return a Cursor used for iteration.
1490      * 
1491      * @private
1492      *
1493      * @param context used as 'this' in iteration operations
1494      * @param {Function} the Constructor of the Cursor to use for the Map.
1495      *     
1496      * @returns {Cursor} Cursor used for iteration 
1497      */
1498     baja.OrderedMap.prototype.getCursor = function (context, Cursor) {
1499       Cursor = Cursor || baja.FilterCursor;
1500       return new Cursor(context, this);
1501     }; 
1502   }());
1503     
1504   // Create local for improved minification
1505   var strictArg = baja.strictArg,
1506       strictAllArgs = baja.strictAllArgs,
1507       bajaDef = baja.def,
1508       objectify = baja.objectify;
1509         
1510   ////////////////////////////////////////////////////////////////
1511   // Clock
1512   //////////////////////////////////////////////////////////////// 
1513   
1514   (function bajaClockNamespace() {
1515     
1516     /**
1517      * @namespace Baja Clock methods used for scheduling.
1518      */
1519     baja.clock = new BaseBajaObj();
1520     
1521     /**
1522      * @class Clock Ticket used when scheduling function calls in BajaScript.
1523      * <p>
1524      * This Constructor shouldn't be invoked directly.
1525      * 
1526      * @see baja.clock.schedule
1527      *
1528      * @name Ticket
1529      * @extends BaseBajaObj
1530      * @inner
1531      * @public
1532      */   
1533     var Ticket = function () {
1534       this.$id = -1;
1535     }.$extend(BaseBajaObj);
1536 
1537     /**
1538      * Cancel the currently scheduled Ticket.
1539      */
1540     Ticket.prototype.cancel = function () {
1541       clearTimeout(this.$id);
1542       delete bsClockTimeouts[this.$id.toString()]; 
1543       this.$id = -1;
1544     };
1545 
1546     /**
1547      * Test for ticket expiration.
1548      *
1549      * @returns {Boolean} if the scheduled Ticket is currently expired.
1550      */
1551     Ticket.prototype.isExpired = function () {
1552       return this.$id === -1;
1553     };
1554 
1555     /**
1556      * @class Clock Ticket used when scheduling periodic events in Niagara.
1557      * <p>
1558      * This Constructor shouldn't be invoked directly.
1559      *
1560      * @name IntervalTicket
1561      * @extends Ticket
1562      * @inner
1563      * @public
1564      */
1565     var IntervalTicket = function () {
1566       IntervalTicket.$super.apply(this, arguments);
1567     }.$extend(Ticket);
1568 
1569     /**
1570      * Cancel the currently scheduled Ticket.
1571      */
1572     IntervalTicket.prototype.cancel = function () {
1573       clearInterval(this.$id);
1574       delete bsClockIntervals[this.$id.toString()];  
1575       this.$id = -1;
1576     };
1577 
1578     /** 
1579      * Returns the number of Clock ticks on the system.
1580      * <p>
1581      * This method is typically used for profiling.
1582      *
1583      * @returns {Number} the number of Clock ticks on the system.
1584      */
1585     baja.clock.ticks = function () {
1586       return new Date().valueOf();
1587     };
1588     
1589     /**
1590      * An expired Ticket.
1591      */
1592     baja.clock.expiredTicket = new Ticket();
1593 
1594     /**
1595      * Schedule a one off timer.
1596      * <p>
1597      * When the callback is invoked, 'this' will refer to the Ticket instance.
1598      * <p>
1599      * If any variables need to be passed into this function then it's best to do this via 
1600      * JavaScript closures.
1601      *
1602      * @param {Function} func  the function to be invoked once the specified time has elapsed.
1603      * @param {Number} time  the number of milliseconds before the event is run.
1604      * @returns {Ticket} a new Ticket for the scheduled event.
1605      */
1606     baja.clock.schedule = function (func, time) {
1607       strictAllArgs([func, time], [Function, Number]);
1608 
1609       // If BajaScript has fully stopped then don't schedule anything else...
1610       if (baja.isStopped()) {
1611         return this.expiredTicket;
1612       }
1613 
1614       // Create ticket before timer so we get closure
1615       var t = new Ticket();
1616       t.$id = setTimeout(function () {     
1617         delete bsClockTimeouts[t.$id.toString()]; 
1618         t.$id = -1;    
1619         func.call(t);
1620       }, time);
1621 
1622       // Register the ticket so we can keep track of it
1623       bsClockTimeouts[t.$id.toString()] = t;
1624 
1625       return t;
1626     };
1627 
1628     /**
1629      * Schedule a periodic timer.
1630      * <p>
1631      * When the callback is invoked, 'this' will refer to the Ticket instance.
1632      * <p>
1633      * If any variables need to be passed into this function then it's best to do this via 
1634      * JavaScript closures.
1635      *
1636      * @param {Function} func  the function to be invoked each time the specified time has elapsed.
1637      * @param {Number} time  the number of milliseconds before the event is run.
1638      *
1639      * @returns {IntervalTicket} a new Ticket for the scheduled event.
1640      */
1641     baja.clock.schedulePeriodically = function (func, time) {
1642       strictAllArgs([func, time], [Function, Number]);
1643 
1644       // If BajaScript has fully stopped then don't schedule anything else...
1645       if (baja.isStopped()) {
1646         return this.expiredTicket;
1647       }
1648 
1649       // Create ticket before timer so we get closure
1650       var t = new IntervalTicket();
1651       t.$id = setInterval(function () {
1652         func.call(t);
1653       }, time);
1654 
1655       // Keep track of the ticket internally
1656       bsClockIntervals[t.$id.toString()] = t;
1657 
1658       return t;
1659     };
1660   }());
1661   
1662   ////////////////////////////////////////////////////////////////
1663   // BajsScript Start and Stop
1664   //////////////////////////////////////////////////////////////// 
1665   
1666   (function startAndStop() {  
1667     var bsStarted = false,                 // BajaScript started flag
1668         bsStopped = false,                 // BajaScript stopped flag
1669         bsStopping = false,                // BajaScript stopping flag
1670         bsStartedCallbacks = null,         // Started callbacks
1671         bsPreStopCallbacks = null,         // Stopped callbacks
1672         bsUserName = "",                   // BajaScript user name for current session
1673         bsLang = "",                       // BajaScript language for user
1674         bsUserHome = "station:|slot:/",    // BajaScript user home
1675         bsTimeFormat = "",                 // BajaScript user time format pattern
1676         bsLogClientErrorsInServer = false, // BajaScript log client errors in the Server
1677         bsPreLoadRegStorage = null;        // The pre-loaded BajaScript Registry storage
1678     
1679     /**
1680      * Return true if BajaScript has started.
1681      *
1682      * @returns {Boolean} started
1683      */
1684     baja.isStarted = function () {
1685       return bsStarted;
1686     };
1687   
1688     /**
1689      * Return true if BajaScript has stopped.
1690      *
1691      * @returns {Boolean} stopped
1692      */
1693     baja.isStopped = function () {
1694       return bsStopped;
1695     };
1696     
1697     /**
1698      * Return true if BajaScript has stopped or is in the process of stopping.
1699      *
1700      * @returns {Boolean} stopping
1701      */
1702     baja.isStopping = function () {
1703       return bsStopping;
1704     };
1705               
1706     /**
1707      * Add a function to be invoked once BajaScript has started.
1708      * <p>
1709      * If BajaScript has already started, the function will be invoked immediately.
1710      *
1711      * @param {Function} func invoked once BajaScript has started.
1712      */
1713     baja.started = function (func) {
1714       strictArg(func, Function);
1715   
1716       // If we're already started then return immediately
1717       if (bsStarted) {
1718         try {
1719           func.call(baja);
1720         }
1721         catch (err) {
1722           baja.error(err);
1723         }
1724         return;
1725       }  
1726       
1727       // If not started yet then add the function to a callback list
1728       if (!bsStartedCallbacks) {
1729         bsStartedCallbacks = [];
1730       }
1731       
1732       bsStartedCallbacks.push(func);
1733     };
1734   
1735     /**
1736      * Add a function to be invoked just before BajaScript is stopped.
1737      * <p>
1738      * If BajaScript has already stopped, the function will be invoked immediately.
1739      *
1740      * @param {Function} func
1741      */
1742     baja.preStop = function (func) {
1743       strictArg(func, Function);
1744   
1745       // If we're already started then return immediately
1746       if (bsStopped) {
1747         try {
1748           func.call(baja);
1749         }
1750         catch (err) {
1751           baja.error(err);
1752         }
1753         return;
1754       }
1755   
1756       // If not started yet then add the function to a callback list
1757       if (!bsPreStopCallbacks) {
1758         bsPreStopCallbacks = [];
1759       }
1760       bsPreStopCallbacks.push(func);
1761     };
1762   
1763     /**
1764      * Start BajaScript.
1765      * <p>
1766      * This must be called to start BajaScript. This will make a network call to create a Session
1767      * that starts BajaScript. It's recommended to call this as soon as possible.
1768      * <p>
1769      * This method takes a started function or an Object Literal for the method's arguments...
1770      * <pre>
1771      *   baja.start(function () {
1772      *     // Called once BajaScript has started.
1773      *   });
1774      *
1775      *   //...or this can be invoked via an Object Literal...
1776      *
1777      *   baja.start({
1778      *     started: function () {
1779      *       // Called when BajaScript has started. We're ready to rock and roll at this point!
1780      *     },
1781      *     commFail: function () {
1782      *       // Called when the BajaScript communcations engine completely fails
1783      *     },
1784      *     typeSpecs: ["control:BooleanWritable", "control:NumericWritable"] // Types and Contracts we want imported 
1785      *                                                                       // upfront before our Web App loads
1786      *   });
1787      * </pre>
1788      *
1789      * @see baja.started
1790      * @see baja.save
1791      * @see baja.preStop
1792      * @see baja.stop
1793      *
1794      * @param {Object|Function} [obj]  the Object Literal for the method's arguments or the function invoke after the comms have started.
1795      * @param {Function} [obj.started] function to invoke after the comms have started and (if running in a browser) the DOM is ready.
1796      * @param {Function} [obj.commFail] function to invoke if the comms fail.
1797      * @param {Array} [obj.typeSpecs] an array of type specs (moduleName:typeName) to import from the Server.
1798      *                                Please note, this may not be needed if the Type and Contract information
1799      *                                has been cached by web storage.
1800      * @param {Boolean} [obj.navFile] if true, this will load the nav file for the user on start up. By default, this is false.
1801      */ 
1802     baja.start = function (obj) {
1803       
1804       // Initialize Comms Engine
1805       obj = objectify(obj, "started");
1806   
1807       var started = obj.started;
1808       obj.started = function () {
1809         // Signal that we have started
1810         bsStarted = true;
1811   
1812         // Call started callbacks
1813         if (bsStartedCallbacks) {
1814           var i;
1815           for (i = 0; i < bsStartedCallbacks.length; ++i) {
1816             try {
1817               bsStartedCallbacks[i].call(baja);
1818             }
1819             catch (err) {
1820               baja.error(err);
1821             }
1822           }
1823           bsStartedCallbacks = null;
1824         }
1825   
1826         // Invoke original started function
1827         if (typeof started === "function") {
1828           try {
1829             started();
1830           }
1831           catch (err2) {
1832             baja.error(err2);
1833           }
1834         }
1835       };
1836        
1837       baja.comm.start(obj);
1838   
1839       // Registry the common type library
1840       baja.registry.register(JSON.parse(baja.$ctypes));
1841       delete baja.$ctypes;
1842       
1843       // Load Web Storage while the BajaScript comms engine starts.
1844       bsPreLoadRegStorage = baja.registry.loadFromStorage();    
1845       
1846       // If a pre-start function was passed in then invoke that here.
1847       if (typeof obj.preStart === "function") {
1848         obj.preStart();
1849       }
1850     };
1851     
1852     /**
1853      * Save BajaScript's Registry Storage. This is automatically called when
1854      * BajaScript stops.
1855      */
1856     baja.save = function () {
1857       // If available, save registry information to storage
1858       if (bsRegStorage) {
1859         baja.registry.saveToStorage(bsRegStorage);
1860       }
1861     };
1862   
1863     /**
1864      * Stop BajaScript.
1865      * <p>
1866      * This method should be called when the page running BajaScript is unloaded. This will
1867      * make a network call to stop BajaScript's session.
1868      * <p>
1869      * This method takes a stopped function or an Object Literal for the method's arguments...
1870      * <pre>
1871      *   baja.stop(function () {
1872      *     // Called once stop has completed
1873      *   });
1874      *   
1875      *   //...or this can be invoked via an Object Literal...
1876      *   
1877      *   baja.stop({
1878      *     stopped: function () {
1879      *       // Called once stop has completed 
1880      *     }
1881      *   });
1882      * </pre>
1883      *
1884      * @see baja.start
1885      * @see baja.started
1886      * @see baja.preStop
1887      *
1888      * @param {Object|Function} [obj] the Object Literal for the method's arguments or a function
1889      *                                to be called once BajaScript has stopped.
1890      * @param {Function} [obj.stopped] called once BajaScript has stopped.
1891      * @param {Function} [obj.preStop] called just before BajaScript has stopped.
1892      */
1893     baja.stop = function (obj) {
1894       try {
1895         // Don't allow stop to happen twice
1896         if (bsStopped) {
1897           return;
1898         }
1899         
1900         // Flag up that we're in the middle of stopping BajaScript...
1901         bsStopping = true;
1902         
1903         baja.save();
1904       
1905         obj = objectify(obj, "stopped");
1906   
1907         if (typeof obj.preStop === "function") {
1908           baja.preStop(obj.preStop);
1909         }
1910   
1911         // Call preStop callbacks
1912         if (bsPreStopCallbacks) {
1913           var i;
1914           for (i = 0; i < bsPreStopCallbacks.length; ++i) {
1915             try {
1916               bsPreStopCallbacks[i].call(baja);
1917             }
1918             catch (err) {
1919               baja.error(err);
1920             }
1921           }
1922           bsPreStopCallbacks = null;
1923         }
1924   
1925         // Stop all registered timers  
1926         var id;
1927         for (id in bsClockTimeouts) {
1928           if (bsClockTimeouts.hasOwnProperty(id)) {
1929             bsClockTimeouts[id].cancel();
1930           }
1931         }
1932   
1933         for (id in bsClockIntervals) {
1934           if (bsClockIntervals.hasOwnProperty(id)) {
1935             bsClockIntervals[id].cancel();
1936           }
1937         }
1938   
1939         // These should be empty but we'll recreate them anyway
1940         bsClockTimeouts = {};
1941         bsClockIntervals = {};
1942     
1943         // Stop Comms Engine
1944         baja.comm.stop(obj);
1945       }
1946       finally {
1947         // Signal that BajaScript has fully stopped
1948         bsStopped = true;
1949       }
1950     };
1951     
1952     /**
1953      * Returns the user name the user is currently logged in with.
1954      *
1955      * @returns {String} the user name.
1956      */
1957     baja.getUserName = function () {
1958       return bsUserName;
1959     };
1960   
1961     /**
1962      * Returns language code for the user.
1963      *
1964      * @returns {String} the language character code.
1965      */
1966     baja.getLanguage = function () {
1967       return bsLang;
1968     };
1969     
1970     /**
1971      * Return the user home.
1972      *
1973      * @returns {baja.Ord}
1974      */
1975     baja.getUserHome = function () {
1976       return baja.Ord.make(bsUserHome);
1977     };
1978     
1979     /**
1980      * Return the user's default time format pattern.
1981      *
1982      * @returns {String}
1983      */
1984     baja.getTimeFormatPattern = function () {
1985       return bsTimeFormat;
1986     };
1987     
1988     /**
1989      * Return true if any client Errors are also being logged in the Server.
1990      * <p>
1991      * Please note, since BOX and HTTP Errors are technically from the Server,
1992      * these do not constitute as client Errors.
1993      *
1994      * @returns {Boolean}
1995      */
1996     baja.isLogClientErrorsInServer = function () {
1997       return bsLogClientErrorsInServer;
1998     };  
1999     
2000     /**
2001      * Initialize BajaScript from System Properties.
2002      *
2003      * @private   
2004      *
2005      * @param {Object} props  System Properties loaded from Server.
2006      */
2007     baja.initFromSysProps = function (props) {  
2008       // Record system properties...
2009       bsUserName = props.userName;
2010       bsLang = props.lang;
2011       bsUserHome = props.userHome;
2012       bsTimeFormat = props.timeFormat;
2013       bsLogClientErrorsInServer = props.logClientErrors || false;
2014   
2015       // Bail if web storage isn't enabled    
2016       if (!props.enableWebStorage) {
2017         // Free up the reference to original loaded data
2018         bsPreLoadRegStorage = null;
2019         
2020         // If Storage isn't enabled then try clearing it anyway
2021         baja.registry.clearStorage();
2022         return;
2023       }
2024     
2025       if (bsPreLoadRegStorage) {
2026         // If any of this information has changed then we need to wipe the registry storage and start again...
2027         if (bsPreLoadRegStorage.lang !== props.lang ||
2028             bsPreLoadRegStorage.version !== baja.version ||
2029             bsPreLoadRegStorage.regLastBuildTime !== props.regLastBuildTime) 
2030         {
2031           bsPreLoadRegStorage = null;
2032           baja.registry.clearStorage();
2033         }
2034       }
2035   
2036       if (!bsPreLoadRegStorage) {
2037         bsPreLoadRegStorage = {
2038           "lang": props.lang,
2039           "version": baja.version,
2040           "regLastBuildTime": props.regLastBuildTime,
2041           "types": {},
2042           "lexicons": {}
2043         };
2044       }
2045       
2046       // Now we know we want to use this as the registry storage, assign it accordingly.
2047       bsRegStorage = bsPreLoadRegStorage;
2048   
2049       // Free up the reference to original loaded data
2050       bsPreLoadRegStorage = null;
2051       
2052       // Submit all Type information into the registry
2053       baja.registry.register(bsRegStorage.types);
2054       
2055       // Load the cached Lexicons
2056       var moduleName;
2057       for (moduleName in bsRegStorage.lexicons) {
2058         if (bsRegStorage.lexicons.hasOwnProperty(moduleName)) {
2059           bsLexicons[moduleName] = new Lexicon(moduleName, bsRegStorage.lexicons[moduleName]);
2060         }
2061       }
2062     };
2063   }()); // startAndStop
2064     
2065   ////////////////////////////////////////////////////////////////
2066   // Types and Registry
2067   //////////////////////////////////////////////////////////////// 
2068   
2069   (function registry() {
2070     function loadTypes(registry, typeSpecs, encodeContracts, cb, async) {
2071       strictAllArgs([typeSpecs, encodeContracts, cb, async], 
2072                     [Array, Boolean, baja.comm.Callback, Boolean]);
2073   
2074       var rts = [], // Types to request
2075           t, // Type
2076           i;
2077   
2078       // Only request Types that aren't already present in the registry
2079       
2080       for (i = 0; i < typeSpecs.length; ++i) { 
2081         if (registry.$types.hasOwnProperty(typeSpecs[i])) {
2082           t = registry.$types[typeSpecs[i]];
2083           if (encodeContracts && (t.isComplex() || t.isFrozenEnum()) && !t.hasContract()) {
2084             rts.push(typeSpecs[i]);
2085           }
2086         }
2087         else {
2088           rts.push(typeSpecs[i]);
2089         }
2090       }
2091   
2092       var ts = null;
2093   
2094       // Create inner callback    
2095       cb.addOk(function (ok, fail, resp) {
2096         ts = [];
2097         var i;
2098   
2099         if (resp !== undefined) {              
2100           // Add information to the registry and make sure registry storage is updated.
2101           registry.register(resp, /*updateRegStorageTypes*/true);          
2102         }
2103   
2104         // Get all registry information we requested
2105         for (i = 0; i < typeSpecs.length; ++i) {
2106           ts.push(registry.$types[typeSpecs[i]]);
2107         }
2108   
2109         ok(ts);
2110       });
2111   
2112       // If there are Types to request then make the network call
2113       if (rts.length > 0) {    
2114         baja.comm.loadTypes(rts, encodeContracts, cb, async);
2115       }
2116       else {
2117         // If we already have all of the Type information then return it
2118         cb.ok();
2119       }
2120   
2121       return ts;
2122     }
2123     
2124     /**
2125      * Return an instance from the Type.
2126      * <p>
2127      * When creating an instance of a Type, this method should always be used by preference...
2128      * <pre>
2129      *   // Create an instance of a NumericWritable Control Component...
2130      *   var v = baja.$("control:NumericWritable");
2131      * </pre>
2132      * <p>
2133      * At first this 'dollar' function looks a bit strange. However, much like other popular JavaScript libraries, this
2134      * function has been reserved for the most commonly used part of BajaScript; creating instances of BajaScript Objects.
2135      *
2136      * @see Type#getInstance
2137      *
2138      * @param {String} typeSpec the Type Specification.
2139      * @returns an instance from the Type
2140      */
2141     baja.$ = function (typeSpec) {
2142       // Get a fully loaded Type
2143       var type = baja.lt(typeSpec, /*encodeContracts*/true);
2144       
2145       // If only the TypeSpec was specified then just create an instance of that Type
2146       if (arguments.length === 1) {
2147         return type.getInstance();
2148       }
2149       else {
2150         // If more arguments were specified then pass them onto to 'getInstance'
2151         var args = Array.prototype.slice.call(arguments);
2152         args.shift(); // remove the first 'typeSpec argument
2153         return type.getInstance.apply(type, args);
2154       }
2155     };
2156     
2157     /**
2158      * Load the Type and Contract information for the given TypeSpecs.
2159      * <p>
2160      * A TypeSpec is a String in the format of 'moduleName:typeName' in Niagara.
2161      * <p>
2162      * This method may perform a network call if one of the TypeSpecs can't be found locally in the
2163      * Registry or if a Contract needs to be retrieved.
2164      * <p>
2165      * If a network call is expected, it's recommended to pass in ok and fail callback functions that will load the Type 
2166      * information asynchronously.
2167      * <p>
2168      * If a number of network calls is expected to be made then a batch {@link baja.comms.Batch} object 
2169      * can be passed into this method along with a callback. The network call will then be 'batched' accordingly.
2170      * <p> 
2171      * The method can be invoked with an array of TypeSpecs or an Object Literal...
2172      * <pre>
2173      *   baja.importTypes(["control:NumericWritable", "control:BooleanWritable"]);
2174      *
2175      *   // ...or via an Object Literal to specify further options...
2176      *
2177      *   baja.importTypes({
2178      *     typeSpecs: [["control:NumericWritable", "control:BooleanWritable"]],
2179      *     ok: function (newlyAddedTypes) {
2180      *       // Called on success (if specified, network call will be asynchronous otherwise synchronous)
2181      *     },
2182      *     fail: function (err) {
2183      *       // Called on failure (optional)
2184      *     },
2185      *     batch: batch // If specified, network calls are batched into this object
2186      *   });
2187      * </pre>
2188      * <p>
2189      * If the types for a given Web Application are known upfront then please specify them in {@link baja#start} instead.
2190      *
2191      * @see baja.start
2192      *
2193      * @param {Object} obj  the Object Literal for the method's arguments.
2194      * @param {Function} [obj.ok] the ok callback. By defining this, the network call automatically becomes asynchronous.
2195      *                            An array of the newly added Types will be passed into this handler.
2196      * @param {Function} [obj.fail] the fail callback.
2197      * @param {baja.comm.Batch} [obj.batch] the batch Buffer. If defined, any network calls will be batched into this object.
2198      * @returns {Array} an array of Types if this method is invoked synchronously otherwise null.
2199      */
2200     baja.importTypes = function (obj) {
2201       obj = objectify(obj, "typeSpecs");
2202       var cb = new baja.comm.Callback(obj.ok, obj.fail, obj.batch);
2203       try {
2204         return loadTypes(baja.registry, 
2205                          obj.typeSpecs,
2206                          /*encodeContracts*/true,
2207                          cb,
2208                          /*async*/obj.ok !== undefined);
2209       }
2210       catch (err) {
2211         cb.fail(err);
2212       }
2213       return null;
2214     };
2215     
2216     /**
2217      * Loads and returns a Type.
2218      * <p>
2219      * This queries the BajaScript registry for a Type. If it doesn't exist
2220      * then as a last resort, a synchronous network call is made in an attempt
2221      * to retrieve the Type information.
2222      * <p>
2223      * Please note, no Contract information is retreived if a network call is made, only
2224      * the Type information.
2225      * <p>
2226      * If a network call is expected to be made then please try asynchronously importing the Type
2227      * information first via {@link baja#importTypes}.
2228      *
2229      * @see baja.importTypes
2230      * 
2231      * @param {String} typeSpec  the type spec of the type we're interested in (moduleName:typeName).
2232      * @param {Boolean} [encodeContracts] encode the Contract when the Type is loaded (false by default).
2233      * @returns {Type} the Type for the given type spec.
2234      * @throws error if the Type can't be found.
2235      */
2236     baja.lt = function (typeSpec, encodeContracts) {        
2237       if (baja.registry.$types.hasOwnProperty(typeSpec)) {
2238         return baja.registry.$types[typeSpec];
2239       }
2240       else {
2241         encodeContracts = bajaDef(encodeContracts, false);
2242         var cb = new baja.comm.Callback();
2243         try {
2244           var ts = loadTypes(baja.registry, 
2245                              [typeSpec],
2246                              encodeContracts,
2247                              cb,
2248                              /*async*/false);
2249   
2250           if (ts !== null) {
2251             return ts[0];
2252           }        
2253         }
2254         catch (err) {
2255           cb.fail(err);
2256         }
2257         return null;
2258       }
2259     };
2260 
2261     (function bajaRegistryNamespace() {
2262       // BajaScript Registry
2263       var bsRegistryOrdTypes = {}, 
2264           bsRegistrySimples = {}, 
2265           bsRegistryCtors = {};
2266       
2267       /**
2268        * @class A BajaScript Type.
2269        * <p>
2270        * This Constructor shouldn't be invoked directly.
2271        * <p>
2272        * Type is a inner class. To access a Type please use {@link baja.lt}.
2273        * 
2274        * @name Type
2275        * @extends BaseBajaObj
2276        * @inner
2277        * @public
2278        */
2279       var Type = function (typeSpec, 
2280                           superType, 
2281                           isAbstract, 
2282                           isInterface, 
2283                           interfaces, 
2284                           contract, 
2285                           trans, 
2286                           iconStr,
2287                           isValue,
2288                           isSimple,
2289                           isSingleton,
2290                           isNumber,
2291                           isComplex,
2292                           isComponent,
2293                           isLink,
2294                           isAction,
2295                           isTopic,
2296                           isFrozenEnum,
2297                           isOrdScheme) {
2298         // TODO: Never store transient Types in WebStorage
2299         this.$typeSpec = typeSpec;
2300         this.$superType = superType;
2301         this.$isAbstract = isAbstract;
2302         this.$isInterface = isInterface;    
2303         this.$interfaces = interfaces;     
2304         this.$contract = contract; 
2305         this.$isTransient = trans;
2306         this.$iconStr = iconStr;
2307         this.$isValue = isValue;
2308         this.$isSimple = isSimple;
2309         this.$isSingleton = isSingleton;
2310         this.$isNumber = isNumber;
2311         this.$isComplex = isComplex;
2312         this.$isComponent = isComponent;
2313         this.$isLink = isLink;
2314         this.$isAction = isAction;
2315         this.$isTopic = isTopic;
2316         this.$isFrozenEnum = isFrozenEnum;
2317         this.$isOrdScheme = isOrdScheme;
2318       }.$extend(BaseBajaObj);
2319   
2320       /**
2321        * Test for equality.
2322        * 
2323        * @param obj  value to test for equality.
2324        * @returns {Boolean}
2325        */
2326       Type.prototype.equals = function (obj) {
2327         if (obj === undefined || obj === null) {
2328           return false;
2329         } 
2330         if (obj.constructor !== Type) {
2331           return false;
2332         }    
2333         return obj.$typeSpec === this.$typeSpec;
2334       };
2335   
2336       /**
2337        * Return the Module Name for the Type.
2338        *
2339        * @returns {String} module name.
2340        */
2341       Type.prototype.getModuleName = function () { 
2342         return this.$typeSpec.split(":")[0];
2343       };
2344   
2345       /**
2346        * Return the Type Name for the Type.
2347        *
2348        * @returns {String} type name.
2349        */
2350       Type.prototype.getTypeName = function () { 
2351         return this.$typeSpec.split(":")[1];
2352       };
2353   
2354       /**
2355        * Return the full Type Specification for the Type (moduleName:typeName).
2356        *
2357        * @returns {String} type spec.
2358        */
2359       Type.prototype.getTypeSpec = function () {
2360         return this.$typeSpec;
2361       };
2362         
2363       /**
2364        * Return an instance of the Type.
2365        * <p> 
2366        * A Type may have an Function Constructor associated with it. If a Constructor 
2367        * is found with this Type, this it's used to return an instance.
2368        * <p>
2369        * If a Constructor can't be found on this Type, then the Super Types are inspected
2370        * and the first Constructor found is used instead. This provides an elegant 'dynamic typing'
2371        * mechanism whereby a Constructor is not needed for every single Type.
2372        * <p>
2373        * If the Type is a concrete Simple or Singleton, then the 'DEFAULT' Property on the
2374        * Constructor is returned.
2375        *
2376        * @throws if an instance of the Type can't be created (i.e. if the Type is an Interface or Abstract or no
2377        *         constructor can be found).
2378        * @returns instance of Type.
2379        */
2380       Type.prototype.getInstance = function (arg) { 
2381         var typeSpec = this.getTypeSpec(),
2382             def;
2383   
2384         if (this.$isInterface) {
2385           throw new Error("Cannot call 'getInstance' on an Interface Type: " + typeSpec);
2386         }
2387         if (this.$isAbstract) {
2388           throw new Error("Cannot call 'getInstance' on an Abstract Type: " + typeSpec);
2389         }
2390   
2391         // If this is a Simple then check to see if this Type as a default instance on its Constructor, if so then return it
2392         // If there isn't a Constructor then default to baja.DefaultSimple
2393         if ((this.isSimple() || this.isSingleton()) && bsRegistryCtors.hasOwnProperty(typeSpec)) {
2394           if (arguments.length === 0) {
2395             return bsRegistryCtors[typeSpec].DEFAULT;
2396           }
2397           else {
2398             // If there were any arguments then attempt to use then in the Simple's make method
2399             def = bsRegistryCtors[typeSpec].DEFAULT; 
2400             return def.make.apply(def, arguments);            
2401           }
2402         }
2403   
2404         // If we have a cached version of a Simple then return it. This is used since there may be 
2405         // DefaultSimples and FrozenEnums out there that don't have their own Constructor but are still immutable. 
2406         // Hence we still have an internal caching mechanism for them as a back up.
2407         if (bsRegistrySimples.hasOwnProperty(typeSpec)) {
2408           if (arguments.length === 0) {
2409             return bsRegistrySimples[typeSpec];
2410           }
2411           else {
2412             // If there are arguments specified then use them in the Simple's make method
2413             def = bsRegistrySimples[typeSpec];
2414             return def.make.apply(def, arguments);
2415           }
2416         }
2417   
2418         var t = this, // Type used in iteration
2419             Ct;
2420         
2421         // Go up Super types until we find a valid constructor
2422         while (t !== null) {   
2423           // Create the new Type if there's a Constructor associated with it
2424           if (bsRegistryCtors.hasOwnProperty(t.getTypeSpec())) {
2425             Ct = bsRegistryCtors[t.getTypeSpec()];
2426             break;
2427           }
2428           else {
2429             t = t.$superType;
2430           }
2431         }
2432   
2433         // Throw error if we can't decode this properly. Usually this means we need to rebuild the common type library!
2434         if (t === null) {
2435           throw new Error("Could not find JS Constructor for Type: " + typeSpec);
2436         }
2437   
2438         if (typeof Ct !== "function") {
2439           throw new Error("Fatal error, could not create instance of Type: " + typeSpec);
2440         }
2441   
2442         // Otherwise just default to creating a new Type
2443         var o = new Ct(arg);
2444   
2445         // Update the Type on the new instance if necessary
2446         if (typeSpec !== o.getType().getTypeSpec()) {
2447           var that = this;
2448           o.getType = function () {
2449             return that;
2450           };
2451         }
2452   
2453         // Apply the Contract to the new instance if there's one available.
2454         // This will load the default frozen Slots for the given Type
2455         if (this.isComplex() || this.isFrozenEnum()) {
2456           if (this.isComplex()) {
2457             // Decode Complex BSON for all of the Slots
2458             baja.bson.decodeComplexContract(this, o);
2459           }
2460           else if (this.isFrozenEnum()) {
2461             // Ensure the Contract is loaded (although for better performance it's best to do this before
2462             // hand using something like importTypes(...)
2463             if (!this.hasContract()) {
2464               this.loadContract();
2465             }
2466             
2467             // Get default ordinal for Enum and assign it to the FrozenEnum instance
2468             var ordinals = this.getOrdinals();
2469             if (ordinals.length > 0) {
2470               o.$ordinal = ordinals[0];
2471             }
2472           }
2473           
2474           // Also pass any start up arguments into Contract Committed
2475           o.contractCommitted(arg);
2476         }
2477   
2478         if (this.isSimple()) {
2479           // If this is a simple then cache the instance
2480           bsRegistrySimples[typeSpec] = o;
2481           
2482           // If there are arguments specified then use them with the Simple's make method
2483           if (arguments.length > 0) {
2484             o = o.make.apply(o, arguments);
2485           }
2486         }
2487   
2488         return o;
2489       };
2490   
2491       /**
2492        * Test one Type is another.
2493        *
2494        * @param {String|Type} type  this can be an instance of a Type object or a String type specification (module:typeName).
2495        * @returns {Boolean} true if this Type polymorphically matches the other.
2496        */
2497       Type.prototype.is = function (type) {         
2498         if (type.constructor === String) {
2499           type = baja.lt(type);
2500         }
2501   
2502         // Test Type against this   
2503         if (this.$typeSpec === type.$typeSpec) {
2504           return true;
2505         }
2506         
2507         // Can only perform this test if the type passed in is an interface 
2508         var i;        
2509         if (type.$isInterface) {     
2510           // Test Type against any interfaces
2511           for (i = 0; i < this.$interfaces.length; ++i) {
2512             if (this.$interfaces[i].is(type)) {
2513               return true;
2514             }
2515           }
2516         }
2517         else if (type.$isSimple && !this.isSimple) {
2518           return false;
2519         }
2520         else if (type.$isComponent && !this.$isComponent) {
2521           return false;
2522         }
2523         else if (type.$isComplex && !this.$isComplex) {
2524           return false;
2525         }
2526     
2527         // Test Type against any super Types     
2528         if (this.$superType) {
2529           return this.$superType.is(type);
2530         }
2531           
2532         return false;       
2533       };
2534   
2535       /**
2536        * Return true if the Type is a Value.
2537        *
2538        * @returns {Boolean}
2539        */
2540       Type.prototype.isValue = function () {
2541         return this.$isValue;
2542       };
2543   
2544       /**
2545        * Return true if the Type is a Simple.
2546        *
2547        * @returns {Boolean}
2548        */
2549       Type.prototype.isSimple = function () {
2550         return this.$isSimple;
2551       };
2552   
2553       /**
2554        * Return true if the Type is a Singleton.
2555        *
2556        * @returns {Boolean}
2557        */
2558       Type.prototype.isSingleton = function () {
2559         return this.$isSingleton;
2560       };
2561   
2562       /**
2563        * Return true if the Type is a Number.
2564        *
2565        * @returns {Boolean}
2566        */
2567       Type.prototype.isNumber = function () {
2568         return this.$isNumber;
2569       };
2570   
2571       /**
2572        * Return true if the Type is a Complex.
2573        *
2574        * @returns {Boolean}
2575        */
2576       Type.prototype.isComplex = function () {
2577         return this.$isComplex;
2578       };
2579   
2580       /**
2581        * Return true if the Type is a Component.
2582        *
2583        * @returns {Boolean}
2584        */
2585       Type.prototype.isComponent = function () {
2586         return this.$isComponent;
2587       };
2588   
2589       /**
2590        * Return true if the Type is a Struct.
2591        *
2592        * @returns {Boolean}
2593        */
2594       Type.prototype.isStruct = function () {
2595         return this.isComplex() && !this.isComponent();
2596       };
2597   
2598       /**
2599        * Return true if the Type is a Link.
2600        *
2601        * @returns {Boolean}
2602        */
2603       Type.prototype.isLink = function () {
2604         return this.$isLink;
2605       };
2606   
2607       /**
2608        * Return true if the Type is a baja:Action.
2609        *
2610        * @returns {Boolean}
2611        */
2612       Type.prototype.isAction = function () {
2613         return this.$isAction;
2614       };
2615   
2616       /**
2617        * Return true if the Type is a baja:Topic.
2618        *
2619        * @returns {Boolean}
2620        */
2621       Type.prototype.isTopic = function () {
2622         return this.$isTopic;
2623       };
2624   
2625       /**
2626        * Return true if the Type is a baja:FrozenEnum.
2627        *
2628        * @returns {Boolean}
2629        */
2630       Type.prototype.isFrozenEnum = function () {
2631         return this.$isFrozenEnum;
2632       };
2633   
2634       /**
2635        * Return true if the Type is a baja:OrdScheme.
2636        *
2637        * @returns {Boolean}
2638        */
2639       Type.prototype.isOrdScheme = function () {
2640         return this.$isOrdScheme;
2641       };
2642   
2643       /**
2644        * Return the Super Type.
2645        *
2646        * @returns {Type} Super Type or null if not available
2647        */
2648       Type.prototype.getSuperType = function () {
2649         return this.$superType;
2650       };
2651   
2652       /**
2653        * Return an array of interfaces Types implemented by this Type.
2654        *
2655        * @returns {Array} an array of interface types (all Type)
2656        */
2657       Type.prototype.getInterfaces = function () {
2658         return this.$interfaces.slice(0); // Return copy of array
2659       };
2660   
2661       /**
2662        * Return true if Type is Abstract.
2663        *
2664        * @returns {Boolean}
2665        */
2666       Type.prototype.isAbstract = function () {
2667         return this.$isAbstract;
2668       };
2669   
2670       /**
2671        * Return true if Type is an Interface.
2672        *
2673        * @returns {Boolean}
2674        */
2675       Type.prototype.isInterface = function () {
2676         return this.$isInterface;
2677       };
2678   
2679       /**
2680        * Return true if Type is transient.
2681        *
2682        * @returns {Boolean}
2683        */
2684       Type.prototype.isTransient = function () {
2685         return this.$isTransient;
2686       };
2687   
2688       /**
2689        * Return type spec as toString (moduleName:typeName).
2690        *
2691        * @returns {String} type spec
2692        */
2693       Type.prototype.toString = function () {
2694         return this.getTypeSpec();
2695       };
2696   
2697       /**
2698        * Return the Contract for the Type.
2699        *
2700        * @private
2701        *
2702        * @returns Contract
2703        */
2704       Type.prototype.getContract = function () {
2705         return this.$contract;
2706       };
2707   
2708       /**
2709        * Return true if the Type has a Contract.
2710        *
2711        * @private
2712        *
2713        * @returns {Boolean} true if the Type has a Contract.
2714        */
2715       Type.prototype.hasContract = function () {
2716         return this.$contract !== null;
2717       };
2718           
2719       /**
2720        * Ensures the Contract for the given Type is loaded.
2721        * <p>
2722        * If the Contract doesn't exist and this Type is a Complex or a FrozenEnum, a network call will be made to 
2723        * get the Contract.
2724        * 
2725        * @private
2726        *
2727        * @param {Object} [obj]  Object Literal containing the method's arguments.
2728        * @param {Function} [obj.ok] ok function callback. If defined, the network call is automatically made asynchronously
2729        *                               otherwise the network call is synchronous.
2730        * @param {Function} [obj.fail] fail function callback. If the Contract fails to load, this function will be called.
2731        * @param {baja.comm.Batch} [obj.batch] the batch buffer. If defined, the network call will be batched into this object.
2732        */   
2733       Type.prototype.loadContract = function (obj) {
2734         obj = objectify(obj);
2735         var cb = new baja.comm.Callback(obj.ok, obj.fail, obj.batch);
2736   
2737         try {    
2738           // See if the Contract is already present. If not then make a network call
2739           if (!(this.isComplex() || this.isFrozenEnum()) ||      
2740               this.isInterface() || 
2741               this.hasContract()) {
2742             cb.ok();
2743             return;
2744           }
2745   
2746           // Create the Callback to update this Types Contract
2747           cb.addOk(function (ok, fail, contracts) {
2748             var c; // Contract     
2749             var ts = []; // TypeSpecs to request
2750   
2751             // Update the Registry with all of these Contract definitions
2752             for (c in contracts) {
2753               if (contracts.hasOwnProperty(c)) {            
2754                 if (!baja.registry.$types.hasOwnProperty(c)) {
2755                   ts.push(c);
2756                 }
2757               }
2758             }
2759   
2760             // If we need to load any types then do so via a network call
2761             if (ts.length > 0) {
2762               // TODO: At some point, allow this to be asynchronous
2763               loadTypes(baja.registry, 
2764                         ts, 
2765                         /*encodeContracts*/false, 
2766                         new baja.comm.Callback(), 
2767                         /*async*/false);
2768             }
2769   
2770             // Update the Contracts
2771             for (c in contracts) {
2772               if (contracts.hasOwnProperty(c)) {
2773                 if (baja.registry.$types.hasOwnProperty(c)) {
2774                   baja.registry.$types[c].$contract = contracts[c];
2775   
2776                   // If available, save contract to web storage...
2777                   if (bsRegStorage && bsRegStorage.types.hasOwnProperty(c)) {
2778                     bsRegStorage.types[c].c = contracts[c];
2779                   }
2780                 }
2781               }
2782             }
2783   
2784             ok();
2785           });
2786   
2787           baja.comm.loadContract(this.getTypeSpec(), cb, /*async*/obj.ok !== undefined);      
2788         }
2789         catch (err) {
2790           cb.fail(err);
2791         }
2792       };
2793   
2794       /**
2795        * Return the Types's Icon.
2796        * 
2797        * @returns {String}
2798        */
2799       Type.prototype.getIcon = function () {
2800         if (this.$icon) {
2801           return this.$icon;
2802         }
2803   
2804         this.$icon = this.$iconStr ? baja.Icon.DEFAULT.decodeFromString(this.$iconStr) : baja.Icon.getStdObjectIcon();
2805         return this.$icon;
2806       };
2807   
2808       ////////////////////////////////////////////////////////////////
2809       // Type FrozenEnum Methods
2810       //////////////////////////////////////////////////////////////// 
2811   
2812       function enumTypeCheck(type) {
2813         if (!type.isFrozenEnum()) {
2814           throw new Error("Type must be a FrozenEnum");
2815         }
2816         if (!type.hasContract()) {
2817           type.loadContract();
2818           if (!type.hasContract()) {
2819             throw new Error("Unable to load the FrozenEnum Contract");
2820           }
2821         }
2822       }
2823   
2824       /**
2825        * Returns the ordinals for a Type that maps to a FrozenEnum.
2826        *
2827        * @private
2828        *
2829        * @throws error if Type is not a FrozenEnum or if Contract can't be loaded.
2830        * @returns {Array} array of ordinals for frozen enum.
2831        */
2832       Type.prototype.getOrdinals = function () {
2833         enumTypeCheck(this);    
2834         var ordinals = [], i;
2835         for (i = 0; i < this.$contract.length; ++i) {
2836           ordinals.push(this.$contract[i].o);
2837         }
2838         return ordinals;
2839       };
2840   
2841       /**
2842        * Returns whether the specified ordinal exists for this FrozenEnum Type.
2843        *
2844        * @private 
2845        *
2846        * @param {Number} ordinal
2847        * @throws error if Type is not a FrozenEnum or if Contract can't be loaded.
2848        * @returns {Boolean} true if the ordinal number exists in this FrozenEnum Type.
2849        */   
2850       Type.prototype.isOrdinal = function (ordinal) {
2851         enumTypeCheck(this);
2852         var contract = this.$contract, i;
2853         
2854         // Lazily generate the byOrdinal map    
2855         if (this.$byOrdinal === undefined) {
2856           this.$byOrdinal = {};
2857           for (i = 0; i < contract.length; ++i) {
2858             this.$byOrdinal[contract[i].o] = {
2859               o: contract[i].o,
2860               t: contract[i].t,
2861               dt: contract[i].dt
2862             };
2863           }
2864         }
2865   
2866         return this.$byOrdinal.hasOwnProperty(ordinal);
2867       };
2868   
2869       /**
2870        * Returns the tag for the ordinal (this Type has to map to a FrozenEnum).
2871        *
2872        * @private 
2873        *
2874        * @param {Number} ordinal
2875        * @throws error if Type is not a FrozenEnum, if Contract can't be loaded or if the ordinal doesn't exist.
2876        * @returns {String} the tag for the ordinal.
2877        */ 
2878       Type.prototype.getTag = function (ordinal) {
2879         enumTypeCheck(this); 
2880         if (this.isOrdinal(ordinal)) {
2881           return this.$byOrdinal[ordinal].t;
2882         }
2883         else {
2884           throw new Error("No tag for ordinal: " + ordinal + " in: " + this.getTypeSpec());
2885         }
2886       };
2887   
2888       /**
2889        * Returns the display tag for the ordinal (this Type has to map to a FrozenEnum).
2890        *
2891        * @private 
2892        *
2893        * @param {Number} ordinal
2894        * @throws error if Type is not a FrozenEnum, if Contract can't be loaded or if the ordinal doesn't exist.
2895        * @returns {String} the display tag for the ordinal.
2896        */ 
2897       Type.prototype.getDisplayTag = function (ordinal) {
2898         enumTypeCheck(this); 
2899         if (this.isOrdinal(ordinal)) {
2900           return this.$byOrdinal[ordinal].dt;
2901         }
2902         else {
2903           throw new Error("No display tag for ordinal: " + ordinal + " in: " + this.getTypeSpec());
2904         }
2905       };
2906   
2907       /**
2908        * Returns whether the specified tag exists for this FrozenEnum Type.
2909        *
2910        * @private 
2911        *
2912        * @param {String} tag
2913        * @throws error if Type is not a FrozenEnum or if Contract can't be loaded.
2914        * @returns {Boolean} true if the tag exists.
2915        */ 
2916       Type.prototype.isTag = function (tag) {
2917         enumTypeCheck(this); 
2918         var contract = this.$contract, i;
2919         
2920         // Lazily generate the byTag map    
2921         if (this.$byTag === undefined) {
2922           this.$byTag = {};
2923           for (i = 0; i < contract.length; ++i) {
2924             this.$byTag[contract[i].t] = {
2925               o: contract[i].o,
2926               t: contract[i].t,
2927               dt: contract[i].dt
2928             };
2929           }
2930         }
2931   
2932         return this.$byTag.hasOwnProperty(tag);
2933       };
2934   
2935       /**
2936        * Returns the ordinal for the tag (providing the Type maps to a FrozenEnum).
2937        *
2938        * @private 
2939        *
2940        * @param {String} tag
2941        * @throws error if Type is not a FrozenEnum, if Contract can't be loaded or the tag doesn't exist.
2942        * @returns {Number} ordinal for the tag.
2943        */ 
2944       Type.prototype.tagToOrdinal = function (tag) {
2945         enumTypeCheck(this); 
2946         if (this.isTag(tag)) {
2947           return this.$byTag[tag].o;
2948         }
2949         else {
2950           throw new Error("No ordinal tag for tag: " + tag + " in: " + this.getTypeSpec());
2951         }
2952       };
2953   
2954       /**
2955        * Returns the EnumRange for the Type (providing the Type maps to a FrozenEnum).
2956        *
2957        * @private 
2958        *
2959        * @see baja.EnumRange
2960        *
2961        * @throws error if Type is not a FrozenEnum, if Contract can't be loaded or the tag doesn't exist.
2962        * @returns {baja.EnumRange} the enum range.
2963        */ 
2964       Type.prototype.getRange = function () {
2965         enumTypeCheck(this); 
2966         if (this.$range === undefined) {
2967           this.$range = baja.EnumRange.make(this);
2968         }
2969         return this.$range;
2970       };
2971   
2972       /**
2973        * Returns the FrozenEnum for the Type (providing the Type maps to a FrozenEnum).
2974        *
2975        * @private 
2976        *
2977        * @see baja.EnumRange
2978        *
2979        * @param {String|Number} arg  the tag or ordinal for the frozen enum to return.
2980        * @throws error if Type is not a FrozenEnum, if Contract can't be loaded or the tag or ordinal doesn't exist.
2981        * @returns {FrozenEnum} the frozen enumeration for the tag or ordinal.
2982        */ 
2983       Type.prototype.getFrozenEnum = function (arg) {
2984         enumTypeCheck(this); 
2985   
2986         var ordinal = 0, fe;
2987   
2988         // Set the ordinal depending on the argument...
2989         if (typeof arg === "string") {
2990           // Look up the ordinal if the tag was passed in
2991           ordinal = this.tagToOrdinal(arg);
2992         }
2993         else if (typeof arg === "number" && this.isOrdinal(arg)) {
2994           // Validate the ordinal that was passed in
2995           ordinal = arg;
2996         }
2997         else {
2998           throw new Error("Invalid argument for FrozenEnum creation");
2999         }
3000   
3001         // Lazily create cache of frozen enum instances
3002         if (this.$enums === undefined) {
3003           this.$enums = {};
3004         }
3005   
3006         // Look up enum with ordinal in simples Cache
3007         if (this.$enums.hasOwnProperty(ordinal)) {
3008           fe = this.$enums[ordinal];
3009         }
3010         else {
3011   
3012           // Create instance and set up type access
3013           fe = this.getInstance();
3014   
3015           // If the ordinal differs then make a clone of the object
3016           if (fe.getOrdinal() !== ordinal) {
3017             var newFe = new fe.constructor();
3018             newFe.getType = fe.getType;
3019             fe = newFe;   
3020             fe.$ordinal = ordinal;
3021           }
3022   
3023           // Cache the frozen enum in the Type
3024           this.$enums[ordinal] = fe;
3025         }
3026   
3027         return fe;
3028       };
3029   
3030       /**
3031        * Return true if this Type has a constructor directly assocated with it.
3032        *
3033        * @private 
3034        *
3035        * @returns {Boolean}
3036        */ 
3037       Type.prototype.hasConstructor = function () {
3038         return bsRegistryCtors.hasOwnProperty(this.$typeSpec);
3039       };
3040   
3041       /**
3042        * Type Registration.
3043        * <p>
3044        * Registers the Constructor with the given TypeSpec. This method is used to extend BajaScript and associate
3045        * JavaScript Objects with Niagara Types.
3046        * <p>
3047        * This method also performs checks on the given Constructor to ensure it's 
3048        * suitable for the given Type.
3049        * 
3050        * @private
3051        *
3052        * @param {String} typeSpec  the TypeSpec we want to register with.
3053        * @returns {Object}  the Object (this).
3054        */ 
3055       Function.prototype.registerType = function (typeSpec) {
3056         strictArg(typeSpec, String);
3057         if (typeof this !== "function") {
3058           throw new Error("Can only loadType on a Function Constructor: " + typeSpec);
3059         }
3060   
3061         // Register the TypeSpec with this Constructor function
3062         bsRegistryCtors[typeSpec] = this;
3063   
3064         // Please note, the Constructor is now lazily validated when the Type information
3065         // is registered with BajaScript.
3066   
3067         // Set up default type access
3068         var type;
3069         this.prototype.getType = function () {
3070           // Lazily load Type information
3071           if (!type) {
3072             type = baja.lt(typeSpec);
3073           }
3074           return type;
3075         };
3076   
3077         return this;
3078       };
3079           
3080       /**
3081        * @namespace Baja Type Registry
3082        * <p>
3083        * At the core of BajaScript is a lazily loading Type and Contract system. A Contract
3084        * represents a frozen slot defition for a Complex or a set of FrozenEnum ordinals, tags and display tags
3085        * <p>
3086        * The most commonly used Types are held generated into a Common Type Library that gets
3087        * forms part of BajaScript JavaScript library to avoid unnecessary network calls.
3088        */
3089       baja.registry = new BaseBajaObj();
3090       baja.registry.$types = {
3091         // Add very core Baja Types as these will NEVER be included into any JSON    
3092         "baja:Object": new Type("baja:Object", // TypeSpec
3093           null,          // Super Type
3094           true,          // Is Abstract
3095           false,         // Is Interface
3096           [],            // Interfaces
3097           null,          // Contract  
3098           false,         // Transient
3099           null,          // Icon 
3100           false,         // isValue
3101           false,         // isSimple
3102           false,         // isSingleton
3103           false,         // isNumber
3104           false,         // isComplex
3105           false,         // isComponent
3106           false,         // isLink
3107           false,         // isAction
3108           false,         // isTopic
3109           false,         // isFrozenEnum
3110           false),        // isOrdScheme
3111                     
3112   
3113         "baja:Interface": new Type("baja:Interface", // TypeSpec
3114           null,             // Super Type
3115           true,             // Is Abstract
3116           true,             // Is Interface
3117           [],               // Interfaces
3118           null,             // Contract 
3119           false,            // Transient  
3120           null,             // Icon
3121           false,            // isValue
3122           false,            // isSimple
3123           false,            // isSingleton
3124           false,            // isNumber
3125           false,            // isComplex
3126           false,            // isComponent
3127           false,            // isLink
3128           false,            // isAction
3129           false,            // isTopic
3130           false,            // isFrozenEnum
3131           false)            // isOrdScheme          
3132       };
3133   
3134       /**
3135        * Inspect the object structure and create Type information to add to the Registry.
3136        * <p>
3137        * This should only be invoked by Tridium developers and is normally used in a network callback.
3138        *
3139        * @private   
3140        *
3141        * @param {Object} obj  the Object structure holding the Type information.
3142        * @param {Boolean} [updateRegStorageTypes] if true, this will update the Registry
3143        *                                          storage type database. (Contracts held in the 
3144        *                                          registry storage database will be updated regardless
3145        *                                          of whether this flag is truthy or not).
3146        */
3147       baja.registry.register = function (obj, updateRegStorageTypes) { 
3148         if (obj === undefined) {
3149           return;
3150         }   
3151   
3152         var intType = baja.lt("baja:Interface"),
3153             objType = baja.lt("baja:Object"),    
3154             typeSpec, // Add Types to Registry database without super or interface information...
3155             data,
3156             newTypeObj = {};
3157   
3158         for (typeSpec in obj) {
3159           if (obj.hasOwnProperty(typeSpec)) {
3160             data = obj[typeSpec]; 
3161               
3162             // If the type doesn't exist in the registry then create it...
3163             if (!this.$types.hasOwnProperty(typeSpec)) {
3164               this.$types[typeSpec] = new Type(typeSpec,                // TypeSpec
3165                                                null,                    // Super Type
3166                                                data.a || false,         // Is Abstract
3167                                                data.i || false,         // Is Interface
3168                                                [],                      // Interfaces
3169                                                bajaDef(data.c, null),   // Contract
3170                                                data.t || false,         // Transient
3171                                                bajaDef(data.ic, null), // Icon String
3172                                                data.isv || false,       // isValue
3173                                                data.iss || false,       // isSimple
3174                                                data.isg || false,       // isSingleton
3175                                                data.isn || false,       // isNumber
3176                                                data.isx || false,       // isComplex
3177                                                data.isc || false,       // isComponent
3178                                                data.isl || false,       // isLink
3179                                                data.isa || false,       // isAction
3180                                                data.ist || false,       // isTopic
3181                                                data.ise || false,       // isFrozenEnum,
3182                                                data.os  || false);      // isOrdScheme
3183   
3184               // Create object with new information we're processing...
3185               newTypeObj[typeSpec] = data;
3186               
3187               // Update local storage if available...
3188               if (updateRegStorageTypes && bsRegStorage) {
3189                 // Update web storage (providing Type isn't transient)...
3190                 if (!data.t) {
3191                   bsRegStorage.types[typeSpec] = data;
3192                 }
3193               }
3194             }
3195             else {              
3196               // If the type does exist in the registry then ensure it's contract is loaded (if there is one)
3197               if (data.c) {                
3198                 this.$types[typeSpec].$contract = data.c;
3199                 
3200                 // If available, update contract in web storage...
3201                 if (bsRegStorage && bsRegStorage.types.hasOwnProperty(typeSpec)) {
3202                   bsRegStorage.types[typeSpec].c = data.c;
3203                 }
3204               }           
3205             }
3206           }
3207         }
3208   
3209         // Now add all super and interface information...
3210         var i,
3211             type;
3212         
3213         for (typeSpec in newTypeObj) {
3214           if (newTypeObj.hasOwnProperty(typeSpec)) {
3215             data = newTypeObj[typeSpec]; 
3216             type = this.$types[typeSpec];
3217   
3218             // Skip baja:Interface and baja:Object...
3219             if (type === intType || type === objType) {
3220               continue;
3221             }
3222   
3223             // Set up interfaces...
3224             if (data.it && data.it.length > 0) {
3225               for (i = 0; i < data.it.length; ++i) {
3226                 type.$interfaces.push(baja.lt(data.it[i]));
3227               }
3228             }
3229             else if (data.i) {
3230               // If this is an interface then this must at least extend baja:Interface
3231               type.$interfaces.push(intType);
3232             }
3233   
3234             // Set up Super Type for non-interfaces...
3235             if (!data.i) {
3236               if (data.p) {
3237                 type.$superType = baja.lt(data.p);
3238               }
3239               else {
3240                 type.$superType = objType;
3241               }
3242             }     
3243           }
3244         }
3245   
3246         // Iterate through newly added Type information
3247         var ctor;
3248         for (typeSpec in newTypeObj) {
3249           if (newTypeObj.hasOwnProperty(typeSpec)) {
3250             data = newTypeObj[typeSpec]; 
3251             type = baja.lt(typeSpec);
3252   
3253             // Lazily do checks on Ctor information associated with Type        
3254             if (!type.isAbstract()) {
3255               if (bsRegistryCtors.hasOwnProperty(typeSpec)) {
3256                 ctor = bsRegistryCtors[typeSpec];
3257   
3258                 // Check concrete Simple Types conform
3259                 if (type.isSimple()) {
3260                   if (ctor.DEFAULT === undefined) {
3261                     throw new Error("Concrete Simple implementations must define a DEFAULT instance argument on the Constructor: " + typeSpec);
3262                   }
3263                   if (typeof ctor.DEFAULT.make !== "function") {
3264                     throw new Error("Concrete Simple implementations must define a make method: " + typeSpec);
3265                   }
3266                   if (typeof ctor.DEFAULT.decodeFromString !== "function") {
3267                     throw new Error("Concrete Simple implementations must define a decodeFromString method: " + typeSpec);
3268                   }
3269                   if (typeof ctor.DEFAULT.encodeToString !== "function") {
3270                     throw new Error("Concrete Simple implementations must define a encodeToString method: " + typeSpec);
3271                   }
3272                 }    
3273   
3274                 // Check concrete Singletons Types conform
3275                 if (type.isSingleton()) {
3276                   if (ctor.DEFAULT === undefined) {
3277                     throw new Error("Concrete Singletons must define a DEFAULT instance argument on the Constructor: " + typeSpec);
3278                   }
3279                 }
3280               }
3281   
3282               // Register ORD schemes...        
3283               if (data.os && type.isOrdScheme()) {
3284                 bsRegistryOrdTypes[data.os] = type;
3285               }
3286             }
3287           }
3288         }
3289       };
3290   
3291       /**
3292        * Return the Type for the given ORD scheme name.
3293        *
3294        * @private
3295        *
3296        * @param {String} schemeId  the ORD scheme name.
3297        * @returns the Type for the ORD Scheme (null is returned if a Type can't be found).
3298        */
3299       baja.registry.getOrdScheme = function (schemeId) {
3300         return bajaDef(bsRegistryOrdTypes[schemeId], null);
3301       };
3302   
3303       /**
3304        * Does the Type exist in the BajaScript registry?
3305        * <p>
3306        * This method will not result in any network calls.
3307        *
3308        * @param {String} typeSpec  the type specification to query the registry for.
3309        * @returns {Boolean} true if the Type exists in the BajaScript registry.
3310        */
3311       baja.registry.hasType = function (typeSpec) {
3312         return this.$types.hasOwnProperty(typeSpec);
3313       };
3314   
3315       /**
3316        * Same as {@link baja#lt}.
3317        * 
3318        * @name baja.registry#loadType
3319        * @function
3320        *
3321        * @see baja.lt
3322        */
3323       baja.registry.loadType = baja.lt;  
3324             
3325       /**
3326        * Same as {@link baja#importTypes}.
3327        * 
3328        * @name baja.registry.loadTypesWithContract
3329        * @function
3330        *
3331        * @see baja.importTypes
3332        */
3333       baja.registry.loadTypesWithContract = baja.importTypes;  
3334       
3335       /**
3336        * Get an array of concrete Types from the given typeInfo. This method
3337        * will make a network call. The type information returned in the 
3338        * ok handler may not necessarily have Contract information loaded. 
3339        * To then ensure the Contract information for Complex and FrozenEnums
3340        * is loaded, please use {@link baja#importTypes}.
3341        * <p>
3342        * This method takes an Object Literal as an argument.
3343        * <pre>
3344        *   baja.getConcreteTypes({
3345        *     type: "control:ControlPoint",
3346        *     ok: function (concreteTypes) {
3347        *       // Called on success
3348        *     },
3349        *     fail: function (err) {
3350        *       // Called on failure (optional)
3351        *     },
3352        *     batch: batch // If specified, network calls are batched into this object
3353        *   });
3354        * </pre>
3355        * 
3356        * @see baja.importTypes
3357        * @see Type
3358        * 
3359        * @param {Object} obj the Object literal for the method's arguments.
3360        * @param {String|Type} obj.type the type or (String type specification - module:typeName) to query the registry for.
3361        * @param {Function} obj.ok the ok callback. When invoked, an array of Types will be passed 
3362        *                          in as an argument.
3363        * @param {Function} [obj.fail] the fail callback.
3364        * @param {baja.comm.Batch} [obj.batch] the batch Buffer. If defined, the network call will be batched into this object.
3365        */
3366       baja.registry.getConcreteTypes = function (obj) {
3367         obj = objectify(obj);
3368         
3369         var typeSpec;
3370         if (obj.type) {
3371           typeSpec = obj.type.toString();
3372         }
3373         
3374         // Ensure we have these arguments
3375         baja.strictAllArgs([typeSpec, obj.ok], [String, Function]);
3376         
3377         var cb = new baja.comm.Callback(obj.ok, obj.fail, obj.batch);
3378                 
3379         cb.addOk(function (ok, fail, resp) {    
3380           var types = resp.t,
3381               specs = resp.s;
3382           
3383           // Register all of the Type information we get back
3384           baja.registry.register(types, /*updateRegStorageTypes*/true);
3385           
3386           // Get all of the Type inforamtion from the BajaScript registry
3387           var concreteTypes = [];
3388           baja.iterate(specs, function (typeSpec) {
3389             concreteTypes.push(baja.lt(typeSpec));
3390           });
3391           
3392           // Make the callback with the Type information.
3393           ok(concreteTypes);
3394         });
3395         
3396         // Make the network call
3397         baja.comm.getConcreteTypes(typeSpec, cb);
3398       };
3399       
3400       /**
3401        * Clear the registry from permanent storage. By default, this does nothing and 
3402        * must be overridden in a utility library like browser.js to clear the
3403        * localStorage (or to a flat file in a server environment, etc).
3404        *  
3405        * @name baja.registry.clearStorage
3406        * @function
3407        * @private
3408        */
3409       baja.registry.clearStorage = function () {
3410         // Do nothing - override with browser-dependent localStorage hook, etc.
3411       };
3412       
3413       /**
3414        * Saves the registry to permanent storage. By default, this does nothing and 
3415        * must be overridden in a utility library like browser.js to save to
3416        * localStorage (or to a flat file in a server environment, etc).
3417        *  
3418        * @name baja.registry.saveToStorage
3419        * @function
3420        * @private
3421        *
3422        * @param {Object} regStorage the BajaScript registry information to store
3423        */
3424       baja.registry.saveToStorage = function (regStorage) {
3425         // Do nothing - override with browser-dependent localStorage hook, etc.
3426       };
3427       
3428       /**
3429        * Load Registry Storage information and return it.
3430        * By default, this does nothing and must be overriden in a utility library
3431        * like browser.js to load from localStorage (or from a flat file in a server
3432        * environment, etc).
3433        * 
3434        * @name baja.registry.loadFromStorage
3435        * @function
3436        * @private
3437        */
3438       baja.registry.loadFromStorage = function () {
3439         // Return null - override with browser-dependent localStorage hook, etc.,
3440         return null;
3441       };
3442   
3443     }());
3444   }());
3445     
3446 }(baja, BaseBajaObj));