1 //
  2 // Copyright 2010, Tridium, Inc. All Rights Reserved.
  3 //
  4 
  5 /**
  6  * Core Component Architecture for BajaScript.
  7  *
  8  * @author Gareth Johnson
  9  * @version 1.0.0.0
 10  */
 11 
 12 //JsLint options (see http://www.jslint.com )
 13 /*jslint rhino: true, onevar: false, plusplus: true, white: true, undef: false, nomen: false, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false, indent: 2, vars: true, continue: true */
 14 
 15 // Globals for JsLint to ignore 
 16 /*global baja, syncVal, BaseBajaObj, syncProps*/ 
 17   
 18 (function comp(baja, BaseBajaObj) {
 19 
 20   // Use ECMAScript 5 Strict Mode
 21   "use strict";
 22   
 23   var emptyArray = [], // An empty Array that should be treated as being immutable
 24       strictArg = baja.strictArg, // Create local for improved minification
 25       strictAllArgs = baja.strictAllArgs,
 26       bajaDef = baja.def,
 27       objectify = baja.objectify,
 28       Callback = baja.comm.Callback;
 29      
 30   ////////////////////////////////////////////////////////////////
 31   // Flags
 32   //////////////////////////////////////////////////////////////// 
 33 
 34   /**
 35    * @class Slot Flags.
 36    * <p>
 37    * Flags are boolean values which are stored as
 38    * a bitmask on each slot in a Complex.  Some
 39    * flags apply to all slot types, while some only
 40    * have meaning for certain slot types.
 41    * <p>
 42    * Flags should always be a Number. This Constructor should only be 
 43    * used to create new objects by Tridium developers.
 44    *
 45    * @name baja.Flags
 46    * @extends BaseBajaObj
 47    */
 48   baja.Flags = function (mask, symbol, fname) {
 49     this.$mask = mask;
 50     this.$symbol = symbol;
 51     this.$name = fname;
 52     
 53     // Cache this Flag via the symbol name in an Object for quick decoding
 54     baja.Flags.bySymbol[symbol] = this;
 55   }.$extend(BaseBajaObj);
 56   
 57   baja.Flags.bySymbol = {}; // Used to quickly decode Flag Strings
 58   
 59   /**
 60    * Return the mask for the Flags.
 61    *
 62    * @returns {Number}
 63    */
 64   baja.Flags.prototype.getMask = function () {
 65     return this.$mask;
 66   };
 67   
 68   /**
 69    * Return the symbol for the Flags.
 70    *
 71    * @returns {String}
 72    */
 73   baja.Flags.prototype.getSymbol = function () {
 74     return this.$symbol;
 75   };
 76   
 77   /**
 78    * Return the String representation of the Flags.
 79    *
 80    * @returns {String}
 81    */
 82   baja.Flags.prototype.toString = function () {
 83     return this.$name;
 84   };
 85   
 86   /**
 87    * Equality test.
 88    *
 89    * @param obj
 90    * @returns {Boolean}
 91    */
 92   baja.Flags.prototype.equals = function (obj) { 
 93     return obj instanceof baja.Flags && obj.getMask() === this.getMask();
 94   };
 95   
 96   /**
 97    * readonly flag (Number).
 98    */
 99   baja.Flags.READONLY             = 0x00000001;  // 'r'
100   
101   /**
102    * transient flag (Number).
103    */
104   baja.Flags.TRANSIENT            = 0x00000002;  // 't'
105   
106   /**
107    * hidden flag (Number).
108    */
109   baja.Flags.HIDDEN               = 0x00000004;  // 'h'
110   
111   /**
112    * summary flag (Number).
113    */
114   baja.Flags.SUMMARY              = 0x00000008;  // 's'
115   
116   /**
117    * async flag (Number).
118    */
119   baja.Flags.ASYNC                = 0x00000010;  // 'a'
120   
121   /**
122    * noRun flag (Number).
123    */
124   baja.Flags.NO_RUN               = 0x00000020;  // 'n'
125   
126   /**
127    * defaultOnClone flag (Number).
128    */
129   baja.Flags.DEFAULT_ON_CLONE     = 0x00000040;  // 'd'
130   
131   /**
132    * confirmRequired flag (Number).
133    */
134   baja.Flags.CONFIRM_REQUIRED     = 0x00000080;  // 'c'
135   
136   /**
137    * operator flag (Number).
138    */
139   baja.Flags.OPERATOR             = 0x00000100;  // 'o'
140   
141   /**
142    * executeOnChange flag (Number).
143    */
144   baja.Flags.EXECUTE_ON_CHANGE    = 0x00000200;  // 'x'
145   
146   /**
147    * fanIn flag (Number).
148    */
149   baja.Flags.FAN_IN               = 0x00000400;  // 'f'
150   
151   /**
152    * noAudit flag (Number).
153    */
154   baja.Flags.NO_AUDIT             = 0x00000800;  // 'A'
155   
156   /**
157    * composite flag (Number).
158    */
159   baja.Flags.COMPOSITE            = 0x00001000;  // 'p'
160   
161   /**
162    * removeOnClone flag (Number).
163    */
164   baja.Flags.REMOVE_ON_CLONE      = 0x00002000;  // 'R'
165   
166   /**
167    * metaData flag (Number).
168    */
169   baja.Flags.METADATA             = 0x00004000;  // 'm'
170   
171   /**
172    * linkTarget flag (Number).
173    */
174   baja.Flags.LINK_TARGET          = 0x00008000;  // 'L'
175   
176   /**
177    * nonCritical flag (Number).
178    */
179   baja.Flags.NON_CRITICAL         = 0x00010000;  // 'N'
180   
181   /**
182    * userDefined1 flag (Number).
183    */
184   baja.Flags.USER_DEFINED_1       = 0x10000000;  // '1'
185   
186   /**
187    * userDefined2 flag (Number).
188    */
189   baja.Flags.USER_DEFINED_2       = 0x20000000;  // '2'
190   
191   /**
192    * userDefined3 flag (Number).
193    */
194   baja.Flags.USER_DEFINED_3       = 0x40000000;  // '3'
195   
196   /**
197    * userDefined4 flag (Number).
198    */
199   baja.Flags.USER_DEFINED_4       = 0x80000000;  // '4'
200   
201   baja.Flags.flags = [new baja.Flags(baja.Flags.READONLY,           "r", "readonly"),
202                       new baja.Flags(baja.Flags.TRANSIENT,          "t", "transient"),
203                       new baja.Flags(baja.Flags.HIDDEN,             "h", "hidden"),
204                       new baja.Flags(baja.Flags.SUMMARY,            "s", "summary"),
205                       new baja.Flags(baja.Flags.ASYNC,              "a", "async"),
206                       new baja.Flags(baja.Flags.NO_RUN,             "n", "noRun"),
207                       new baja.Flags(baja.Flags.DEFAULT_ON_CLONE,   "d", "defaultOnClone"),
208                       new baja.Flags(baja.Flags.CONFIRM_REQUIRED,   "c", "confirmRequired"),
209                       new baja.Flags(baja.Flags.OPERATOR,           "o", "operator"),
210                       new baja.Flags(baja.Flags.EXECUTE_ON_CHANGE,  "x", "executeOnChange"),
211                       new baja.Flags(baja.Flags.FAN_IN,             "f", "fanIn"),
212                       new baja.Flags(baja.Flags.NO_AUDIT,           "A", "noAudit"),
213                       new baja.Flags(baja.Flags.COMPOSITE,          "p", "composite"),
214                       new baja.Flags(baja.Flags.REMOVE_ON_CLONE,    "R", "removeOnClone"),
215                       new baja.Flags(baja.Flags.METADATA,           "m", "metadata"),
216                       new baja.Flags(baja.Flags.NON_CRITICAL,       "N", "nonCritical"),
217                       new baja.Flags(baja.Flags.LINK_TARGET,        "L", "linkTarget"),
218                       new baja.Flags(baja.Flags.USER_DEFINED_1,     "1", "userDefined1"),
219                       new baja.Flags(baja.Flags.USER_DEFINED_2,     "2", "userDefined2"),
220                       new baja.Flags(baja.Flags.USER_DEFINED_3,     "3", "userDefined3"),
221                       new baja.Flags(baja.Flags.USER_DEFINED_4,     "4", "userDefined4")
222                       ];
223   
224   /**
225    * Encode Slot Flags to a String.
226    *
227    * @param {Number} flags  the flags to be encoded.
228    *
229    * @returns {String}
230    */  
231   baja.Flags.encodeToString = function (flags) {
232     if (flags === 0) {
233       return "";
234     }
235   
236     strictArg(flags, Number);
237     var s = "", i;
238     for (i = 0; i < baja.Flags.flags.length; ++i) {
239       if ((baja.Flags.flags[i].getMask() & flags) !== 0) {
240         s += baja.Flags.flags[i].getSymbol();
241       }
242     }
243     return s;
244   };  
245   
246   /**
247    * Decode Slot Flags from a String.
248    *
249    * @param {String} flagsStr  the Flags encoded as a String.
250    * @returns {Number}
251    */  
252   baja.Flags.decodeFromString = function (flagsStr) {
253     if (flagsStr === "0") {
254       return 0;
255     }
256     
257     strictArg(flagsStr, String);
258     
259     var decodedFlags = 0,
260         symbols = flagsStr.split(''), 
261         flags,
262         i;
263         
264     for (i = 0; i < symbols.length; ++i) {
265       // Find the flags via a Symbol look up
266       flags = baja.Flags.bySymbol[symbols[i]];
267       
268       if (flags) {
269         // Add the mask for the flag to the result
270         decodedFlags |= flags.getMask();
271       }
272     }
273     return decodedFlags;
274   };
275     
276   ////////////////////////////////////////////////////////////////
277   // Slots
278   ////////////////////////////////////////////////////////////////  
279   
280   /**
281    * @class Slot
282    * <p>
283    * A Niagara Complex is made up of Slots. A Slot can be a Property, Action or a Topic.
284    * This is the base class for all Slots in BajaScript.
285    * <p>
286    * A new object should never be directly created with this Constructor.
287    *
288    * @name baja.Slot
289    * @extends BaseBajaObj
290    *
291    * @param {String} slotName  the name of the Slot.
292    * @param {String} displayName  the display name of the Slot.
293    */
294   baja.Slot = function (slotName, displayName) {
295     this.$slotName = slotName || "";
296     this.$displayName = displayName || "";
297   }.$extend(BaseBajaObj);
298   
299   /**
300    * Return the name of the Slot.
301    *
302    * @returns {String}
303    */
304   baja.Slot.prototype.getName = function () { 
305     return this.$slotName; 
306   };
307   
308   /**
309    * Return a String representation of the Slot.
310    *
311    * @returns {String}
312    */
313   baja.Slot.prototype.toString = function () { 
314     return this.getName(); 
315   };
316   
317   /**
318    * Return the display name of the Slot.
319    * <p>
320    * Please note, this method is intended for INTERNAL use by Tridium only. An
321    * external developer should never call this method.
322    *   
323    * @private 
324    * @returns {String}
325    */
326   baja.Slot.prototype.$getDisplayName = function () { 
327     return this.$displayName; 
328   };
329   
330   /**
331    * Set the display name of the Slot.
332    * <p>
333    * Please note, this method is intended for INTERNAL use by Tridium only. An
334    * external developer should never call this method.
335    *   
336    * @private 
337    * @param {String} displayName
338    */
339   baja.Slot.prototype.$setDisplayName = function (displayName) { 
340     this.$displayName = displayName;
341   };
342       
343   /**
344    * Is the Slot frozen?
345    *
346    * @returns {Boolean}
347    */
348   baja.Slot.prototype.isFrozen = function () {  
349     return false; 
350   };
351   
352   /**
353    * Is the Slot a Property?
354    *
355    * @returns {Boolean}
356    */
357   baja.Slot.prototype.isProperty = function () {
358     return false;
359   };
360   
361   /**
362    * Is the Slot a Topic?
363    *
364    * @returns {Boolean}
365    */
366   baja.Slot.prototype.isTopic = function () {
367     return false;
368   };
369   
370   /**
371    * Is the Slot an Action?
372    *
373    * @returns {Boolean}
374    */
375   baja.Slot.prototype.isAction = function () {
376     return false;
377   };
378 
379   /**
380    * @class Property Slot.
381    * <p>
382    * Property defines a Slot which is a storage location
383    * for a variable in a Complex.
384    * <p>
385    * A new object should never be directly created with this Constructor. All Slots are 
386    * created internally by BajaScript.
387    *
388    * @name baja.Property
389    * @extends baja.Slot
390    */  
391   baja.Property = function (slotName, displayName) {  
392     baja.Property.$super.apply(this, arguments);
393   }.$extend(baja.Slot);
394     
395   /**
396    * Is this Slot a Property?
397    *
398    * @returns {Boolean}
399    */
400   baja.Property.prototype.isProperty = function () {
401     return true;
402   };  
403 
404   /**
405    * @class Dynamic Property Slot.
406    * <p>
407    * Property defines a Slot which is a storage location
408    * for a variable in a Complex.
409    * <p>
410    * A new object should never be directly created with this Constructor. All Slots are 
411    * created internally by BajaScript.
412    *
413    * @name baja.DynamicProperty
414    * @extends baja.Property
415    * @inner
416    */
417   var DynamicProperty = function (slotName, displayName, display, flags, facets, value) {  
418     DynamicProperty.$super.apply(this, [slotName, displayName]);
419     this.$display = display || "";
420     this.$flags = flags || 0;
421     this.$facets = facets || baja.Facets.DEFAULT;
422     this.$val = bajaDef(value, null);
423   }.$extend(baja.Property);
424     
425   /**   
426    * Return the Property value.
427    * <p>
428    * Please note, this method is intended for INTERNAL use by Tridium only. An
429    * external developer should never call this method. Access a Property's value
430    * through the associated baja.Complex instead.
431    *
432    * @see baja.Complex#get
433    *
434    * @name baja.DynamicProperty#$getValue
435    * @function
436    * @private  
437    *
438    * @returns value
439    */
440   DynamicProperty.prototype.$getValue = function () {
441     return this.$val;
442   };
443 
444   /**   
445    * Set the Property value.
446    * <p>
447    * Please note, this method is intended for INTERNAL use by Tridium only. An
448    * external developer should never call this method.
449    *
450    * @name baja.DynamicProperty#$setValue
451    * @function
452    * @private  
453    *
454    * @param val value to be set.
455    */
456   DynamicProperty.prototype.$setValue = function (val) {
457     this.$val = val;
458   };   
459     
460   /**   
461    * Return the Flags for the Property.
462    *
463    * @name baja.DynamicProperty#getFlags
464    * @function
465    * @see baja.Flags
466    *
467    * @returns {Number}
468    */
469   DynamicProperty.prototype.getFlags = function () { 
470     return this.$flags; 
471   };
472   
473   /**   
474    * Set the Flags for the Property.
475    * <p>
476    * Please note, this method is intended for INTERNAL use by Tridium only. An
477    * external developer should never call this method.
478    *
479    * @name baja.DynamicProperty#$setFlags
480    * @function
481    * @private
482    * @see baja.Flags
483    *
484    * @param {Number} flags
485    */
486   DynamicProperty.prototype.$setFlags = function (flags) { 
487     this.$flags = flags; 
488   };
489   
490   /**
491    * Return the Facets for the Property.
492    *
493    * @name baja.DynamicProperty#getFacets
494    * @function
495    * @see baja.Facets
496    *
497    * @returns the Slot Facets
498    */
499   DynamicProperty.prototype.getFacets = function () {
500     return this.$facets; 
501   };
502   
503   /**   
504    * Set the Facets for the Property.
505    * <p>
506    * Please note, this method is intended for INTERNAL use by Tridium only. An
507    * external developer should never call this method.
508    *
509    * @name baja.DynamicProperty#$setFacets
510    * @function
511    * @private
512    * @see baja.Facets
513    *
514    * @param {baja.Facets} facets
515    */
516   DynamicProperty.prototype.$setFacets = function (facets) {
517     this.$facets = facets; 
518   };
519   
520   /**
521    * Return the default flags for the Property.
522    *
523    * @name baja.DynamicProperty#getDefaultFlags
524    * @function
525    *
526    * @returns {Number}
527    */
528   DynamicProperty.prototype.getDefaultFlags = function () {
529     return this.getFlags();
530   };
531   
532   /**
533    * Return the default value for the Property.
534    *
535    * @name baja.DynamicProperty#getDefaultValue
536    * @function
537    *
538    * @returns the default value for the Property.
539    */
540   DynamicProperty.prototype.getDefaultValue = function () {   
541     return this.$val;
542   }; 
543   
544   /**
545    * Return the Type for this Property.
546    *
547    * @name baja.DynamicProperty#getType
548    * @function
549    *
550    * @returns the Type for the Property.
551    */
552   DynamicProperty.prototype.getType = function () { 
553     return this.$val.getType(); 
554   };
555     
556   /**
557    * Return the display String for this Property.
558    * <p>
559    * Please note, this method is intended for INTERNAL use by Tridium only. An
560    * external developer should never call this method.
561    *
562    * @name baja.DynamicProperty#$getDisplay
563    * @function
564    * @private
565    *
566    * @returns {String}
567    */
568   DynamicProperty.prototype.$getDisplay = function () {
569     return this.$display;
570   };
571   
572   /**
573    * Set the display for this Property.
574    * <p>
575    * Please note, this method is intended for INTERNAL use by Tridium only. An
576    * external developer should never call this method.
577    *
578    * @name baja.DynamicProperty#$setDisplay
579    * @function
580    * @private
581    *
582    * @param {String} display the display String.
583    */
584   DynamicProperty.prototype.$setDisplay = function (display) {
585     this.$display = display;
586   };
587 
588   /**
589    * @class Action Slot.
590    * <p>
591    * Action is a Slot that defines a behavior which can
592    * be invoked on a Component.
593    * <p>
594    * A new object should never be directly created with this Constructor. All Slots are 
595    * created internally by BajaScript.
596    *
597    * @name baja.Action
598    * @extends baja.Slot
599    */
600   baja.Action = function (slotName, displayName) {
601     baja.Action.$super.call(this, slotName, displayName);
602   }.$extend(baja.Slot);
603      
604   /**
605    * Is the Slot an Action?
606    *
607    * @returns {Boolean}
608    */
609   baja.Action.prototype.isAction = function () {
610     return true;
611   };
612   
613   /**
614    * @class Topic Slot.
615    * <p>
616    * Topic defines a Slot which indicates an event that
617    * is fired on a Component.
618    * <p>
619    * A new object should never be directly created with this Constructor. All Slots are 
620    * created internally by BajaScript.
621    *
622    * @name baja.Topic
623    * @extends baja.Slot
624    */
625   baja.Topic = function (slotName, displayName) {
626     baja.Topic.$super.call(this, slotName, displayName);
627   }.$extend(baja.Slot);
628   
629   /** 
630    * Is the Slot a Topic?
631    *
632    * @returns {Boolean}
633    */   
634   baja.Topic.prototype.isTopic = function () {
635     return true;
636   };
637         
638   /**
639    * @class PropertyAction Slot.
640    * <p>
641    * A Property that is also an Action. Typically this is used for dynamic Actions.
642    * <p>
643    * A new object should never be directly created with this Constructor. All Slots are 
644    * created internally by BajaScript.
645    * 
646    * @name baja.PropertyAction
647    * @extends baja.DynamicProperty
648    */
649   var PropertyAction = function (slotName, displayName, display, flags, facets, value) {
650     PropertyAction.$super.apply(this, arguments);
651   }.$extend(DynamicProperty);
652    
653   /**
654    * Is the Slot an Action?
655    *
656    * @name baja.PropertyAction#isAction
657    * @function
658    *
659    * @returns {Boolean}
660    */   
661   PropertyAction.prototype.isAction = function () {
662     return true;
663   };
664   
665   /**
666    * Return the Action's parameter Type.
667    *
668    * @name baja.PropertyAction#getParamType
669    * @function
670    *
671    * @returns {Type} the parameter type (or null if the Action doesn't have a parameter).
672    */
673   PropertyAction.prototype.getParamType = function () {
674     return this.$val.getParamType();
675   };
676   
677   /**
678    * Return the Action's parameter default value.
679    *
680    * @name baja.PropertyAction#getParamDefault
681    * @function
682    *
683    * @returns the parameter default value (or null if the Action doesn't have a parameter).
684    */
685   PropertyAction.prototype.getParamDefault = function () {
686     return this.$val.getParamDefault();
687   };
688   
689   /**
690    * Return the Action's return Type.
691    *
692    * @name baja.PropertyAction#getReturnType
693    * @function
694    *
695    * @returns {Type} the return type (or null if the Action doesn't have a return Type).
696    */
697   PropertyAction.prototype.getReturnType = function () {
698     return this.$val.getReturnType();
699   }; 
700   
701   /**
702    * @class PropertyTopic Slot.
703    * <p>
704    * A Property that is also a Topic. Typically this is used for dynamic Topics.
705    * <p>
706    * A new object should never be directly created with this Constructor. All Slots are 
707    * created internally by BajaScript.
708    *
709    * @name baja.PropertyTopic
710    * @extends baja.DynamicProperty
711    */
712   var PropertyTopic = function (slotName, displayName, display, flags, facets, value) {
713     PropertyTopic.$super.apply(this, arguments);
714   }.$extend(DynamicProperty);
715   
716   /**
717    * Is the Property a Topic?
718    *
719    * @name baja.PropertyTopic#isTopic
720    * @function
721    *
722    * @returns {Boolean}
723    */
724   PropertyTopic.prototype.isTopic = function () {
725     return true;
726   };
727   
728   /**
729    * Return the Topic's event Type.
730    *
731    * @name baja.PropertyTopic#getEventType
732    * @function
733    *
734    * @returns {Type} the event Type (or null if the Topic has not event Type).
735    */
736   PropertyTopic.prototype.getEventType = function () {
737     return this.$val.getEventType();
738   }; 
739 
740   ////////////////////////////////////////////////////////////////
741   // Slot Cursor
742   //////////////////////////////////////////////////////////////// 
743   
744   /** 
745    * @class A Cursor used for Slot iteration.
746    *
747    * @name baja.SlotCursor
748    * @extends baja.FilterCursor
749    */
750   var SlotCursor = function () {
751     SlotCursor.$super.apply(this, arguments);
752   }.$extend(baja.FilterCursor);
753   
754   /**
755    * If the Slot is a Property, return its value (otherwise return null).
756    *
757    * @name baja.SlotCursor#getValue
758    * @function
759    *
760    * @returns a Property value.
761    */
762   SlotCursor.prototype.getValue = function () {
763     var slot = this.get();
764     return slot !== null && slot.isProperty() ? this.$context.get(slot) : null;
765   };
766   
767   /**
768    * If the Slot is a Property, return its display String (otherwise return null).
769    *
770    * @name baja.SlotCursor#getDisplay
771    * @function
772    *
773    * @returns {String} display String.
774    */
775   SlotCursor.prototype.getDisplay = function () {
776     var slot = this.get();
777     return slot !== null && slot.isProperty() ? this.$context.getDisplay(slot) : null;
778   };
779   
780   /**
781    * Return the first Property value in the cursor (regardless of iterative state).
782    *
783    * @name baja.SlotCursor#firstValue
784    * @function
785    *
786    * @returns first Property value found in the Cursor (or null if nothing found).
787    */
788   SlotCursor.prototype.firstValue = function () {
789     var slot = this.first();
790     return slot !== null && slot.isProperty() ? this.$context.get(slot) : null;
791   };
792   
793   /**
794    * Return the first Property display String in the cursor (regardless of iterative state).
795    *
796    * @name baja.SlotCursor#firstDisplay
797    * @function
798    *
799    * @returns first Property display String found in the Cursor (or null if nothing found).
800    */
801   SlotCursor.prototype.firstDisplay = function () {
802     var slot = this.first();
803     return slot !== null && slot.isProperty() ? this.$context.getDisplay(slot) : null;
804   };
805   
806   /**
807    * Return the last Property value in the cursor (regardless of iterative state).
808    *
809    * @name baja.SlotCursor#lastValue
810    * @function
811    *
812    * @returns first Property value found in the Cursor (or null if nothing found).
813    */
814   SlotCursor.prototype.lastValue = function () {
815     var slot = this.last();
816     return slot !== null && slot.isProperty() ? this.$context.get(slot) : null;
817   };
818   
819   /**
820    * Return the last Property display String in the cursor (regardless of iterative state).
821    *
822    * @name baja.SlotCursor#lastDisplay
823    * @function
824    *
825    * @returns first Property display String found in the Cursor (or null if nothing found).
826    */
827   SlotCursor.prototype.lastDisplay = function () {
828     var slot = this.last();
829     return slot !== null && slot.isProperty() ? this.$context.getDisplay(slot) : null;
830   };
831   
832   /**
833    * Iterate through the Cursor and call 'each' on every Property Slot and get its value.
834    * <p>
835    * When the function is called, 'this' refers to the associated Complex and the argument
836    * is the value of the Property.
837    * 
838    * @name baja.SlotCursor#eachValue
839    * @function
840    *
841    * @param {Function} func function called on every iteration with the argument being a Property's value.
842    */
843   SlotCursor.prototype.eachValue = function (func) {
844     return this.each(function (slot, i) {
845       if (slot.isProperty()) {
846         return func.call(this, this.get(slot), i);
847       }
848       
849       // Return false so nothing stops iterating
850       return false;
851     });
852   };
853   
854   /**
855    * Iterate through the Cursor and call 'each' on every Property Slot and get its display String.
856    * <p>
857    * When the function is called, 'this' refers to the associated Complex and the argument
858    * is the display String.
859    * 
860    * @name baja.SlotCursor#eachDisplay
861    * @function
862    *
863    * @param {Function} func function called on every iteration with the argument being a Property's display String
864    */
865   SlotCursor.prototype.eachDisplay = function (func) {
866     return this.each(function (slot, i) {
867       if (slot.isProperty()) {
868         return func.call(this, this.getDisplay(slot), i);
869       }
870       
871       // Return false so nothing stops iterating
872       return false;
873     });
874   };
875   
876   /**
877    * Return an array of Property values (regardless of iterative state).
878    *
879    * @name baja.SlotCursor#toValueArray
880    * @function
881    *
882    * @returns {Number}
883    */  
884   SlotCursor.prototype.toValueArray = function () {
885     var slots = this.toArray(),
886         values = [],
887         i;
888     
889     for (i = 0; i < slots.length; ++i) {
890       if (slots[i].isProperty()) {
891         values.push(this.$context.get(slots[i]));
892       }
893     }
894             
895     return values;
896   };
897   
898   /**
899    * Return an array of Property display Strings (regardless of iterative state).
900    *
901    * @name baja.SlotCursor#toDisplayArray
902    * @function
903    *
904    * @returns {Number}
905    */  
906   SlotCursor.prototype.toDisplayArray = function () {
907     var slots = this.toArray(),
908         displays = [],
909         i;
910     
911     for (i = 0; i < slots.length; ++i) {
912       if (slots[i].isProperty()) {
913         displays.push(this.$context.getDisplay(slots[i]));
914       }
915     }
916             
917     return displays;
918   };
919   
920   /**
921    * Return an Object Map of Property names with their corresponding values (regardless of iterative state).
922    *
923    * @name baja.SlotCursor#toValueMap
924    * @function
925    *
926    * @returns {Object}
927    */  
928   SlotCursor.prototype.toValueMap = function () {
929     var slots = this.toArray(),
930         map = {},
931         s,
932         i;
933     
934     for (i = 0; i < slots.length; ++i) {
935       s = slots[i];
936       if (s.isProperty()) {
937         map[s.getName()] = this.$context.get(s);
938       }
939     }
940             
941     return map;
942   };
943   
944   /**
945    * Return an Object Map of Property names with their corresponding display Strings (regardless of iterative state).
946    *
947    * @name baja.SlotCursor#toDisplayMap
948    * @function
949    *   
950    * @returns {Object}
951    */  
952   SlotCursor.prototype.toDisplayMap = function () {
953     var slots = this.toArray(),
954         map = {},
955         s,
956         i;
957     
958     for (i = 0; i < slots.length; ++i) {
959       s = slots[i];
960       if (s.isProperty()) {
961         map[s.getName()] = this.$context.getDisplay(s);
962       }
963     }
964             
965     return map;
966   };
967   
968   function slotCursorFrozen(slot) {
969     return slot.isFrozen();
970   }
971    
972   /**
973    * Adds a filter to the Cursor for frozen Slots.
974    * 
975    * @name baja.SlotCursor#frozen
976    * @function
977    *
978    * @returns {baja.SlotCursor} itself.
979    */
980   SlotCursor.prototype.frozen = function () {
981     this.filter(slotCursorFrozen);
982     return this;
983   };
984   
985   function slotCursorDynamic(slot) {
986     return !slot.isFrozen();
987   }
988   
989   /**
990    * Adds a filter to the Cursor for dynamic Slots.
991    * 
992    * @name baja.SlotCursor#dynamic
993    * @function
994    *
995    * @returns {baja.SlotCursor} itself.
996    */
997   SlotCursor.prototype.dynamic = function () {
998     this.filter(slotCursorDynamic);
999     return this;
1000   };
1001   
1002   function slotCursorProperties(slot) {
1003     return slot.isProperty();
1004   }
1005   
1006   /**
1007    * Adds a filter to the Cursor for Properties.
1008    * 
1009    * @name baja.SlotCursor#properties
1010    * @function
1011    *
1012    * @returns {baja.SlotCursor} itself.
1013    */
1014   SlotCursor.prototype.properties = function () {
1015     this.filter(slotCursorProperties);
1016     return this;
1017   };
1018   
1019   function slotCursorActions(slot) {
1020     return slot.isAction();
1021   }
1022   
1023   /**
1024    * Adds a filter to the Cursor for Actions.
1025    *
1026    * @name baja.SlotCursor#actions
1027    * @function
1028    * 
1029    * @returns {baja.SlotCursor} itself.
1030    */
1031   SlotCursor.prototype.actions = function () {
1032     this.filter(slotCursorActions);
1033     return this;
1034   };
1035   
1036   function slotCursorTopics(slot) {
1037     return slot.isTopic();
1038   }
1039   
1040   /**
1041    * Adds a filter to the Cursor for Topics.
1042    * 
1043    * @name baja.SlotCursor#topics
1044    * @function
1045    *
1046    * @returns {baja.SlotCursor} itself.
1047    */
1048   SlotCursor.prototype.topics = function () {
1049     this.filter(slotCursorTopics);
1050     return this;
1051   };
1052     
1053   /**
1054    * Adds a filter for Property values that match the TypeSpec via {@link Type#is}.
1055    * <p>
1056    * This method can take a variable number of TypeSpecs. If a variable number of TypeSpecs
1057    * are specified then a slot will be filtered through if any of the TypeSpecs match (logical OR).
1058    * 
1059    * @name baja.SlotCursor#is
1060    * @function
1061    * @see Type#is
1062    *
1063    * @param {Type|String} typeSpec the TypeSpec to test against.
1064    *
1065    * @returns {baja.SlotCursor} itself.
1066    */
1067   SlotCursor.prototype.is = function (typeSpec) {
1068     typeSpec = Array.prototype.slice.call(arguments);
1069     
1070     this.filter(function (slot) {
1071       if (slot.isProperty()) {
1072         var t = slot.getType(),
1073             i;
1074         
1075         for (i = 0; i < typeSpec.length; ++i) {
1076           if (t.is(typeSpec[i])) {
1077             return true;
1078           }
1079         }
1080       }
1081       return false;
1082     });
1083     
1084     return this;
1085   };
1086   
1087   function slotCursorIsValue(slot) {
1088     return slot.isProperty() && slot.getType().isValue();
1089   }
1090   
1091   /**
1092    * Adds a filter for Property values that are of Type baja:Value {@link Type#isValue}.
1093    * 
1094    * @name baja.SlotCursor#isValue
1095    * @function
1096    * @see Type#isValue
1097    *
1098    * @returns {baja.SlotCursor} itself.
1099    */
1100   SlotCursor.prototype.isValue = function () {
1101     this.filter(slotCursorIsValue);
1102     return this;
1103   };
1104   
1105   function slotCursorIsSimple(slot) {
1106     return slot.isProperty() && slot.getType().isSimple();
1107   }
1108   
1109   /**
1110    * Adds a filter for Property values that are of Type baja:Simple {@link Type#isSimple}.
1111    * 
1112    * @name baja.SlotCursor#isSimple
1113    * @function
1114    * @see Type#isSimple
1115    *
1116    * @returns {baja.SlotCursor} itself.
1117    */
1118   SlotCursor.prototype.isSimple = function () {
1119     this.filter(slotCursorIsSimple);
1120     return this;
1121   };
1122   
1123   function slotCursorIsNumber(slot) {
1124     return slot.isProperty() && slot.getType().isNumber();
1125   }
1126   
1127   /**
1128    * Adds a filter for Property values that are of Type baja:Number {@link Type#isNumber}.
1129    * 
1130    * @name baja.SlotCursor#isNumber
1131    * @function
1132    * @see Type#isNumber
1133    *
1134    * @returns {baja.SlotCursor} itself.
1135    */
1136   SlotCursor.prototype.isNumber = function () {
1137     this.filter(slotCursorIsNumber);
1138     return this;
1139   };
1140   
1141   function slotCursorIsComplex(slot) {
1142     return slot.isProperty() && slot.getType().isComplex();
1143   }
1144   
1145   /**
1146    * Adds a filter for Property values that are of Type baja:Complex {@link Type#isComplex}.
1147    * 
1148    * @name baja.SlotCursor#isComplex
1149    * @function
1150    * @see Type#isComplex
1151    *
1152    * @returns {baja.SlotCursor} itself.
1153    */
1154   SlotCursor.prototype.isComplex = function () {
1155     this.filter(slotCursorIsComplex);
1156     return this;
1157   };
1158   
1159   function slotCursorIsComponent(slot) {
1160     return slot.isProperty() && slot.getType().isComponent();
1161   }
1162   
1163   /**
1164    * Adds a filter for Property values that are of Type baja:Component {@link Type#isComponent}.
1165    * 
1166    * @name baja.SlotCursor#isComponent
1167    * @function
1168    * @see Type#isComponent
1169    *
1170    * @returns {baja.SlotCursor} itself.
1171    */
1172   SlotCursor.prototype.isComponent = function () {
1173     this.filter(slotCursorIsComponent);
1174     return this;
1175   };
1176   
1177   function slotCursorIsStruct(slot) {
1178     return slot.isProperty() && slot.getType().isStruct();
1179   }
1180   
1181   /**
1182    * Adds a filter for Property values that are of Type baja:Struct {@link Type#isStruct}.
1183    * 
1184    * @name baja.SlotCursor#isStruct
1185    * @function
1186    * @see Type#isStruct
1187    *
1188    * @returns {baja.SlotCursor} itself.
1189    */
1190   SlotCursor.prototype.isStruct = function () {
1191     this.filter(slotCursorIsStruct);
1192     return this;
1193   };
1194   
1195   /**
1196    * Adds a filter for Properties whose Type matches via equals. 
1197    * <p>
1198    * This method can take a variable number of TypeSpecs. If a variable number of TypeSpecs
1199    * are specified then a slot will be filtered through if any of the TypeSpecs match (logical OR).
1200    *
1201    * @name baja.SlotCursor#equalType
1202    * @function
1203    *
1204    * @param {Type|String|Array} typeSpec the TypeSpec to test against. 
1205    *
1206    * @returns {baja.SlotCursor} itself.
1207    */
1208   SlotCursor.prototype.equalType = function (typeSpec) {
1209     typeSpec = Array.prototype.slice.call(arguments);
1210     
1211     // Ensure we have the Types we're interested in
1212     var i;
1213     for (i = 0; i < typeSpec.length; ++i) {
1214       typeSpec[i] = typeof typeSpec[i] === "string" ? baja.lt(typeSpec[i]) : typeSpec[i];
1215     }
1216   
1217     this.filter(function (slot) {
1218       if (slot.isProperty()) {
1219         var t = slot.getType(),
1220             i;
1221         
1222         for (i = 0; i < typeSpec.length; ++i) {
1223           if (t.equals(typeSpec[i])) {
1224             return true;
1225           }
1226         }
1227       }
1228       return false;
1229     });
1230     
1231     return this;
1232   };
1233   
1234   /**
1235    * Adds a filter for Property values that match via equals. 
1236    * <p>
1237    * This method can take a variable number of values. If a variable number of values
1238    * are specified then a slot will be filtered through if any of the values match (logical OR).
1239    *
1240    * @name baja.SlotCursor#equalValue
1241    * @function
1242    *
1243    * @param value the value to be used for equals.
1244    *
1245    * @returns {baja.SlotCursor} itself.
1246    */
1247   SlotCursor.prototype.equalValue = function (value) {
1248     value = Array.prototype.slice.call(arguments);
1249     
1250     this.filter(function (slot) {
1251       if (slot.isProperty()) {
1252         var v = this.get(slot),
1253             i;
1254         
1255         for (i = 0; i < value.length; ++i) {
1256           if (v.equals(value[i])) {
1257             return true;
1258           }
1259         }
1260       }
1261       return false;
1262     });
1263     
1264     return this;
1265   };
1266   
1267   /**
1268    * Adds a filter for Property values that match via equivalent. 
1269    * <p>
1270    * This method can take a variable number of values. If a variable number of values
1271    * are specified then a slot will be filtered through if any of the values match (logical OR).
1272    *
1273    * @name baja.SlotCursor#equivalent
1274    * @function
1275    *
1276    * @param value the value to be used for equivalent.
1277    *
1278    * @returns {baja.SlotCursor} itself.
1279    */
1280   SlotCursor.prototype.equivalent = function (value) {
1281     value = Array.prototype.slice.call(arguments); 
1282     
1283     this.filter(function (slot) {
1284       if (slot.isProperty()) {
1285         var v = this.get(slot),
1286             i;
1287         
1288         for (i = 0; i < value.length; ++i) {
1289           if (v.equivalent(value[i])) {
1290             return true;
1291           }
1292         }
1293       }
1294       return false;
1295     });
1296     
1297     return this;
1298   };
1299   
1300   /**
1301    * Adds a filter for Slots that match the given Slot name.
1302    *
1303    * @name baja.SlotCursor#slotName
1304    * @function
1305    *
1306    * @param {String|RegEx} slotName a String or Regular Expression for matching Slots via name.
1307    *
1308    * @returns {baja.SlotCursor} itself.
1309    */
1310   SlotCursor.prototype.slotName = function (sName) {
1311     if (typeof sName === "string") {
1312       // String Comparison
1313       this.filter(function (slot) {
1314         return slot.getName().equals(sName.toString());
1315       });
1316     }
1317     else {
1318       // RegEx test
1319       this.filter(function (slot) {
1320         return sName.test(slot.getName());
1321       });
1322     }
1323     return this;
1324   };
1325     
1326   /**
1327    * Adds a filter for Slots that match the requested Slot Flags.
1328    *
1329    * @name baja.SlotCursor#flags
1330    * @function
1331    *
1332    * @see baja.Flags
1333    *
1334    * @param {Number} flgs the Slot flags to be tested for.
1335    *
1336    * @returns {baja.SlotCursor} itself.
1337    */
1338   SlotCursor.prototype.flags = function (flgs) {
1339     this.filter(function (slot) {
1340       return (this.getFlags(slot) & flgs);
1341     });
1342     return this;
1343   };
1344           
1345   ////////////////////////////////////////////////////////////////
1346   // Baja Components
1347   //////////////////////////////////////////////////////////////// 
1348   
1349   // Internal Component Event flags
1350   var CHANGED        = 0,
1351       ADDED          = 1,
1352       REMOVED        = 2,
1353       RENAMED        = 3,
1354       REORDERED      = 4,
1355       PARENTED       = 5,
1356       UNPARENTED     = 6,
1357       ACTION_INVOKED = 7,
1358       TOPIC_FIRED    = 8,
1359       FLAGS_CHANGED  = 9,
1360       FACETS_CHANGED = 10,
1361       RECATEGORIZED  = 11,
1362       KNOB_ADDED     = 12,
1363       KNOB_REMOVED   = 13,
1364       SUBSCRIBED     = 14,
1365       UNSUBSCRIBED   = 15;
1366   
1367   function applyObjToComplex(clx, obj) {
1368     var slotName,
1369         value,
1370         isComponent = clx.getType().isComponent();
1371     
1372     for (slotName in obj) {
1373       if (obj.hasOwnProperty(slotName)) {
1374         value = obj[slotName];
1375         if (clx.has(slotName)) {
1376           
1377           // If value is a Object Literal then recursively apply it
1378           if (value && value.constructor === Object) {
1379             var v = clx.get(slotName);
1380             applyObjToComplex(v, 
1381                               value);
1382           }
1383           else {
1384             clx.set({
1385               slot: slotName,
1386               value: value
1387             });
1388           }
1389         }
1390         else if (isComponent) {
1391           
1392           // If value is an Object Literal then recursively apply it 
1393           if (value && value.constructor === Object) {
1394             var oldValue = value;
1395             value = new baja.Component();
1396             applyObjToComplex(value, 
1397                               oldValue);
1398           }
1399           
1400           clx.add({
1401             slot: slotName,
1402             value: value
1403           });
1404         }
1405       }
1406     }
1407   }
1408   
1409   /**
1410    * @class Complex
1411    * <p>
1412    * Complex is the Value which is defined by one or more
1413    * property slots. Complex is never used directly, rather
1414    * it is the base class for Struct and Component.
1415    * <p>
1416    * Since Complex relates to a abstract Type, this Constructor should
1417    * never be directly used to create a new object.
1418    *
1419    * @see baja.Struct
1420    * @see baja.Component
1421    * 
1422    * @name baja.Complex
1423    * @extends baja.Value
1424    */
1425   baja.Complex = function () {
1426     baja.Complex.$super.apply(this, arguments);
1427     this.$map = new baja.OrderedMap();
1428     this.$parent = null;
1429     this.$propInParent = null; 
1430   }.$extend(baja.Value);
1431   
1432   /**
1433    * Called once the frozen Slots have been loaded onto the Complex.
1434    * 
1435    * @private
1436    */
1437   baja.Complex.prototype.contractCommitted = function (arg) {
1438     // If this is a complex and there was an argument then attempt
1439     // to set (of add if a Component) Properties...
1440     if (arg && arg.constructor === Object) {
1441       applyObjToComplex(this, arg);
1442     }
1443   };
1444   
1445   /**
1446    * Return the name of the Component.
1447    * <p>
1448    * The name is taken from the parent Component's Property for this Component instance.
1449    * 
1450    * @returns {String} name (null if not mounted).
1451    */
1452   baja.Complex.prototype.getName = function () { 
1453     return this.$propInParent === null ? null : this.$propInParent.getName();
1454   };
1455   
1456   /**
1457    * Return a display name.
1458    * <p>
1459    * If a Slot is defined as an argument, the display name for the slot will be returned. 
1460    * If not Slot is defined, the display name of the Complex will be returned.
1461    *
1462    * @param {baja.Slot|String} [slot]  the Slot or Slot name.
1463    *
1464    * @returns {String} the display name (or null if none available).
1465    */
1466   baja.Complex.prototype.getDisplayName = function (slot) {
1467     // If no Slot defined then get the display name of the Complex
1468     if (slot === undefined) {
1469       return this.getPropertyInParent() === null ? null : this.getParent().getDisplayName(this.getPropertyInParent());
1470     }
1471   
1472     slot = this.getSlot(slot);
1473     
1474     // Bail if this slot doesn't exist
1475     if (slot === null) {
1476       return null;
1477     }
1478     
1479     // This should be ok but just double check to ensure we have a display string
1480     var s = slot.$getDisplayName();
1481     if (typeof s !== "string") {
1482       s = "";
1483     }
1484     
1485     // If there is no display name then default to unescaping the slot name
1486     if (s === "") {
1487       s = baja.SlotPath.unescape(slot.getName());
1488     }
1489     
1490     return s;
1491   };
1492   
1493   /**
1494    * Return a display string.
1495    * <p>
1496    * If a Slot argument is defined, the display name for the Slot will be returned. 
1497    * If a Slot argument is not defined, the display name for the Complex will be returned.
1498    * <p>
1499    * Note that when an instance of a Complex is created, auto-generated accessors are
1500    * created to make accessing a frozen Slot's display string convenient...
1501    * <pre>
1502    *   // myPoint has a Property named out...
1503    *   baja.outln("The display string of the out Property: " + myPoint.getOutDisplay());
1504    * </pre>
1505    * The auto-generated accessor is in the format of <code>'get(first letter is captialized)SlotNameDisplay()'</code>
1506    * <p>
1507    * If the name of an automatically generated method is already used in the Complex, a number will be added to the function name.
1508    *
1509    * @param {baja.Slot|String} [slot]  the Slot or Slot name.
1510    *
1511    * @returns {String} display (or null if none available).
1512    */
1513   baja.Complex.prototype.getDisplay = function (slot) {
1514     if (slot === undefined) {
1515       return this.getPropertyInParent() === null ? null : this.getPropertyInParent().$getDisplay();
1516     }
1517     slot = this.getSlot(slot);
1518     return slot === null ? null : slot.$getDisplay();  
1519   };
1520       
1521   /**
1522    * Return the String representation.
1523    *
1524    * @returns {String}
1525    */
1526   baja.Complex.prototype.toString = function () {
1527     var str = this.getDisplay(); 
1528     return typeof str === "string" ? str : this.getType().toString();
1529   };
1530   
1531   /**
1532    * Return the parent.
1533    *
1534    * @returns parent
1535    */
1536   baja.Complex.prototype.getParent = function () {
1537     return this.$parent;
1538   };
1539   
1540   /**
1541    * Return the Property in the parent.
1542    *
1543    * @returns {baja.Property} the Property in the parent (null if not mounted).
1544    */
1545   baja.Complex.prototype.getPropertyInParent = function () {
1546     return this.$propInParent;
1547   };
1548   
1549   /**
1550    * Return the Slot.
1551    * <p>
1552    * This is useful method to ensure you have the Slot instance instead of the Slot name String.
1553    * If a Slot is passed in, it will simply be checked and returned.
1554    *
1555    * @param {baja.Slot|String} slot the Slot or Slot name.
1556    * @returns {baja.Slot} the Slot for the Component (or null if the Slot doesn't exist).
1557    */
1558   baja.Complex.prototype.getSlot = function (slot) {
1559     if (typeof slot === "string") {
1560       return this.$map.get(slot);
1561     }
1562     else {
1563       strictArg(slot, baja.Slot);
1564       return this.$map.get(slot.getName());
1565     }
1566   };
1567       
1568   /**
1569    * Return a Cursor for accessing a Complex's Slots.
1570    * <p>
1571    * Please see {@link baja.SlotCursor} for useful builder methods. For example...
1572    * <pre>
1573    *   // A Cursor for Dynamic Properties
1574    *   var frozenPropCursor = myComp.getSlots().dynamic().properties();
1575    *
1576    *   // A Cursor for Frozen Actions
1577    *   var frozenPropCursor = myComp.getSlots().frozen().actions();
1578    *
1579    *   // An Array of Control Points
1580    *   var valArray = myComp.getSlots().properties().is("control:ControlPoint").toValueArray();
1581    *
1582    *   // An Array of Action Slots
1583    *   var actionArray = myComp.getSlots().actions().toArray();
1584    *
1585    *   // An Object Map of slot name/value pairs
1586    *   var map = myComp.getSlots().properties().toMap();
1587    * 
1588    *   // The very first dynamic Property
1589    *   var firstProp = myComp.getSlots().dynamic().properties().first();
1590    *
1591    *   // The very last dynamic Property
1592    *   var lastProp = myComp.getSlots().dynamic().properties().last();
1593    *
1594    *   // The very first dynamic Property value
1595    *   var firstVal = myComp.getSlots().dynamic().properties().firstValue();
1596    *
1597    *   // The very first dynamic Property value
1598    *   var lastVal = myComp.getSlots().dynamic().properties().lastValue();
1599    *
1600    *   // All the Slots that start with the name 'foo'
1601    *   var slotNameCursor = myComp.getSlots().slotName(/^foo/);
1602    *
1603    *   // Use a custom Cursor to find all of the Slots that have a particular facets key/value
1604    *   var custom = myComp.getSlots(function (slot) {
1605    *      return slot.isProperty() && (this.getFacets(slot).get("myKey", "def") === "foo");
1606    *   });
1607    * 
1608    *   // Same as above
1609    *   var custom2 = myComp.getSlots().filter(function (slot) {
1610    *      return slot.isProperty() && (this.getFacets(slot).get("myKey", "def") === "foo");
1611    *   });
1612    *   
1613    *   // All Slots marked summary on the Component
1614    *   var summarySlotCursor = myComp.getSlots().flags(baja.Flags.SUMMARY);
1615    *
1616    *   // Call function for each Property that's a ControlPoint
1617    *   myComp.getSlots().is("control:ControlPoint").each(function (slot) {
1618    *     baja.outln("The Nav ORD for the ControlPoint: " + this.get(slot).getNavOrd();
1619    *   });
1620    * </pre>
1621    *
1622    * @param {Function} [filter]  function to filter out the Slots we're not interested in.
1623    *                             The filter function will be passed each Slot to see if it should be
1624    *                             be included. The function must return false to filter out a value and true
1625    *                             to keep it.
1626    *
1627    * @returns {SlotCursor} a Cursor for iterating through the Complex's Slots.
1628    */
1629   baja.Complex.prototype.getSlots = function (filter) {
1630     var cursor = this.$map.getCursor(this, SlotCursor);
1631     if (filter) {
1632       cursor.filter(filter);
1633     }
1634     return cursor;
1635   };
1636   
1637   /**
1638    * Return Flags for a slot or for the Complex's parent Property.
1639    * <p>
1640    * If no arguments are provided and the Complex has a parent, the 
1641    * flags for the parent's Property will be returned. 
1642    *
1643    * @see baja.Flags
1644    *
1645    * @param {baja.Slot|String} [slot] Slot or Slot name.
1646    * @returns {Number} the flags for the Slot or the parent's Property flags.
1647    */
1648   baja.Complex.prototype.getFlags = function (slot) { 
1649     // If no arguments are specified then attempt to get parent properly slot Flags
1650     if (arguments.length === 0) {
1651       if (this.$parent !== null && this.$propInParent !== null) {
1652         return this.$parent.getFlags(this.$propInParent);
1653       }
1654       else {
1655         throw new Error("Complex has no parent");
1656       }
1657     }
1658   
1659     slot = this.getSlot(slot); 
1660     if (slot === null) {
1661       throw new Error("Slot doesn't exist: " + slot);
1662     }
1663     return slot.getFlags();
1664   };
1665     
1666   /**
1667    * Return a Property's value.
1668    * <p>
1669    * Note that when an instance of a Complex is created, auto-generated accessors are
1670    * created to make accessing a frozen Property's value convenient...
1671    * <pre>
1672    *   // myPoint has a Property named out...
1673    *   var val = myPoint.getOut();
1674    * </pre>
1675    * The auto-generated accessor is in the format of <code>'get(first letter is captialized)SlotName()'</code>.
1676    * <p>
1677    * If the name of an automatically generated method is already used in the Complex, a number will be added to the function name.
1678    *
1679    * @param {baja.Property|String} prop the Property or Property name.
1680    * @returns the value for the Property (null if the Property doesn't exist).
1681    */
1682   baja.Complex.prototype.get = function (prop) {
1683     prop = this.getSlot(prop);
1684     if (prop === null) {
1685       return null;
1686     }
1687     return prop.$getValue();
1688   };
1689   
1690   /**
1691    * Return true if the Slot exists.
1692    *
1693    * @param {baja.Property|String} prop the Property or Property name
1694    * @returns {Boolean}
1695    */
1696   baja.Complex.prototype.has = function (prop) {
1697     return this.getSlot(prop) !== null;
1698   };
1699   
1700   /**
1701    * Return the result of 'valueOf' on the specified Property's value.
1702    * If valueOf is not available then the Property's value is returned.
1703    *
1704    * @see baja.Complex#get
1705    *
1706    * @param {baja.Property|String} prop the Property or Property name.
1707    * @returns the valueOf for the Property's value or the Property's value
1708    *         (null if the Property doesn't exist).
1709    */
1710   baja.Complex.prototype.getValueOf = function (prop) {
1711     var v = this.get(prop);
1712     if (v !== null && typeof v.valueOf === "function") {
1713       return v.valueOf();
1714     }
1715     else {
1716       return v;
1717     }
1718   };
1719   
1720   function syncStruct(fromVal, toVal) {
1721     fromVal.getSlots().properties().each(function (fromProp) {
1722       var toProp = toVal.getSlot(fromProp.getName());
1723       
1724       // Sync value display and slot display name
1725       fromProp.$setDisplay(toProp.$getDisplay());
1726       fromProp.$setDisplayName(toProp.$getDisplayName());
1727       
1728       if (fromProp.getType().isStruct()) {
1729         // If another struct then sync appropriately
1730         syncStruct(fromProp.$getValue(), toProp.$getValue());
1731       }  
1732       else {
1733         // If a simple then directly set the value
1734         fromProp.$setValue(toProp.$getValue());
1735       }      
1736     });
1737   }
1738         
1739   /**
1740    * Set a Property's value.
1741    * <p>
1742    * If the Complex is mounted, this will <strong>asynchronously</strong> set the Properties
1743    * value on the Server. 
1744    * <p> 
1745    * An Object Literal is used to specify the method's arguments...
1746    * <pre>
1747    *   myObj.set({
1748    *     slot: "outsideAirTemp",
1749    *     value: 23.5,
1750    *     ok: function () {
1751    *       // Called once value has been set on the Server (optional)
1752    *     },
1753    *     fail: function (err) {
1754    *       // Called if the value fails to set on the Server (optional)
1755    *     },
1756    *     batch // if defined, any network calls will be batched into this object (optional)
1757    *   });
1758    * </pre>
1759    * <p>
1760    * Note that when an instance of a Complex is created, auto-generated setters are
1761    * created to make setting a frozen Property's value convenient...
1762    * <pre>
1763    *   // myPoint has a Property named outsideAirTemp...
1764    *   myObj.setOutsideAirTemp(23.5);
1765    *   
1766    *   // ... or via an Object Literal if more arguments are needed...
1767    *   
1768    *   myObj.setOutsideAirTemp({
1769    *     value: 23.5,
1770    *     ok: function () {
1771    *       // Called once value has been set on the Server (optional)
1772    *     }
1773    *   });
1774    * </pre>
1775    * The auto-generated setter is in the format of <code>'set(first letter is captialized)SlotName(...)'</code>.
1776    * <p>
1777    * If the name of an automatically generated method is already used in the Complex, a number will be added to the function name.
1778    * <p>
1779    * For callbacks, the 'this' keyword is set to the parent Component instance (if the Component is available).
1780    *
1781    * @param {Object} obj  the Object Literal for the method's arguments.
1782    * @param {baja.Property|String} obj.slot  the Property or Property name the value will be set on.
1783    * @param obj.value  the value being set (Type must extend baja:Value).
1784    * @param {Function} [obj.ok] the ok function callback. Called once network call has succeeded on the Server.
1785    * @param {Function} [obj.fail] the fail function callback. Called if this method has an error.
1786    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
1787    * @param [obj.cx]  the Context (used internally by BajaScript).
1788    */
1789   baja.Complex.prototype.set = function (obj) {    
1790     obj = objectify(obj);
1791     
1792     var cb = new Callback(obj.ok, obj.fail, obj.batch),   
1793         prop = obj.slot,
1794         val = obj.value,
1795         cx = bajaDef(obj.cx, null),
1796         serverDecode = cx && cx.serverDecode,
1797         commit = cx && cx.commit,
1798         syncStructVals = cx && cx.syncStructVals,
1799         comp = this;     // Ensure 'this' is the Component in the ok and fail callback...
1800     
1801     // Find the top level Component
1802     while (comp !== null && !comp.getType().isComponent()) {
1803       comp = comp.getParent();
1804     }
1805     
1806     cb.addOk(function (ok, fail, resp) {
1807       if (comp !== null) {
1808         ok.call(comp, resp);
1809       }
1810       else {
1811         ok(resp);
1812       }
1813     });
1814   
1815     cb.addFail(function (ok, fail, err) {
1816       if (comp !== null) {
1817         fail.call(comp, err);
1818       }
1819       else {
1820         fail(err);
1821       }
1822     });
1823             
1824     try {
1825       prop = this.getSlot(prop);
1826       
1827       // If decoding from the Server then short circuit some of this
1828       if (!serverDecode) {        
1829         // Validate arguments
1830         strictArg(prop, baja.Property);
1831         strictArg(val);
1832         
1833         if (prop === null) {
1834           throw new Error("Could not find Property: " + obj.slot);
1835         }
1836         if (!baja.hasType(val)) {
1837           throw new Error("Can only set BValue Types as Component Properties");
1838         }
1839         if (val.getType().isAbstract()) {
1840           throw new Error("Cannot set value in Complex to Abstract Type: " + val.getType());
1841         }
1842         if (val.getType().isNumber() && prop.getType().isNumber() && !val.getType().equals(prop.getType())) {
1843           // Recreate the number with the correct boxed type if the type spec differs
1844           val = prop.getType().getInstance().constructor.make(val.valueOf()); 
1845         }
1846         if (!val.getType().isValue()) {
1847           throw new Error("Cannot set non Value Types as Properties in a Complex");
1848         }
1849         if (val === this) {
1850           throw new Error("Illegal argument: this === value");
1851         } 
1852       }  
1853       
1854       if (cx) {
1855         if (typeof cx.displayName === "string") {
1856           prop.$setDisplayName(cx.displayName);
1857         }
1858         if (typeof cx.display === "string") {
1859           prop.$setDisplay(cx.display);
1860         }
1861       }
1862       
1863       if (val.equals(prop.$getValue())) {
1864         // TODO: May need to check for mounted on Components here
1865         cb.ok();
1866         return;
1867       }
1868             
1869       // Return if this set is trapped. If the set is trapped then the set operation will
1870       // be proxied off to a remote Space elsewhere...
1871       if (!commit && this.$fw("modifyTrap", [prop], val, cb, cx)) {
1872         return;
1873       }
1874                   
1875       // Unparent    
1876       var isClx = val.getType().isComplex();
1877       if (isClx) {    
1878         if (val.getParent()) {
1879           throw new Error("Complex already parented: " + val.getType());
1880         } 
1881       
1882         val.$parent = null;
1883         val.$propInParent = null;     
1884       }
1885             
1886       // If this is the same Struct from a Server decode then attempt to sync it
1887       // rather than completely replace it...
1888       if (syncStructVals && val.getType().isStruct() && val.getType().equals(prop.getType())) {
1889         syncStruct(/*from*/prop.$getValue(), /*to*/val);
1890       }
1891       else {  
1892         // Set new Property value
1893         prop.$setValue(val);
1894           
1895         // Parent
1896         if (isClx) {     
1897           val.$parent = this;
1898           val.$propInParent = prop;
1899           
1900           // If we have a Component then attempt to mount it
1901           if (val.getType().isComponent() && this.isMounted()) {  
1902             this.$space.$fw("mount", val);      
1903           }
1904         }
1905       }      
1906       
1907       // Invoke modified event (this will bubble up to a Component for the changed callback etc).
1908       this.$fw("modified", prop, cx);
1909       
1910       // TODO: Modified a link. Need to set up Knobs?
1911       cb.ok();
1912     }
1913     catch (err) {
1914       cb.fail(err);
1915     }
1916   };
1917   
1918   /**
1919    * Load all of the Slots on the Complex.
1920    *
1921    * @param {Object} [obj] the Object Literal for the method's arguments.
1922    * @param {Function} [obj.ok] the ok function callback. Called once network call has succeeded on the Server.
1923    * @param {Function} [obj.fail] the fail function callback. Called if this method has an error.
1924    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
1925    *
1926    * @see baja.Component#loadSlots
1927    */
1928   baja.Complex.prototype.loadSlots = function (obj) {
1929     if (obj && obj.ok && typeof obj.ok === "function") {
1930       obj.ok.call(this);
1931     }
1932   };
1933     
1934   /**
1935    * Return the Facets for a Slot.
1936    * <p>
1937    * If no arguments are provided and the Complex has a parent, the 
1938    * facets for the parent's Property will be returned. 
1939    *
1940    * @param {baja.Slot|String} [slot]  the Slot or Slot name.
1941    * @returns {baja.Facets} the Facets for the Slot (or null if Slot not found) or
1942    *                       the parent's Property facets.
1943    */
1944   baja.Complex.prototype.getFacets = function (slot) {
1945     // If no arguments are specified then attempt to get parent properly slot Flags
1946     if (arguments.length === 0) {
1947       if (this.$parent !== null && this.$propInParent !== null) {
1948         return this.$parent.getFacets(this.$propInParent);
1949       }
1950       else {
1951         return null;
1952       }
1953     }
1954   
1955     slot = this.getSlot(slot);
1956     return slot === null ? null : slot.getFacets();
1957   };
1958     
1959   /**
1960    * Compare if all of this object's properties are equal to the specified object.
1961    *
1962    * @param obj
1963    * @returns {Boolean} true if this object is equivalent this the specified object.
1964    */
1965   baja.Complex.prototype.equivalent = function (obj) { 
1966     if (!baja.hasType(obj)) {
1967       return false;
1968     }
1969   
1970     if (!obj.getType().equals(this.getType())) {
1971       return false;
1972     }
1973     
1974     if (this.$map.getSize() !== obj.$map.getSize()) {
1975       return false;
1976     }
1977     
1978     // Only compare against Properties
1979     var props = obj.getSlots().properties(),
1980         p,
1981         val;
1982     
1983     while (props.next()) {
1984       p = props.get();
1985       
1986       // Always check flags
1987       if (!this.getFlags(p.getName()).equals(obj.getFlags(p))) {
1988         return false;
1989       }
1990       
1991       val = obj.get(p);
1992       
1993       if (val === null) {
1994         return false;
1995       }
1996       
1997       // Compare Property values
1998       if (!val.equivalent(this.get(p.getName()))) {
1999         return false;
2000       }
2001       
2002       // Ensure they're the same order in the SlotMap
2003       if (this.$map.getIndex(p.getName()) !== props.getIndex()) {
2004         return false;
2005       }
2006     }
2007        
2008     return true;
2009   };
2010   
2011   var copyingContext = { type: "copying" };
2012   
2013   /**
2014    * Create a clone of this Complex.
2015    * <p>
2016    * If the exact argument is true and this Complex is a Component then
2017    * the defaultOnClone and removeOnClone flags will be ignored.
2018    *
2019    * @param {Boolean} [exact] flag to indicate whether to create an exact copy (false by default).
2020    * @returns cloned Complex.
2021    */
2022   baja.Complex.prototype.newCopy = function (exact) {
2023     var newInstance = this.getType().getInstance(), 
2024         props = this.getSlots().properties(),    
2025         p,
2026         val;
2027     
2028     while (props.next()) {
2029       p = props.get();
2030        
2031       // If default on clone and it's not an exact copy then skip this Property
2032       if (!exact && (baja.Flags.DEFAULT_ON_CLONE & p.getFlags()) === baja.Flags.DEFAULT_ON_CLONE) {
2033         continue;
2034       }
2035       
2036       // Make a copy of the Property value
2037       val = this.get(p).newCopy(exact);
2038       
2039       if (p.isFrozen()) {
2040         newInstance.set({
2041           "slot": p.getName(), 
2042           "value": val,  
2043           "cx": copyingContext
2044         });
2045       }
2046       else {
2047         // If remove on clone and it's not an exact copy then skip copying this Property
2048         if (!exact && (baja.Flags.REMOVE_ON_CLONE & p.getFlags()) === baja.Flags.REMOVE_ON_CLONE) {
2049           continue;
2050         }
2051       
2052         // TODO: Skip BLinks, dynamic slots added in constructor and slots with removeOnClone set
2053         p = newInstance.add({
2054           "slot": p.getName(), 
2055           "value": val, 
2056           "flags": p.getFlags(), 
2057           "facets": p.getFacets(), 
2058           "cx": copyingContext
2059         });
2060       }
2061       
2062       // Copy of flags
2063       newInstance.getSlot(p.getName()).$setFlags(p.getFlags());
2064     }
2065     
2066     return newInstance;
2067   };
2068   
2069     
2070   /**
2071    * Internal framework method.
2072    * <p>
2073    * This method should only be used by Tridium developers. It follows
2074    * the same design pattern as Niagara's Component 'fw' method.
2075    *
2076    * @private
2077    */
2078   baja.Complex.prototype.$fw = function (x, a, b, c, d) {    
2079     if (x === "modified") {
2080       if (this.$parent !== null) {
2081         this.$parent.$fw(x, this.$propInParent, b, c, d);
2082       }
2083     }
2084     else if (x === "modifyTrap") {
2085       if (this.$parent !== null) {
2086         a.push(this.$propInParent);
2087         return this.$parent.$fw(x, a, b, c, d);
2088       }
2089       else {
2090         return false;
2091       }
2092     }
2093   };
2094       
2095   /**
2096    * @class Represents a baja:Struct in BajaScript.
2097    * <p>
2098    * Struct is the base class for a component which has
2099    * one or more properties.  Structs must only declare
2100    * properties which are typed as boolean, int, float,
2101    * String, Simple, or other Structs.  This means
2102    * that a Struct may never have a Component property.
2103    * Structs only support Property slots, never Actions or Topics.
2104    * <p>
2105    * A Struct can only contain frozen Slots. A frozen Slot is defined
2106    * at compile time (usually hard coded in Java).
2107    *
2108    * @see baja.Component
2109    *
2110    * @name baja.Struct
2111    * @extends baja.Complex
2112    */
2113   baja.Struct = function () {  
2114     baja.Struct.$super.apply(this, arguments);  
2115   }.$extend(baja.Complex).registerType("baja:Struct");
2116   
2117   /**
2118    * @class Represents a baja:Action in BajaScript.
2119    * <p>
2120    * Please note: this represents the Property's value and NOT the Property itself.
2121    *
2122    * @name baja.ActionProperty
2123    * @extends baja.Struct
2124    */
2125   baja.ActionProperty = function () {
2126     baja.ActionProperty.$super.apply(this, arguments); 
2127     this.$paramType = null;
2128     this.$paramDef = null;
2129     this.$returnType = null;    
2130   }.$extend(baja.Struct).registerType("baja:Action");
2131   
2132   /**
2133    * Return the Action's parameter Type.
2134    *
2135    * @returns {Type} parameter type (or null if the Action has no parameter).
2136    */   
2137   baja.ActionProperty.prototype.getParamType = function () {
2138     return this.$paramType;
2139   };
2140   
2141   /**
2142    * Return the Action's parameter default value.
2143    *
2144    * @returns parameter default value (or null if the Action has no parameter).
2145    */
2146   baja.ActionProperty.prototype.getParamDefault = function () {
2147     return this.$paramDef;
2148   };
2149   
2150   /**
2151    * Return the Action's return Type.
2152    *
2153    * @returns return type (or null if the Action has nothing to return).
2154    */
2155   baja.ActionProperty.prototype.getReturnType = function () {
2156     return this.$returnType;
2157   };
2158   
2159   /**
2160    * Called when the Action Property is invoked.
2161    *
2162    * @private
2163    *
2164    * @param target the Component target the Action is being invoked upon.
2165    * @param arg the argument for the Action.
2166    * @param cx the Context for the Action invocation (could be null).
2167    * @returns the Action's return value (null if nothing to return).
2168    */
2169   baja.ActionProperty.prototype.invoke = function (target, arg, cx) {
2170     return null;
2171   };
2172   
2173   /**
2174    * @class Represents a baja:Topic in BajaScript.
2175    * <p>
2176    * Please note: this represents the Property's value and not the Property itself.
2177    *
2178    * @name baja.TopicProperty
2179    * @extends baja.Struct
2180    */
2181   baja.TopicProperty = function () {
2182     baja.TopicProperty.$super.apply(this, arguments); 
2183     this.$eventType = null; 
2184   }.$extend(baja.Struct).registerType("baja:Topic");
2185   
2186   /**
2187    * Return the Topic's event Type.
2188    *
2189    * @returns {Type} event Type (or null if the Topic has no event Type).
2190    */   
2191   baja.TopicProperty.prototype.getEventType = function () {
2192     return this.$eventType;
2193   };
2194   
2195   /**
2196    * Called when the Topic Property is fired.
2197    * 
2198    * @private
2199    *
2200    * @param target the Component target the Topic is being fired upon.
2201    * @param event the event for the Topic.
2202    * @param cx the Context for the Topic being fired (could be null).
2203    */  
2204   baja.TopicProperty.prototype.fire = function (target, event, cx) {
2205   };
2206            
2207   /**
2208    * @class Represents a baja:Component in BajaScript.
2209    * <p>
2210    * Component is the required base class for all
2211    * Baja component classes.
2212    * <p> 
2213    * Just like Niagara, 'baja:Component' contains a lot of the core functionality of the framework.
2214    * Unlike 'baja:Struct', a Component can contain both frozen and dynamic Slots. Frozen Slots are
2215    * defined at compile time (typically hard coded in Java) and Dynamic Slots can be added at
2216    * runtime (i.e. when the Station is running). There are three different types of Slots that 
2217    * a Component can contain (Property, Action and Topic).
2218    *
2219    * @see baja.Struct
2220    * @see baja.Property
2221    * @see baja.Action
2222    * @see baja.Topic
2223    *
2224    * @name baja.Component
2225    * @extends baja.Complex
2226    */  
2227   baja.Component = function () {
2228     baja.Component.$super.apply(this, arguments);
2229     this.$space = null;
2230     this.$handle = null;
2231     this.$bPropsLoaded = false;
2232     this.$subs = [];
2233     this.$lease = false;
2234     this.$leaseTicket = baja.clock.expiredTicket;
2235     this.$knobs = null;
2236     this.$permissionsStr = null;
2237     this.$permissions = null;
2238   }.$extend(baja.Complex).registerType("baja:Component");
2239     
2240   // This is a generic component event handling function
2241   // that can route events to Component or Subscriber event handlers
2242   function handleComponentEvent(component, handlers, id, slot, obj, str, cx) {
2243     var error = baja.error;
2244   
2245     if (id === CHANGED) {
2246       handlers.fireHandlers("changed", error, component, slot, cx);     
2247     }
2248     else if (id === ADDED) {
2249       handlers.fireHandlers("added", error, component, slot, cx);
2250     }
2251     else if (id === REMOVED) {
2252       handlers.fireHandlers("removed", error, component, slot, obj, cx);      
2253     }
2254     else if (id === RENAMED) {
2255       handlers.fireHandlers("renamed", error, component, slot, str, cx);
2256     }
2257     else if (id === REORDERED) {
2258       handlers.fireHandlers("reordered", error, component, cx);
2259     }
2260     else if (id === TOPIC_FIRED) {
2261       handlers.fireHandlers("topicFired", error, component, slot, obj, cx);         
2262     }
2263     else if (id === FLAGS_CHANGED) {
2264       handlers.fireHandlers("flagsChanged", error, component, slot, cx);
2265     }
2266     else if (id === FACETS_CHANGED) {
2267       handlers.fireHandlers("facetsChanged", error, component, slot, cx);
2268     }
2269     else if (id === SUBSCRIBED) {
2270       handlers.fireHandlers("subscribed", error, component, cx);
2271     }
2272     else if (id === UNSUBSCRIBED) {
2273       handlers.fireHandlers("unsubscribed", error, component, cx);
2274     }
2275     else if (id === KNOB_ADDED) {
2276       handlers.fireHandlers("addKnob", error, component, slot, obj, cx);
2277     }
2278     else if (id === KNOB_REMOVED) {
2279       handlers.fireHandlers("removeKnob", error, component, slot, obj, cx);
2280     }
2281   }  
2282   
2283   // Handle Component child events
2284   function handleComponentChildEvent(component, handlers, id, str, cx) {
2285     var error = baja.error;
2286     if (id === RENAMED) {
2287       handlers.fireHandlers("componentRenamed", error, component, str, cx);
2288     }
2289     else if (id === FLAGS_CHANGED) {
2290       handlers.fireHandlers("componentFlagsChanged", error, component, cx);
2291     }
2292     else if (id === FACETS_CHANGED) {
2293       handlers.fireHandlers("componentFacetsChanged", error, component, cx);
2294     }
2295   }
2296   
2297   // Handler Reorder Component Child Events  
2298   function handleReorderComponentChildEvent(component, handlers, cx) {
2299     handlers.fireHandlers("componentReordered", baja.error, component, cx);
2300   }
2301     
2302   function fwCompEvent(comp, id, slot, obj, str, cx) {             
2303     
2304     if (comp.isSubscribed() || id === UNSUBSCRIBED) {  
2305       // First support framework callback
2306       // TODO: Commented out for now for improved performance. Are these really needed?
2307       /*
2308       try {
2309         if (id === CHANGED) {
2310           comp.$fw("changed", slot, cx);   
2311         }
2312         else if (id === ADDED) {
2313           comp.$fw("added", slot, cx);   
2314         }
2315         else if (id === REMOVED) {   
2316           comp.$fw("removed", slot, obj, cx);   
2317         }
2318         else if (id === RENAMED) {
2319           comp.$fw("renamed", slot, str, cx);
2320         }
2321         else if (id === REORDERED) {
2322           comp.$fw("reordered", cx);
2323         }
2324         else if (id === TOPIC_FIRED) {
2325           comp.$fw("fired", slot, obj, cx);   
2326         }
2327         else if (id === SUBSCRIBED) {
2328           comp.$fw("subscribed", cx);
2329         }
2330         else if (id === UNSUBSCRIBED) {
2331           comp.$fw("unsubscribed", cx);
2332         }
2333         else if (id === KNOB_ADDED) {
2334           comp.$fw("knobAdded", obj, cx);
2335         }
2336         else if (id === KNOB_REMOVED) {
2337           comp.$fw("knobRemoved", obj, cx);
2338         }
2339       }
2340       catch (e) {
2341         error(e);
2342       }
2343       */
2344       
2345       // Route to event handlers on the Component
2346       if (comp.hasHandlers()) {
2347         handleComponentEvent(comp, comp, id, slot, obj, str, cx);
2348       }
2349       
2350       // Route to Subscribers if there are any registered
2351       if (comp.$subs.length > 0) {  
2352         // Route to all registered Subscribers
2353         var i;
2354         for (i = 0; i < comp.$subs.length; ++i) {
2355           // Route to event handlers on the Subscriber  
2356           if (comp.$subs[i].hasHandlers()) {          
2357             handleComponentEvent(comp, comp.$subs[i], id, slot, obj, str, cx);            
2358           }
2359         }
2360       }
2361     }
2362                    
2363     var targetVal = null;
2364     if (id === RENAMED) {
2365       if (slot && slot.isProperty()) {
2366         targetVal = comp.get(slot);
2367       }
2368     }
2369     else if (id === FLAGS_CHANGED || 
2370              id === FACETS_CHANGED) {
2371       if (slot && slot.isProperty()) {
2372         targetVal = comp.get(slot);
2373       }
2374     }
2375     
2376     // Route to child Component
2377     if (targetVal !== null && targetVal.getType().isComponent() && targetVal.isSubscribed()) {
2378       // Route to event handlers on the Component
2379       handleComponentChildEvent(targetVal, targetVal, id, str, cx);
2380       
2381       // Route to Subscribers if there are any registered
2382       if (targetVal.$subs.length > 0) {  
2383         // Route to all registered Subscribers
2384         var x;
2385         for (x = 0; x < targetVal.$subs.length; ++x) {
2386           // Route to event handlers on the Subscriber      
2387           handleComponentChildEvent(targetVal, targetVal.$subs[x], id, str, cx);
2388         }
2389       }
2390     }
2391         
2392     // Special case for child reordered Component events. We need to route to all child Components on the target Component
2393     if (id === REORDERED) {
2394       comp.getSlots(function (slot) {
2395         return slot.isProperty() && slot.getType().isComponent() && this.get(slot).isSubscribed();
2396       }).each(function (slot) {
2397         // Route reordered event
2398         var component = this.get(slot);
2399         handleReorderComponentChildEvent(component, component, cx);     
2400         if (component.$subs.length > 0) { 
2401           var i;
2402           for (i = 0; i < component.$subs.length; ++i) {
2403             // Route to event handlers on the Subscriber      
2404             handleReorderComponentChildEvent(component, component.$subs[i], cx);
2405           }
2406         }
2407       });
2408     }
2409     
2410     // NavEvents
2411     if (baja.nav.hasHandlers() &&
2412          comp.isMounted() && 
2413         ((id === ADDED && slot.getType().isComponent()) ||
2414          (id === REMOVED && obj.getType().isComponent()) ||
2415          (id === RENAMED && slot.getType().isComponent()) ||
2416          id === REORDERED)) {
2417       handleComponentEvent(comp, baja.nav, id, slot, obj, str, cx); 
2418     }
2419   }
2420     
2421   function setContextInOkCallback(comp, cb) {
2422     cb.addOk(function (ok, fail, resp) {
2423       ok.call(comp, resp);
2424     });
2425   }
2426   
2427   function setContextInFailCallback(comp, cb) {
2428     cb.addFail(function (ok, fail, err) {
2429       fail.call(comp, err);
2430     });
2431   }
2432   
2433   /**
2434    * Set a Slot's flags.
2435    * <p>
2436    * If the Complex is mounted, this will <strong>asynchronously</strong> set the Slot Flags on the Server. 
2437    * <p> 
2438    * An Object Literal is used to specify the method's arguments...
2439    * <pre>
2440    *   myObj.setFlags({
2441    *     slot: "outsideAirTemp",
2442    *     flags: baja.Flags.SUMMARY,
2443    *     ok: function () {
2444    *       // Called once the Flags have been set (optional)
2445    *     },
2446    *     fail: function (err) {
2447    *       // Called if the flags fail to set (optional)
2448    *     },
2449    *     batch // if defined, any network calls will be batched into this object (optional)
2450    *   });
2451    * </pre>
2452    * For callbacks, the 'this' keyword is set to the Component instance.
2453    *
2454    * @param {Object} obj the Object Literal for the method's arguments.
2455    * @param {baja.Slot|String} obj.slot the Slot of Slot name.
2456    * @param {Number} obj.flags the new flags for the Slot.
2457    * @param {Function} [obj.ok] the ok function callback. Called once the method has succeeded.
2458    * @param {Function} [obj.fail] the fail function callback. Called if this method has an error.
2459    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
2460    * @param [obj.cx] the Context (used internally by BajaScript).
2461    */
2462   baja.Component.prototype.setFlags = function (obj) {
2463     obj = objectify(obj);
2464     
2465     var slot = obj.slot,
2466         flags = obj.flags,
2467         cx = obj.cx,
2468         serverDecode = cx && cx.serverDecode,
2469         commit = cx && cx.commit,
2470         cb = new Callback(obj.ok, obj.fail, obj.batch);
2471         
2472     // Ensure 'this' is Component in callbacks...
2473     setContextInOkCallback(this, cb);
2474     setContextInFailCallback(this, cb);
2475         
2476     try {
2477       slot = this.getSlot(slot);
2478       
2479       // Short circuit some of this if this is called from a Server decode
2480       if (!serverDecode) {
2481         // Validate arguments
2482         strictArg(slot, baja.Slot);
2483         strictArg(flags, Number);
2484         
2485         if (slot === null) {
2486           throw new Error("Could not find Slot: " + obj.slot);
2487         }
2488             
2489         // Subclass check
2490         if (typeof this.checkSetFlags === "function") {
2491           this.checkSetFlags(slot, flags, cx);
2492         }   
2493       }
2494       
2495       // Check if this is a proxy. If so then trap it...
2496       if (!commit && this.isMounted() && this.$space.hasCallbacks()) {
2497         this.$space.getCallbacks().setFlags(this, slot, flags, cb);
2498         return;
2499       }
2500      
2501       // Set the flags for the Slot
2502       slot.$setFlags(flags);
2503       
2504       if (cx) {
2505         if (typeof cx.displayName === "string") {
2506           slot.$setDisplayName(cx.displayName);
2507         }
2508         if (typeof cx.display === "string") {
2509           slot.$setDisplay(cx.display);
2510         }
2511       }
2512       
2513       // Fire Component Event
2514       fwCompEvent(this, FLAGS_CHANGED, slot, null, null, cx);
2515       
2516       cb.ok();
2517     }
2518     catch (err) {
2519       cb.fail(err);
2520     }
2521   };
2522   
2523   /**
2524    * Set a dynamic Slot's facets.
2525    * <p>
2526    * If the Complex is mounted, this will <strong>asynchronously</strong> change the facets on the Server. 
2527    * <p> 
2528    * An Object Literal is used to specify the method's arguments...
2529    * <pre>
2530    *   myObj.setFacets({
2531    *     slot: "outsideAirTemp",
2532    *     facets: baja.Facets.make(["foo"], ["boo"]),
2533    *     ok: function () {
2534    *       // Called once the Facets have been set (optional)
2535    *     },
2536    *     fail: function (err) {
2537    *       // Called if the facets fail to change (optional)
2538    *     },
2539    *     batch // if defined, any network calls will be batched into this object (optional)
2540    *   });
2541    * </pre>
2542    * For callbacks, the 'this' keyword is set to the Component instance.
2543    *
2544    * @param {Object} obj the Object Literal for the method's arguments.
2545    * @param {baja.Slot|String} obj.slot the Slot of Slot name.
2546    * @param {baja.Facets} obj.facets the new facets for the dynamic Slot.
2547    * @param {Function} [obj.ok] the ok function callback. Called once the method has succeeded.
2548    * @param {Function} [obj.fail] the fail function callback. Called if this method has an error.
2549    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
2550    * @param [obj.cx] the Context (used internally by BajaScript).
2551    */
2552   baja.Component.prototype.setFacets = function (obj) {
2553     obj = objectify(obj);
2554     var cb = new Callback(obj.ok, obj.fail, obj.batch);
2555         
2556     // Ensure 'this' is Component in callbacks...
2557     setContextInOkCallback(this, cb);
2558     setContextInFailCallback(this, cb);
2559     
2560     var slot = obj.slot,
2561         facets = obj.facets,
2562         cx = obj.cx,
2563         serverDecode = cx && cx.serverDecode,
2564         commit = cx && cx.commit;
2565     
2566     try {
2567       slot = this.getSlot(slot);
2568       
2569       if (facets === null) {
2570         facets = baja.Facets.DEFAULT;
2571       }
2572       
2573       // Short circuit some of this if this is the result of a Server Decode
2574       if (!serverDecode) {
2575         // Validate arguments
2576         strictArg(slot, baja.Slot);
2577         strictArg(facets, baja.Facets);
2578         
2579         if (slot === null) {
2580           throw new Error("Could not find Slot: " + obj.slot);
2581         }
2582                 
2583         if (slot.isFrozen()) {
2584           throw new Error("Cannot set facets of frozen Slot: " + slot.getName());
2585         }
2586             
2587         // Subclass check
2588         if (typeof this.checkSetFacets === "function") {
2589           this.checkSetFacets(slot, facets, cx);
2590         }
2591       }
2592       
2593       // Check if this is a proxy. If so then trap it...
2594       if (!commit && this.isMounted() && this.$space.hasCallbacks()) {
2595         this.$space.getCallbacks().setFacets(this, slot, facets, cb);
2596         return;
2597       }
2598       
2599       // Set the flags for the Slot
2600       slot.$setFacets(facets);
2601       
2602       if (cx) {
2603         if (typeof cx.displayName === "string") {
2604           slot.$setDisplayName(cx.displayName);
2605         }
2606         if (typeof cx.display === "string") {
2607           slot.$setDisplay(cx.display);
2608         }
2609       }
2610       
2611       // Fire Component Event
2612       fwCompEvent(this, FACETS_CHANGED, slot, facets, null, cx);
2613       
2614       cb.ok();
2615     }
2616     catch (err) {
2617       cb.fail(err);
2618     }
2619   };
2620         
2621   /**
2622    * Add a dynamic Property to a Component.
2623    * <p>   
2624    * If the value extends baja:Action, the new slot is also an Action.  
2625    * If the value extends baja:Topic, the new slot is also a Topic.  
2626    * <p>
2627    * If the Complex is mounted, this will <strong>asynchronously</strong> add
2628    * the Property to the Component on the Server. 
2629    * <p> 
2630    * An Object Literal is used to specify the method's arguments...
2631    * <pre>
2632    *   myObj.add({
2633    *     slot: "foo",
2634    *     value: "slot value",
2635    *     facets: baja.Facets.make(["doo"], ["boo"]), // Optional
2636    *     flags: baja.Flags.SUMMARY, // Optional  
2637    *     ok: function (prop) {
2638    *       // Called once the Property has been added (optional)
2639    *     },
2640    *     fail: function (err) {
2641    *       // Called if the Property fails to add (optional)
2642    *     },
2643    *     batch // if defined, any network calls will be batched into this object (optional)
2644    *   });
2645    * </pre>
2646    * For callbacks, the 'this' keyword is set to the Component instance.
2647    *
2648    * @see baja.Facets
2649    * @see baja.Flags
2650    * @see baja.Component#getUniqueName
2651    *
2652    * @param {Object} obj the Object Literal for the method's arguments.
2653    * @param {String} obj.slot the Slot name the unique name to use as the String
2654    *                          key for the slot.  If null is passed, then a
2655    *                          unique name will automatically be generated.
2656    *                          If the name ends with the '?' character a unique
2657    *                          name will automatically be generated by appending
2658    *                          numbers to the specified name.  The name must meet 
2659    *                          the "name" production in the SlotPath BNF grammar.  
2660    *                          Informally this means that the name must start with 
2661    *                          an ascii letter and contain only ascii letters, ascii 
2662    *                          digits, or '_'.  Escape sequences can be specified 
2663    *                          using the '$' char.  Use baja.SlotPath.escape() to escape
2664    *                          illegal characters. 
2665    * @param {Object} obj.value the value to be added (Type must extend baja:Value).
2666    * @param {Number} [obj.flags] optional Slot flags.
2667    * @param {baja.Facets} [obj.facets] optional Slot Facets.
2668    * @param {Function} [obj.ok] the ok callback. This function is called once the Property
2669    *                            has been added to the Server. The function is passed the new
2670    *                            Property that has just been added.
2671    * @param {Function} [obj.fail] the fail callback. This function is called if the Property
2672    *                              fails to add. Any error information is passed into this function.
2673    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object
2674    * @param [obj.cx] the Context (used internally by BajaScript).
2675    * @returns if not mounted, the newly added Property. Otherwise null is returned.
2676    */
2677   baja.Component.prototype.add = function (obj) {    
2678     obj = objectify(obj, "value");
2679         
2680     var cb = new Callback(obj.ok, obj.fail, obj.batch);
2681         
2682     // Ensure 'this' is Component in callbacks...
2683     setContextInOkCallback(this, cb);
2684     setContextInFailCallback(this, cb);
2685     
2686     var slotName = obj.slot,
2687         val = obj.value,
2688         flags = obj.flags,
2689         facets = obj.facets,
2690         cx = obj.cx,
2691         serverDecode,
2692         commit;
2693         
2694     try {
2695       slotName = bajaDef(slotName, null);    
2696       flags = bajaDef(flags, 0);
2697       facets = bajaDef(facets, baja.Facets.DEFAULT);
2698       cx = bajaDef(cx, null);
2699       serverDecode = cx && cx.serverDecode;
2700       commit = cx && cx.commit;
2701       
2702       // Short-circuit some of this if this is the result of a Server Decode
2703       if (!serverDecode) {      
2704         // Validate arguments
2705         strictArg(slotName, String);
2706         strictArg(val);
2707         strictArg(flags, Number);
2708         strictArg(facets, baja.Facets);
2709         strictArg(cx);
2710             
2711         if (!baja.hasType(val)) {
2712           throw new Error("Can only add BValue Types as Component Properties");
2713         }
2714         if (val.getType().isAbstract()) {
2715           throw new Error("Cannot add Abstract Type to Component: " + val.getType());
2716         }
2717         if (!val.getType().isValue()) {
2718           throw new Error("Cannot add non Value Types as Properties to a Component");
2719         }
2720         if (val === this) {
2721           throw new Error("Illegal argument value === this");
2722         }
2723         // Custom check add
2724         if (typeof this.checkAdd === "function") {
2725           this.checkAdd(slotName, val, flags, facets, cx);
2726         }
2727         if (val.getType().isComponent()) {
2728           if (typeof val.isParentLegal === "function") {
2729             if (!this.getType().is("baja:UnrestrictedFolder")) {
2730               if (!val.isParentLegal(this)) {
2731                 throw new Error("Illegal parent: " + this.getType() + " for child " + val.getType());
2732               }
2733             }
2734           }
2735           if (typeof this.isChildLegal === "function") {
2736             if (!this.isChildLegal(val)) {
2737               throw new Error("Illegal child: " + val.getType() + " for parent " + this.getType());
2738             }
2739           }
2740         }
2741       }
2742               
2743       // Check if this is a proxy. If so then trap it...
2744       if (!commit && this.isMounted() && this.$space.hasCallbacks()) {      
2745         this.$space.getCallbacks().add(this, 
2746                                        slotName, 
2747                                        val, 
2748                                        flags, 
2749                                        facets, 
2750                                        cb);
2751         return null;
2752       }
2753         
2754       if (!serverDecode) {
2755         if (slotName === null) {
2756           slotName = this.getUniqueName(val.getType().getTypeName()); // TODO: Need extra argument checking before this is reached
2757         }
2758         else if (slotName.substring(slotName.length - 1, slotName.length) === "?") {
2759           slotName = this.getUniqueName(slotName.substring(0, slotName.length - 1));
2760         } 
2761         
2762         baja.SlotPath.verifyValidName(slotName);
2763       }
2764                   
2765       // Check for duplicate Slot
2766       if (this.$map.get(slotName) !== null) {
2767         throw new Error("Duplicate Slot: " + slotName);
2768       }
2769              
2770       var displayName = slotName,      
2771           display = "";
2772             
2773       if (cx) {
2774         if (typeof cx.displayName === "string") {
2775           displayName = cx.displayName;
2776         }
2777         if (typeof cx.display === "string") {
2778           display = cx.display;
2779         }
2780       }
2781        
2782       var p;
2783       if (val.getType().isAction()) {
2784         p = new PropertyAction(slotName, displayName, display, flags, facets, val);
2785       }
2786       else if (val.getType().isTopic()) {
2787         p = new PropertyTopic(slotName, displayName, display, flags, facets, val);
2788       }
2789       else {
2790         p = new DynamicProperty(slotName, displayName, display, flags, facets, val);
2791       }
2792       
2793       // Add the Slot to the map
2794       this.$map.put(slotName, p);
2795       
2796       // Set up any parenting if needed      
2797       if (val.getType().isComplex()) {
2798         if (val.getParent() !== null) {
2799           throw new Error("Complex already parented: " + val.getType());
2800         }       
2801         val.$parent = this;
2802         val.$propInParent = p;
2803       
2804         // If we have a Component then attempt to mount it
2805         if (val.getType().isComponent() && this.isMounted()) {      
2806           this.$space.$fw("mount", val);
2807         }
2808       }
2809       
2810       // Fire Component Event
2811       fwCompEvent(this, ADDED, p, null, null, cx);
2812       
2813       cb.ok(p);
2814       return p;
2815     }
2816     catch (err) {
2817       cb.fail(err);
2818     }
2819     return null;
2820   };
2821   
2822   /**
2823    * Return a unique name for a potential new Slot in this Component.
2824    * <p>
2825    * Please note, this method inspects the current Slots this Component has loaded
2826    * to find a unique name. Therefore, if this Component is a Proxy, it must be 
2827    * fully loaded and subscribed. Also please refrain from using this method in a
2828    * batch operation since it's likely the other operations in the batch will influence
2829    * a Slot name's uniqueness.
2830    *
2831    * @param {String} slotName the initial Slot name used to ensure uniqueness. This must be 
2832    *                          a valid Slot name.
2833    * @returns {String} a unique name.
2834    */
2835   baja.Component.prototype.getUniqueName = function (slotName) {
2836     baja.SlotPath.verifyValidName(slotName);
2837     var n = slotName,
2838         i = 1;
2839     while (this.getSlot(n) !== null) {
2840       n = slotName + i;
2841       i++;
2842     }
2843     return n;
2844   };
2845       
2846   function removeUnmountEvent(component, handlers, cx) {
2847     handlers.fireHandlers("unmount", baja.error, component, cx);      
2848   }
2849   
2850   function removePropagateUnmountEvent(component, cx) {
2851     // If the Component is subscribed then trigger the unmount events
2852     if (component.isSubscribed()) {
2853       removeUnmountEvent(component, component, cx);
2854       
2855       if (component.$subs.length > 0) {  
2856         // Route to all registered Subscribers
2857         var i;
2858         for (i = 0; i < component.$subs.length; ++i) {
2859           // Route to event handlers on the Subscriber      
2860           removeUnmountEvent(component, component.$subs[i], cx);
2861         }
2862       }
2863     }
2864     
2865     // Search all child Components and trigger the unmount event
2866     component.getSlots(function (slot) {
2867       return slot.isProperty() && slot.getType().isComponent();
2868     }).each(function (slot) {
2869       removePropagateUnmountEvent(this.get(slot), cx);
2870     });
2871   }
2872   
2873   /**
2874    * Remove the dynamic Slot by the specified name.
2875    * <p>
2876    * If the Complex is mounted, this will <strong>asynchronously</strong> remove
2877    * the Property from the Component on the Server. 
2878    * <p> 
2879    * The Slot, Slot name, a Complex or an Object Literal can be used for the method's arguments...
2880    * <pre>
2881    *   myObj.remove("foo");
2882    *   
2883    *   //...or via the Slot itself...
2884    *
2885    *   myObj.remove(theFooSlot);
2886    *
2887    *   //...or remove the Complex instance from the parent...
2888    *
2889    *   myObj.remove(aComplexInstance);
2890    *
2891    *   //... of if more arguments are needed then via Object Literal notation...
2892    *
2893    *   myObj.remove({
2894    *     slot: "foo",
2895    *     ok: function () {
2896    *       // Called once the Property has been removed (optional)
2897    *     },
2898    *     fail: function (err) {
2899    *       // Called if the Property fails to remove (optional)
2900    *     },
2901    *     batch // if defined, any network calls will be batched into this object (optional)
2902    *   });
2903    * </pre>
2904    * For callbacks, the 'this' keyword is set to the Component instance.
2905    *
2906    * @param {baja.Slot|String|Object} obj the Slot, Slot name, Complex instance or an Object Literal.
2907    * @param {String} obj.slot the Slot, Slot name or Complex instance to remove.
2908    * @param {Function} [obj.ok] the ok callback. This function is called once the Property
2909    *                            has been remove from the Server. 
2910    * @param {Function} [obj.fail] the fail callback. This function is called if the Property
2911    *                              fails to remove. Any error information is passed into this function.
2912    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
2913    * @param [obj.cx] the Context (used internally by BajaScript).
2914    */
2915   baja.Component.prototype.remove = function (obj) {
2916     obj = objectify(obj, "slot");
2917 
2918     var slot = obj.slot,
2919         cx = obj.cx,
2920         serverDecode = cx && cx.serverDecode,
2921         commit = cx && cx.commit,
2922         cb = new Callback(obj.ok, obj.fail, obj.batch);
2923     
2924     // Ensure 'this' is Component in callbacks...
2925     setContextInOkCallback(this, cb);
2926     setContextInFailCallback(this, cb);
2927         
2928     try {
2929       strictArg(slot);     
2930       
2931       var val = null;
2932       if (baja.hasType(slot) && slot.getType().isComplex()) {
2933         val = slot;
2934         slot = slot.getPropertyInParent();
2935       }
2936       else {
2937         slot = this.getSlot(slot);
2938       }
2939       
2940       // Short circuit some of this on a Server decode
2941       if (!serverDecode) {          
2942         if (slot === null) {
2943           throw new Error("Invalid slot for Component remove");
2944         }
2945         if (!slot.isProperty() || slot.isFrozen()) {
2946           throw new Error("Cannot remove Slot that isn't a dynamic Property: " + slot.getName());
2947         }
2948         
2949         // Subclass check
2950         if (typeof this.checkRemove === "function") {
2951           this.checkRemove(slot, cx);
2952         }
2953       }
2954       
2955       // Check if this is a proxy. If so then trap it...
2956       if (!commit && this.isMounted() && this.$space.hasCallbacks()) {
2957         this.$space.getCallbacks().remove(this, slot, cb);
2958         return;
2959       }
2960       
2961       // TODO: Remove links?
2962       
2963       if (val === null) {
2964         val = this.get(slot);
2965       }
2966                       
2967       // Unparent
2968       if (val.getType().isComplex()) {
2969       
2970         // Unmount from Component Space
2971         if (val.getType().isComponent()) {
2972           // Trigger unmount event to this component and all child Components just before it's properly removed
2973           removePropagateUnmountEvent(val, cx);
2974         
2975           // If we have a Component then attempt to unmount it
2976           // (includes unregistering from subscription)
2977           if (this.isMounted()) {
2978             this.$space.$fw("unmount", val);
2979           }
2980         }
2981       
2982         val.$parent = null;
2983         val.$propInParent = null;     
2984       }
2985           
2986       // Remove the Component from the Slot Map
2987       this.$map.remove(slot.getName());
2988                 
2989       // Fire Component Event
2990       fwCompEvent(this, REMOVED, slot, val, null, cx);
2991       
2992       cb.ok();
2993     }
2994     catch (err) {
2995       cb.fail(err);
2996     }
2997   };
2998   
2999   /**
3000    * Rename the specified dynamic Slot.
3001    * <p>
3002    * If the Complex is mounted, this will <strong>asynchronously</strong> rename
3003    * the Slot in the Component on the Server.
3004    * <p> 
3005    * An Object Literal is used for the method's arguments...
3006    * <pre>
3007    *   myObj.rename({
3008    *     slot: "foo",
3009    *     newName: "boo",
3010    *     ok: function () {
3011    *       // Called once the Slot has been renamed (optional)
3012    *     },
3013    *     fail: function (err) {
3014    *       // Called if the Slot fails to rename (optional)
3015    *     },
3016    *     batch // if defined, any network calls will be batched into this object (optional)
3017    *   });
3018    * </pre>
3019    * For callbacks, the 'this' keyword is set to the Component instance.
3020    *
3021    * @param {Object} obj the Object Literal used for the method's arguments.
3022    * @param {baja.Slot|String} obj.slot the dynamic Slot or dynamic Slot name that will be renamed
3023    * @param {String} obj.newName the new name of the Slot. 
3024    *                             The name must meet the "name" production in the
3025    *                             SlotPath BNF grammar.  Informally this means that
3026    *                             the name must start with an ascii letter, and 
3027    *                             contain only ascii letters, ascii digits, or '_'.  
3028    *                             Escape sequences can be specified using the '$' char.  
3029    *                             Use baja.SlotPath.escape() to escape illegal characters.
3030    * @param {Function} [obj.ok] the ok callback. This function is called once the Slot
3031    *                            has been renamed. 
3032    * @param {Function} [obj.fail] the fail callback. This function is called if the Slot
3033    *                              fails to rename. Any error information is passed into this function.
3034    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
3035    * @param [obj.cx] the Context (used internally by BajaScript).
3036    */
3037   baja.Component.prototype.rename = function (obj) {
3038     obj = objectify(obj);
3039     
3040     var slot = obj.slot,
3041         newName = obj.newName,
3042         cx = obj.cx,
3043         serverDecode = cx && cx.serverDecode,
3044         commit = cx && cx.commit,
3045         cb = new Callback(obj.ok, obj.fail, obj.batch);
3046     
3047     // Ensure 'this' is Component in callbacks...
3048     setContextInOkCallback(this, cb);
3049     setContextInFailCallback(this, cb);
3050     
3051     try {
3052       var s = this.getSlot(slot);
3053           
3054       if (s === null) {
3055         throw new Error("Cannot rename. Slot doesn't exist: " + slot + " -> " + newName);
3056       }
3057        
3058       // Short circuit some of these checks on a Server decode 
3059       if (!serverDecode) {     
3060         strictArg(s, baja.Slot);
3061         strictArg(newName, String);
3062               
3063         baja.SlotPath.verifyValidName(newName);
3064         
3065         if (s.isFrozen()) {
3066           throw new Error("Cannot rename frozen Slot: " + slot + " -> " + newName);
3067         }
3068         if (this.getSlot(newName) !== null) {
3069           throw new Error("Cannot rename. Slot name already used: " + slot + " -> " + newName);
3070         }
3071         
3072         // Subclass check
3073         if (typeof this.checkRename === "function") {
3074           this.checkRename(s, newName, cx);
3075         }
3076       }
3077       
3078       // Record the old name
3079       var oldName = s.getName();
3080       
3081        // Check if this is a proxy. If so then trap it...
3082       if (!commit && this.isMounted() && this.$space.hasCallbacks()) {
3083         this.$space.getCallbacks().rename(this, oldName, newName, cb);
3084         return;
3085       }
3086     
3087       // Rename the Component from the Slot Map
3088       if (!this.$map.rename(oldName, newName)) {
3089         throw new Error("Cannot rename: " + oldName + " -> " + newName);
3090       }
3091       
3092       s.$slotName = newName;
3093       
3094       if (cx) {
3095         if (typeof cx.displayName === "string") {
3096           s.$setDisplayName(cx.displayName);
3097         }
3098         if (typeof cx.display === "string") {
3099           s.$setDisplay(cx.display);
3100         }
3101       }
3102             
3103       // Fire Component Event    
3104       fwCompEvent(this, RENAMED, s, null, oldName, cx);
3105       
3106       cb.ok();
3107     }
3108     catch (err) {
3109       cb.fail(err);
3110     }
3111   };    
3112    
3113   /**
3114    * Reorder the Component's dynamic Properties.
3115    * <p>
3116    * If the Complex is mounted, this will <strong>asynchronously</strong> reorder
3117    * the dynamic Properties in the Component on the Server.
3118    * <p> 
3119    * An Property array or an Object Literal can used for the method's arguments...
3120    * <pre>
3121    *   // Order via an array of Properties...
3122    *   myObj.reorder([booProp, fooProp, dooProp]);
3123    *
3124    *   // ...or order via an array of Property names...
3125    *   myObj.reorder(["boo", "foo", "doo"]);
3126    *
3127    *   // ...or for more arguments, use an Object Literal...
3128    *   myObj.reorder({
3129    *     dynamicProperties: [booProp, fooProp, dooProp], // Can also be a Property name array!
3130    *     ok: function () {
3131    *       // Called once the Properties have been reordered (optional)
3132    *     },
3133    *     fail: function (err) {
3134    *       // Called if the Slot fails to reorder (optional)
3135    *     },
3136    *     batch // if defined, any network calls will be batched into this object (optional)
3137    *   });
3138    * </pre>
3139    * For callbacks, the 'this' keyword is set to the Component instance.
3140    *
3141    * @param {Array|Object} obj the array of Properties, Property names or an Object Literal used for the method's arguments.
3142    * @param {Array} obj.dynamicProperties an array of Properties or Property names for the slot order.
3143    * @param {Function} [obj.ok] the ok callback. This function is called once the dynamic Properties have
3144    *                            been reordered. 
3145    * @param {Function} [obj.fail] the fail callback. This function is called if the dynamic Properties fail to reorder. 
3146    *                              Any error information is passed into this function.
3147    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
3148    * @param [obj.cx] the Context (used internally by BajaScript).
3149    */ 
3150   baja.Component.prototype.reorder = function (obj) {
3151     obj = objectify(obj, "dynamicProperties");
3152     
3153     var dynamicProperties = obj.dynamicProperties,
3154         cx = obj.cx,
3155         serverDecode = cx && cx.serverDecode,
3156         commit = cx && cx.commit,
3157         cb = new Callback(obj.ok, obj.fail, obj.batch),
3158         i;
3159         
3160     // Ensure 'this' is Component in callbacks...
3161     setContextInOkCallback(this, cb);
3162     setContextInFailCallback(this, cb);
3163     
3164     // If this is a commit and the component is mounted then only process if the component is subscribed and fully loaded.
3165     // Otherwise it probably won't work
3166     if (commit && this.isMounted() && !(this.isSubscribed() && this.$bPropsLoaded)) {
3167       cb.ok();
3168       return;
3169     }
3170         
3171     try {
3172       // Verify the array contents
3173       if (!serverDecode) {
3174         strictArg(dynamicProperties, Array);
3175       }
3176       
3177       var preGetSlot;      
3178       for (i = 0; i < dynamicProperties.length; ++i) {
3179         preGetSlot = dynamicProperties[i];
3180         dynamicProperties[i] = this.getSlot(dynamicProperties[i]);
3181         
3182         if (!dynamicProperties[i]) {
3183           throw new Error("Could not find dynamic Property to reorder: " + preGetSlot + " in " + this.toPathString());
3184         }
3185         if (dynamicProperties[i].isFrozen()) {
3186           throw new Error("Cannot reorder frozen Properties");
3187         }
3188       }
3189       
3190       var currentDynProps = this.getSlots().dynamic().properties(),
3191           dynPropCount = 0;
3192       while (currentDynProps.next()) {
3193         ++dynPropCount;
3194       }
3195       if (dynPropCount === 0) {
3196         throw new Error("Cannot reorder. No dynamic Props!");
3197       }
3198       if (dynPropCount !== dynamicProperties.length) {
3199         throw new Error("Cannot reorder. Actual count: " + dynPropCount + " != " + dynamicProperties.length);
3200       }
3201       
3202       // Subclass check
3203       if (!serverDecode && typeof this.checkReorder === "function") {
3204         this.checkReorder(dynamicProperties, cx);
3205       }
3206               
3207       // Check if this is a proxy. If so then trap it...
3208       if (!commit && this.isMounted() && this.$space.hasCallbacks()) {
3209         this.$space.getCallbacks().reorder(this, dynamicProperties, cb);
3210         return;
3211       }
3212           
3213       // Get a copy of the keys used in the SlotMap
3214       var keys = this.$map.getKeys();
3215       
3216       var getKeyIndex = function (k) {
3217         var i;
3218         for (i = 0; i < keys.length; ++i) {
3219           if (k === keys[i]) {
3220             return i;
3221           }
3222         }
3223         throw new Error("Could not find Property in reorder: " + k);
3224       };
3225       
3226       var getNewPropsIndex = function (k) {
3227         var i;
3228         for (i = 0; i < dynamicProperties.length; ++i) {
3229           if (k === dynamicProperties[i].getName()) {
3230             return i;
3231           }
3232         }
3233         throw new Error("Could not find dynamic Property in reorder: " + k);
3234       };
3235       
3236       // Sort the map accordingly
3237       var that = this;
3238       this.$map.sort(function (a, b) {
3239         var as = that.$map.get(a);      
3240         var bs = that.$map.get(b);
3241         
3242         // If both Properties are frozen then just compare against the current indexes
3243         if (as.isFrozen() && bs.isFrozen()) {
3244           return getKeyIndex(a) < getKeyIndex(b) ? -1 : 1;
3245         }
3246         else if (as.isFrozen()) {
3247           return -1;
3248         }
3249         else if (bs.isFrozen()) {
3250           return 1;
3251         }
3252         else {
3253           // If both Properties are dynamic then we can order as per the new array
3254           return getNewPropsIndex(a) < getNewPropsIndex(b) ? -1 : 1;
3255         }
3256       });
3257 
3258       // Fire Component Event
3259       fwCompEvent(this, REORDERED, null, null, null, cx);
3260       
3261       cb.ok();
3262     }
3263     catch (err) {
3264       cb.fail(err);
3265     }
3266   };
3267   
3268   var invalidActionArgErrMsg = "Invalid Action Argument: ";
3269       
3270   /**
3271    * Invoke an Action.
3272    * <p>
3273    * If the Complex is mounted, this will <strong>asynchronously</strong> invoke
3274    * the Action on the Component in the Server.
3275    * <p> 
3276    * A Slot, Slot name or an Object Literal can used for the method's arguments...
3277    * <pre>
3278    *   // Invoke the Action via its Action Slot...
3279    *   myObj.invoke(fooAction);
3280    *
3281    *   // ...or via the Action's Slot name...
3282    *   myObj.invoke("foo");
3283    *
3284    *   // ...or for more arguments, use an Object Literal...
3285    *   myObj.invoke({
3286    *     slot: actionSlot, // Can also be an Action Slot name
3287    *     value: "the Action's argument",
3288    *     ok: function (returnValue) {
3289    *       // Called once the Action has been invoked (optional)
3290    *     },
3291    *     fail: function (err) {
3292    *       // Called if the Action fails to invoke (optional)
3293    *     },
3294    *     batch // if defined, any network calls will be batched into this object (optional)
3295    *   });
3296    * </pre>
3297    * For callbacks, the 'this' keyword is set to the Component instance.
3298    * <p>
3299    * Please note that auto-generated convenience methods are created and added to a Component for invoking
3300    * frozen Actions...
3301    * <pre>
3302    *   // Invoke an Action called 'override'. Pass in an argument
3303    *   myPoint.override(overrideVal);
3304    *
3305    *   // ...or via an Object Literal for more arguments...
3306    *   myPoint.override({
3307    *     value: overrideVal,
3308    *     ok: function (returnValue) {
3309    *       // Called once the Action has been invoked (optional)
3310    *     },
3311    *     fail: function (err) {
3312    *       // Called if the Action fails to invoke (optional)
3313    *     },
3314    *     batch // if defined, any network calls will be batched into this object (optional)
3315    *   });
3316    * </pre>
3317    * If the name of the auto-generated Action method is already used, BajaScript will attach a number to the end
3318    * of the method name so it becomes unique. For example, the 'set' Action on a NumericWritable would be called 'set1'
3319    * because Component already has a 'set' method.
3320    *
3321    * @param {baja.Action|String|Object} obj the Action, Action name or Object Literal for the method's arguments.
3322    * @param {baja.Action|String} obj.slot the Action or Action name.
3323    * @param [obj.value] the Action's argument.
3324    * @param {Function} [obj.ok] the ok callback. This function is called once Action has been invoked. 
3325    *                            If the Action has a returned argument, this will be passed to this function.
3326    * @param {Function} [obj.fail] the fail callback. This function is called if the Action fails to invoke. 
3327    *                              Any error information is passed into this function.
3328    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
3329    * @param [obj.cx] the Context (used internally by BajaScript).
3330    */  
3331   baja.Component.prototype.invoke = function (obj) {
3332     obj = objectify(obj, "slot");
3333     
3334     var action = obj.slot,
3335         arg = bajaDef(obj.value, null),
3336         cx = obj.cx,
3337         retVal = null,
3338         cb = new Callback(obj.ok, obj.fail, obj.batch),
3339         flags = 0,
3340         paramType;
3341     
3342     // Ensure 'this' is Component in callbacks...
3343     setContextInOkCallback(this, cb);
3344     setContextInFailCallback(this, cb);
3345             
3346     try {
3347       action = this.getSlot(action);
3348       
3349       if (action === null) {
3350         throw new Error("Action does not exist: " + obj.slot);
3351       }
3352             
3353       // Get Slot flags so we can test for 'async'
3354       flags = this.getFlags(action);
3355       paramType = action.getParamType();
3356       
3357       // Check we have a valid argument for this Action
3358       if (paramType) {
3359         if (baja.hasType(arg)) {
3360           if (arg.getType().isNumber() && paramType.isNumber() && !arg.getType().equals(paramType)) {
3361             // Recreate the number with the correct boxed type if the type spec differs
3362             arg = paramType.getInstance().constructor.make(arg.valueOf()); 
3363           }
3364           else if (!arg.getType().is(paramType)) {
3365             throw new Error(invalidActionArgErrMsg + arg);
3366           }
3367         }
3368         else {
3369           throw new Error(invalidActionArgErrMsg + arg);
3370         }
3371       }
3372     }
3373     catch (err) {
3374       // Notify fail
3375       cb.fail(err);
3376       
3377       // We should ALWAYS bail after calling a callback fail!
3378       return;
3379     }
3380     
3381     var that = this;
3382     function inv() {
3383       try {                        
3384         if (that.isMounted() && that.$space.hasCallbacks()) {
3385           // If mounted then make a network call for the Action invocation
3386           that.$space.getCallbacks().invokeAction(that, action, arg, cb);
3387           return;
3388         }
3389         
3390         if (!action.isProperty()) {
3391           // Invoke do method of Action
3392           var s = "do" + action.getName().capitalizeFirstLetter();
3393           if (typeof that[s] === "function") {
3394             // Invoke but ensure null is returned if the function returns undefined
3395             retVal = bajaDef(that[s](arg, cx), null);
3396           }
3397           else {
3398             throw new Error("Could not find do method for Action: " + action.getName());
3399           }
3400         }
3401         else {
3402           // If the Action is also a Property then forward its invocation on to the value
3403           retVal = bajaDef(that.get(action).invoke(that, arg, cx), null);
3404         }
3405         
3406         cb.ok(retVal);
3407       }
3408       catch (err) {
3409         cb.fail(err);
3410       }
3411     }
3412     
3413     if ((flags & baja.Flags.ASYNC) !== baja.Flags.ASYNC) {
3414       inv();
3415     }
3416     else {
3417       baja.runAsync(inv);
3418     }
3419     
3420     return retVal;
3421   };
3422 
3423   /**
3424    * Fire a Topic.
3425    * <p>
3426    * If the Complex is mounted, this will <strong>asynchronously</strong> fire
3427    * the Topic on the Component in the Server.
3428    * <p> 
3429    * A Slot, Slot name or an Object Literal can used for the method's arguments...
3430    * <pre>
3431    *   // Fire the Topic via its Topic Slot...
3432    *   myObj.fire(fooTopic);
3433    *
3434    *   // ...or via the Topic's Slot name...
3435    *   myObj.fire("foo");
3436    *
3437    *   // ...or for more arguments, use an Object Literal...
3438    *   myObj.fire({
3439    *     slot: topicSlot, // Can also be a Topic Slot name
3440    *     value: "the Topic event argument",
3441    *     ok: function () {
3442    *       // Called once the Topic has been fired (optional)
3443    *     },
3444    *     fail: function (err) {
3445    *       // Called if the Topic fails to fire (optional)
3446    *     },
3447    *     batch // if defined, any network calls will be batched into this object (optional)
3448    *   });
3449    * </pre>
3450    * <p>
3451    * Please note that auto-generated convenience methods are created and added to a Component for firing
3452    * frozen Topics...
3453    * <pre>
3454    *   // Fire a Topic called 'foo'
3455    *   myObj.fireFoo();
3456    *
3457    *   // Fire a Topic called foo with an event argument...
3458    *   myObj.fireFoo("the Topic event argument");
3459    *
3460    *   // ...or via an Object Literal for more arguments...
3461    *   myObj.fireFoo({
3462    *     value: "the Topic event argument",
3463    *     ok: function () {
3464    *       // Called once the Topic has been fired (optional)
3465    *     },
3466    *     fail: function (err) {
3467    *       // Called if the Topic fails to fire (optional)
3468    *     },
3469    *     batch // if defined, any network calls will be batched into this object (optional)
3470    *   });
3471    * </pre>
3472    * If the name of the auto-generated Topic method is already used, BajaScript will attach a number to the end
3473    * of the method name so it becomes unique.
3474    * <p>
3475    * For callbacks, the 'this' keyword is set to the Component instance.
3476    *
3477    * @param {baja.Action|String|Object} obj the Topic, Topic name or Object Literal for the method's arguments.
3478    * @param {baja.Action|String} obj.slot the Topic or Topic name.
3479    * @param [obj.value] the Topic's event.
3480    * @param {Function} [obj.ok] the ok callback. This function is called once Topic has been fired. 
3481    * @param {Function} [obj.fail] the fail callback. This function is called if the Topic fails to fire. 
3482    *                              Any error information is passed into this function.
3483    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
3484    * @param [obj.cx] the Context (used internally by BajaScript).
3485    */ 
3486   baja.Component.prototype.fire = function (obj) {
3487     obj = objectify(obj, "slot");
3488     
3489     var topic = obj.slot,
3490         event = obj.value,
3491         cx = obj.cx,
3492         serverDecode = cx && cx.serverDecode,
3493         commit = cx && cx.commit,
3494         cb = new Callback(obj.ok, obj.fail, obj.batch);
3495     
3496     // Ensure 'this' is Component in callbacks...
3497     setContextInOkCallback(this, cb);
3498     setContextInFailCallback(this, cb);
3499     
3500     try {
3501       // Ensure we have a Topic Slot
3502       topic = this.getSlot(topic);
3503       if (!topic.isTopic()) {
3504         throw new Error("Slot is not a Topic: " + topic.getName());
3505       }
3506       
3507       // Ensure event is not undefined
3508       event = bajaDef(event, null);
3509       
3510       // Short circuit some of this on a Server decode
3511       if (!serverDecode) {
3512         // Validate the event
3513         if (event !== null) {
3514           if (!baja.hasType(event)) {
3515             throw new Error("Topic event is not a BValue");
3516           }
3517           if (event.getType().isAbstract()) {    
3518             throw new Error("Topic event is has abstract Type: " + event.getType());
3519           }
3520           if (!event.getType().isValue()) {
3521             throw new Error("Topic event is not a BValue: " + event.getType());
3522           }
3523         }
3524       }
3525       
3526       // Check if this is a proxy. If so then trap it...
3527       if (!commit && this.isMounted() && this.$space.hasCallbacks()) {
3528         this.$space.getCallbacks().fire(this, topic, event, cb);
3529         return;
3530       }
3531       
3532       // If the Topic is a Property then fire on the Property
3533       if (topic.isProperty()) {
3534         this.get(topic).fire(this, event, cx);
3535       }
3536 
3537       // Fire component event for Topic
3538       fwCompEvent(this, TOPIC_FIRED, topic, event, null, cx); 
3539     }
3540     catch (err) {
3541       cb.fail(err);
3542     }    
3543   };
3544   
3545   /**
3546    * Internal Framework Method.
3547    *
3548    * @private
3549    *
3550    * @see baja.Complex#$fw
3551    */
3552   baja.Component.prototype.$fw = function (x, a, b, c, d) {
3553         
3554     if (x === "modified") {   
3555       // Fire a Component modified event for Property changed    
3556       fwCompEvent(this, CHANGED, a, null, null, b);
3557       return;
3558     }
3559     else if (x === "fwSubscribed") {     
3560       fwCompEvent(this, SUBSCRIBED, null, null, null, b);
3561       return;
3562     }
3563     else if (x === "fwUnsubscribed") {     
3564       fwCompEvent(this, UNSUBSCRIBED, null, null, null, b);
3565       return;
3566     }
3567     else if (x === "modifyTrap") {     
3568       // Check if this is a proxy. If so then trap any modifications
3569       if (this.isMounted() && this.$space.hasCallbacks() && !(d && d.commit)) {
3570         this.$space.getCallbacks().set(this, 
3571                                        a,  // propertyPath
3572                                        b,  // value
3573                                        c); // callback
3574         return true;
3575       }
3576       else {
3577         return false;
3578       }
3579     }
3580     else if (x === "installKnob") {
3581       // Add a knob to the Component
3582       this.$knobs = this.$knobs || {};
3583       this.$knobCount = this.$knobCount || 0;
3584       
3585       this.$knobs[a.getId()] = a;
3586       ++this.$knobCount;
3587       
3588       fwCompEvent(this, KNOB_ADDED, this.getSlot(a.getSourceSlotName()), /*a=Knob*/a, null, /*b=Context*/b);
3589       return;
3590     }
3591     else if (x === "uninstallKnob") {
3592       // Remove the knob from the Component
3593       if (this.$knobs && this.$knobs.hasOwnProperty(a)) {
3594         var k = this.$knobs[a];
3595         delete this.$knobs[a];
3596         --this.$knobCount;
3597       
3598         fwCompEvent(this, KNOB_REMOVED, /*b=Slot name*/this.getSlot(b), k, null, /*c=Context*/c);
3599       }
3600       return;
3601     }
3602     else if (x === "setPermissions") {
3603       // Set the permissions on the Component
3604       this.$permissionsStr = a;
3605       
3606       // Nullify any decoded permissions
3607       this.$permissions = null;
3608     }
3609         
3610     return baja.Component.$super.prototype.$fw.apply(this, arguments);
3611   };
3612     
3613   /**
3614    * Return true if the Component is mounted inside a Space.
3615    *
3616    * @returns {Boolean}
3617    */
3618   baja.Component.prototype.isMounted = function () {
3619     return this.$space !== null;
3620   };
3621   
3622   /**
3623    * Return the Component Space.
3624    *
3625    * @returns the Component Space for this Component (if mounted) otherwise return null.
3626    */
3627   baja.Component.prototype.getComponentSpace = function () {
3628     return this.$space;
3629   };
3630   
3631   /**
3632    * Return the Component's handle.
3633    *
3634    * @returns {String} handle for this Component (if mounted) otherwise return null.
3635    */
3636   baja.Component.prototype.getHandle = function () {
3637     return this.$handle;
3638   };
3639   
3640   /**
3641    * Return the ORD in session for this Component.
3642    *
3643    * @returns {baja.Ord} ORD in Session for this Component (or null if not mounted).
3644    */
3645   baja.Component.prototype.getOrdInSession = function () {
3646     return this.getHandle() === null ? null : baja.Ord.make("station:|h:" + this.getHandle());
3647   };
3648       
3649   var unlease = function () {   
3650     if (!this.$lease) {
3651       return;
3652     }
3653   
3654     // Are currently subscribed?
3655     var prevSub = this.isSubscribed();
3656     this.$lease = false;
3657     this.$leaseTicket.cancel();
3658     
3659     // If the Component is unsubscribed but was previously subscribed then make a network call 
3660     if (!this.isSubscribed() && prevSub && this.isMounted() && this.$space.hasCallbacks()) {
3661       this.$space.getCallbacks().unsubscribe(["h:" + this.$handle], new Callback());
3662     } 
3663   };
3664   
3665   /**
3666    * Subscribe a number of Components for a period of time.
3667    * <p>
3668    * The default period of time is 10 seconds.
3669    * <p>
3670    * Please note that a {@link baja.Subscriber} can also be used to put a Component into
3671    * and out of subscription.
3672    * <p>
3673    * If the Component is mounted and it can be subscribed, this will result in 
3674    * an <strong>asynchronous</strong> network call.
3675    * <p>
3676    * If any of the the Components are already leased, the lease timer will just be renewed.
3677    * <p>
3678    * A time (Number or baja.RelTime) or an Object Literal can be used to specify the method's arguments...
3679    * <pre>
3680    *   // Lease an array of Components for the default time period
3681    *   myComp.lease([comp1, comp2, comp3]);
3682    *   
3683    *   // ...or lease for 2 and half minutes...
3684    *   myComp.lease({
3685    *     time: baja.RelTime.make({minutes: 2, seconds: 30}),
3686    *     comps: [comp1, comp2, comp3]
3687    *   });
3688    * </pre>
3689    * For callbacks, the 'this' keyword is set to whatever the 'comps' argument was originally set to.
3690    *
3691    * @see baja.Subscriber
3692    * @see baja.Component#lease
3693    *
3694    * @param {Object} obj an Object Literal for the method's arguments.
3695    * @param {Array|baja.Component} obj.comps the Components to be subscribed.
3696    * @param {Function} [obj.ok] the ok callback. Called once the Component has been unsubscribed.
3697    * @param {Function} [obj.fail] the fail callback. Called if the Component fails to unsubscribe.
3698    *                              Any errors will be passed to this function.
3699    * @param {Number|RelTime} [obj.time] the number of milliseconds or RelTime for the lease.
3700    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
3701    *                                      The timer will only be started once the batch has fully committed.   
3702    */
3703   baja.Component.lease = function (obj) {
3704     obj = objectify(obj, "comps");  
3705     var cb = new Callback(obj.ok, obj.fail, obj.batch);
3706         
3707     function scheduleUnlease(comp, time) {
3708       // Cancel the current lease ticket
3709       comp.$leaseTicket.cancel();
3710       
3711       // Schedule to expire the Subscription in 60 seconds
3712       comp.$leaseTicket = baja.clock.schedule(function () { 
3713         unlease.call(comp);           
3714       }, time);
3715     }
3716     
3717     try {	  
3718       var comps = obj.comps;
3719 	  
3720       if (!comps) {
3721         throw new Error("Must specify Components for lease");
3722       }
3723       
3724       // Ensure 'comps' is used as the context in the callback...
3725       setContextInOkCallback(comps, cb);
3726       setContextInFailCallback(comps, cb);
3727       
3728       // Get the lease time 
3729       var time = bajaDef(obj.time, /*10 seconds*/10000);
3730       
3731       // If a rel time then get the number of milliseconds from it
3732       if (baja.hasType(time) && time.getType().is("baja:RelTime")) {
3733         time = time.getMillis();
3734       }
3735     
3736       strictArg(time, Number);
3737       if (time < 1) {
3738         throw new Error("Invalid lease time (time must be > 0 ms): " + time);
3739       }
3740 	  
3741       if (!(comps instanceof Array)) {
3742         comps = [comps];
3743       }
3744       
3745       if (comps.length === 0) {
3746         cb.ok();
3747         return;
3748       }
3749 	  
3750       var space = null, i;
3751       for (i = 0; i < comps.length; ++i) {
3752         // Check all Components are valid
3753         strictArg(comps[i], baja.Component);
3754         if (!comps[i].isMounted()) {
3755           throw new Error("Cannot subscribe unmounted Component!");
3756         }
3757         if (!space) {
3758           space = comps[i].getComponentSpace();
3759         }
3760         if (space !== comps[i].getComponentSpace()) {
3761           throw new Error("All Components must belong to the same Component Space!");
3762         }
3763       }
3764             
3765       cb.addOk(function (ok, fail) {
3766         var x;
3767         for (x = 0; x < comps.length; ++x) {
3768           scheduleUnlease(comps[x], time);
3769         }
3770         
3771         ok();
3772       });
3773       
3774       // Build handles we want to subscribe
3775       var handles = [],
3776           compsToSub = [],
3777           j;
3778                     
3779       for (j = 0; j < comps.length; ++j) {
3780         // If already subscribed then don't bother making a network call for these
3781         if (!comps[j].isSubscribed()) {
3782           handles.push("h:" + comps[j].getHandle());
3783           compsToSub.push(comps[j]);
3784         }
3785         comps[j].$lease = true;
3786       }
3787       
3788       // If there's currently a lease active then renew it by calling ok on the callback
3789       if (handles.length === 0 || !space.hasCallbacks()) {
3790         cb.ok();
3791         return;
3792       }
3793       
3794       // Signal that each Component has been subscribed
3795       cb.addOk(function (ok, fail, resp) {
3796         var i;
3797         for (i = 0; i < compsToSub.length; ++i) {
3798           try {
3799              compsToSub[i].$fw("fwSubscribed");
3800           }
3801           catch (err) {
3802             baja.error(err);
3803           }
3804         }
3805         
3806         ok();
3807       });
3808       
3809       // Make network call for subscription      
3810       space.getCallbacks().subscribe(handles, cb, obj.importAsync);   
3811     }
3812     catch (err) {
3813       cb.fail(err);
3814     }
3815   };
3816      
3817   /**
3818    * Subscribe a Component for a period of time.
3819    * <p>
3820    * The default lease time is 10 seconds.
3821    * <p>
3822    * Please note that a {@link baja.Subscriber} can also be used to put a Component into
3823    * and out of subscription.
3824    * <p>
3825    * If the Component is mounted and it can be subscribed, this will result in 
3826    * an <strong>asynchronous</strong> network call.
3827    * <p>
3828    * If lease is called while the Component is already leased, the timer will just be renewed.
3829    * <p>
3830    * A time (Number or baja.RelTime) or an Object Literal can be used to specify the method's arguments...
3831    * <pre>
3832    *   // Lease for 15 seconds
3833    *   myComp.lease(15000);
3834    *   
3835    *   // ...or lease for 2 and half minutes...
3836    *   myComp.lease(baja.RelTime.make({minutes: 2, seconds: 30}));
3837    *
3838    *   // ...or lease using an Object Literal for more arguments...
3839    *   myComp.lease({
3840    *     time: 1000, // in milliseconds. Can also be a RelTime.
3841    *     ok: function () {
3842    *       // Called once the Component is subscribed (optional)
3843    *     },
3844    *     fail: function (err) {
3845    *       // Called if the Component failed to subscribe (optional)
3846    *     },
3847    *     batch // if defined, any network calls will be batched into this object (optional)
3848    *   });
3849    * </pre>
3850    * For callbacks, the 'this' keyword is set to the Component instance.
3851    *
3852    * @see baja.Subscriber
3853    * @see baja.Component.lease
3854    *
3855    * @param {Number|baja.RelTime|Object} [obj] the number of milliseconds, RelTime or an Object Literal 
3856    *                                           for the method's arguments.
3857    * @param {Function} [obj.ok] the ok callback. Called once the Component has been unsubscribed.
3858    * @param {Function} [obj.fail] the fail callback. Called if the Component fails to unsubscribe.
3859    *                              Any errors will be passed to this function.
3860    * @param {Number|RelTime} [obj.time] the number of milliseconds or RelTime for the lease.
3861    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
3862    *                                      The timer will only be started once the batch has fully committed.   
3863    */
3864   baja.Component.prototype.lease = function (obj) {
3865     obj = objectify(obj, "time");  
3866     obj.comps = this;
3867     baja.Component.lease(obj);
3868   };
3869     
3870   /**
3871    * Is the Component subscribed?
3872    *
3873    * @returns {Boolean}
3874    */
3875   baja.Component.prototype.isSubscribed = function () {
3876     // Component is subscribed if is leased or a Subscriber is registered on it
3877     return this.$lease || this.$subs.length > 0;
3878   };
3879     
3880   /**
3881    * Load all of the Slots on the Component.
3882    *
3883    * If the Component is mounted and it can be loaded, this will result in 
3884    * an <strong>asynchronous</strong> network call.
3885    * <p>
3886    * An optional Object Literal can be used to specify the method's arguments...
3887    * <pre>
3888    *   myComp.loadSlots({
3889    *     ok: function () {
3890    *       // Called once the Component is loaded (optional)
3891    *     },
3892    *     fail: function (err) {
3893    *       // Called if the Component failed to load (optional)
3894    *     },
3895    *     batch // if defined, any network calls will be batched into this object (optional)
3896    *   });
3897    * </pre>
3898    * For callbacks, the 'this' keyword is set to the Component instance.
3899    *
3900    * @param {Function} [obj.ok] the ok callback. Called once the Component has been loaded.
3901    * @param {Function} [obj.fail] the fail callback. Called if the Component fails to load.
3902    *                              Any errors will be passed to this function.
3903    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
3904    */
3905   baja.Component.prototype.loadSlots = function (obj) {
3906     obj = objectify(obj); 
3907 
3908     var cb = obj.cb;
3909     if (!cb) {   
3910       cb = new Callback(obj.ok, obj.fail, obj.batch); 
3911     }
3912     
3913     // Ensure 'this' is Component in callbacks...
3914     setContextInOkCallback(this, cb);
3915     setContextInFailCallback(this, cb);
3916 
3917     try {    
3918       if (!this.$bPropsLoaded && this.isMounted() && this.$space.hasCallbacks()) {   
3919         this.$space.getCallbacks().loadSlots("h:" + this.getHandle(), 0, cb);
3920       }
3921       else {
3922         cb.ok();
3923       }
3924     }
3925     catch (err) {
3926       cb.fail(err);
3927     }
3928   };
3929   
3930   /**
3931    * Make a Server Side Call.
3932    * <p>
3933    * Sometimes it's useful to invoke a method on the server from BajaScript.
3934    * A Server Side Call is how this is achieved.
3935    * <p>
3936    * This will result in an <strong>asynchronous</strong> network call.
3937    * <p>
3938    * In order to make a Server Side Call, the developer needs to first create a 
3939    * Niagara (Server Side) class that implements the box:javax.baja.box.BIServerSideCallHandler interface.
3940    * The implementation should also declare itself as an Agent on the target Component Type (more information in Java interface docs).
3941    * Here's an example of how a method implemented by this handler can be invoked...
3942    * <pre>
3943    *   // A resolved and mounted Component...
3944    *   myComp.serverSideCall({
3945    *     typeSpec: "foo:MyServerSideCallHandler", // The TypeSpec (moduleName:typeName) of the Server Side Call Handler
3946    *     methodName: "bar", // The name of the public method we wish to invoke in the handler
3947    *     value: "the argument for the method", // The argument to pass into the method (this can be any Baja Object/Component structure).
3948    *                                              It will be deserialized automatically by Niagara.
3949    *     ok: function (returnVal) {
3950    *       // Called once the method has been executed on the Server (optional)
3951    *     },
3952    *     fail: function (err) {
3953    *       // Called if the method throws an exception on the Server or can't execute for some reason (optional)
3954    *     },
3955    *     batch // if defined, any network calls will be batched into this object (optional)
3956    *   });
3957    * <\pre>
3958    * For callbacks, the 'this' keyword is set to the Component instance.
3959    * 
3960    * @param {Object} obj the Object Literal for the method's arguments.
3961    * @param {String} obj.typeSpec the type specification of the Server Side Call Handler (moduleName:typeName).
3962    * @param {String} obj.methodName the name of the method to invoke in the Server Side Call Handler
3963    * @param obj.value the value for the server side method argument (must be a BajaScript Type)
3964    * @param {Function} [obj.ok] the ok callback. Called once the Server Side Call has been invoked.
3965    *                                             Any return value is passed to this function.
3966    * @param {Function} [obj.fail] the fail callback. Called if the Component fails to load.
3967    *                              Any errors will be passed to this function.
3968    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
3969    */
3970   baja.Component.prototype.serverSideCall = function (obj) { 
3971     obj = objectify(obj);
3972     
3973     var typeSpec = obj.typeSpec,
3974         methodName = obj.methodName,
3975         val = bajaDef(obj.value, null), 
3976         cb = new Callback(obj.ok, obj.fail, obj.batch);  
3977     
3978     // Ensure 'this' is Component in callbacks...
3979     setContextInOkCallback(this, cb);
3980     setContextInFailCallback(this, cb);
3981     
3982     try {    
3983       // Check arguments
3984       strictArg(typeSpec, String);
3985       strictArg(methodName, String);
3986       strictArg(val);
3987       
3988       // Can only make this call on proper mounted Components that have Space Callbacks
3989       if (this.isMounted() && this.$space.hasCallbacks()) {
3990         this.$space.getCallbacks().serverSideCall(this, typeSpec, methodName, val, cb);
3991       }
3992       else {
3993         throw new Error("Unable to make serverSideCall on non-proxy Component Space");
3994       }
3995     }
3996     catch (err) {
3997       cb.fail(err);
3998     }
3999   };
4000   
4001   /**
4002    * Return the Slot Path of the Component.
4003    *
4004    * @returns {baja.SlotPath} the Slot Path or null if not mounted.
4005    */
4006   baja.Component.prototype.getSlotPath = function () {
4007     if (!this.isMounted()) {
4008       return null;
4009     }
4010   
4011     var slotNames = [];
4012     function getParNames(comp) {
4013       slotNames.push(comp.getName());
4014       
4015       var p = comp.getParent();
4016       if (p !== null) {
4017         getParNames(p);
4018       }
4019     }
4020     
4021     getParNames(this);
4022     var b = slotNames.reverse().join("/");
4023     if (b.length === 0) {
4024       b = "/";
4025     }
4026     return new baja.SlotPath(b);
4027   };
4028   
4029   /**
4030    * Return the path string of the Component.
4031    *
4032    * @returns {String} the Path String or null if not mounted.
4033    */
4034   baja.Component.prototype.toPathString = function () {
4035     if (!this.isMounted()) {
4036       return null;
4037     }
4038     return this.getSlotPath().getBody();
4039   };
4040   
4041   /**
4042    * Return the Nav ORD for the Component.
4043    *
4044    * @returns {baja.Ord} the Nav ORD or null if it's not mounted.
4045    */
4046   baja.Component.prototype.getNavOrd = function () {
4047     if (!this.isMounted()) {
4048       return null;
4049     }
4050     var spaceOrd = this.$space.getAbsoluteOrd();
4051     if (spaceOrd === null) {
4052       return null;
4053     }
4054     return baja.Ord.make(spaceOrd.toString() + "|" + this.getSlotPath().toString());
4055   };
4056   
4057   /**
4058    * Return the Nav Name for the Component.
4059    *
4060    * @returns {String}
4061    */
4062   baja.Component.prototype.getNavName = function () {
4063     var name = this.getName();
4064     
4065     if (name !== null) {
4066       return name;
4067     }
4068     
4069     var space = this.getComponentSpace();
4070     if (space && space.getRootComponent() !== null) {
4071       return space.getNavName();
4072     }
4073     else {
4074       return null;
4075     }
4076   };
4077   
4078   /**
4079    * Return the Nav Display Name for the Component.
4080    *
4081    * @returns {String}
4082    */
4083   baja.Component.prototype.getNavDisplayName = function () {
4084     return this.getDisplayName();
4085   };
4086   
4087   /**
4088    * Return the Nav Parent for the Component.
4089    *
4090    * @returns parent Nav Node
4091    */
4092   baja.Component.prototype.getNavParent = function () {
4093     var parent = this.getParent();    
4094     return parent || this.getComponentSpace();
4095   };
4096   
4097   /**
4098    * Access the Nav Children for the Component.
4099    *
4100    * @see baja.NavContainer#getNavChildren
4101    */
4102   baja.Component.prototype.getNavChildren = function (obj) {
4103     obj = objectify(obj, "ok");
4104     
4105     if (this.isMounted() && this.$space.hasCallbacks()) {
4106       // If we're mounted then make a network call to get the NavChildren since this
4107       // is always implemented Server Side
4108       this.$space.getCallbacks().getNavChildren(this.getHandle(),
4109                                                 new Callback(obj.ok, obj.fail, obj.batch));
4110     }
4111     else {
4112       var kids = [];
4113       this.getSlots().properties().isComponent().each(function (slot) {
4114         if ((this.getFlags(slot) & baja.Flags.HIDDEN) === 0) {
4115           kids.push(this.get(slot));
4116         }
4117       });
4118       obj.ok(kids);
4119     }
4120   };
4121   
4122   /**
4123    * Return the Nav Icon for the Component.
4124    *
4125    * @returns {baja.Icon}
4126    */
4127   baja.Component.prototype.getNavIcon = function () {
4128     return this.getIcon();
4129   };
4130   
4131   /**
4132    * Return the Nav Description for the Component.
4133    *
4134    * @returns {String}
4135    */
4136   baja.Component.prototype.getNavDescription = function () {
4137     return this.getType().toString();
4138   };
4139   
4140   // Mix-in the event handlers for baja.Component  
4141   baja.event.mixin(baja.Component.prototype);  
4142     
4143   // These comments are added for the benefit of JsDoc Toolkit...  
4144     
4145   /**
4146    * Attach an Event Handler to this Component instance.
4147    * <p>
4148    * When an instance of Component is subscribed to a Component running
4149    * in the Station, BajaScript can be used to listen for Component Events. 
4150    * For instance, a common one would be a Property changed event...
4151    * <pre>
4152    *   // myPoint is a mounted and subscribed Component...
4153    *   myPoint.attach("changed", function (prop, cx) {
4154    *     if (prop.getName() === "out") {
4155    *       baja.outln("The output of the point is: " + this.getOutDisplay());
4156    *     }
4157    *   });
4158    * </pre>
4159    * Therefore, an event handler consists of a name and a function. When the
4160    * function is called, 'this' will map to the target Component the handler
4161    * is attached too.
4162    * <p>
4163    * For a list of all the event handlers and some of this method's more advanced 
4164    * features, please see {@link baja.Subscriber#attach}.
4165    *
4166    * @function
4167    * @name baja.Component#attach
4168    *
4169    * @see baja.Subscriber
4170    * @see baja.Component#detach
4171    * @see baja.Component#getHandlers
4172    * @see baja.Component#hasHandlers
4173    *
4174    * @param {String} event handler name
4175    * @param {Function} func the event handler function
4176    */
4177     
4178   /**
4179    * Detach an Event Handler from the Component.
4180    * <p>
4181    * If no arguments are used with this method then all events are removed.
4182    * <p>
4183    * For some of this method's more advanced features, please see {@link baja.Subscriber#detach}.
4184    * <p>
4185    * For a list of all the event handlers, please see {@link baja.Subscriber#attach}.
4186    *
4187    * @function
4188    * @name baja.Component#detach
4189    *
4190    * @see baja.Subscriber
4191    * @see baja.Component#attach
4192    * @see baja.Component#getHandlers
4193    * @see baja.Component#hasHandlers
4194    *
4195    * @param {String} [hName] the name of the handler to detach from the Component.
4196    * @param {Function} [func] the function to remove from the Subscriber. It's recommended to supply this just in case
4197    *                          other scripts have added event handlers.
4198    */
4199         
4200   /**
4201    * Return an array of event handlers.
4202    * <p>
4203    * For a list of all the event handlers, please see {@link baja.Subscriber#attach}.
4204    * <p>
4205    * To access multiple handlers, insert a space between the handler names.
4206    *
4207    * @function
4208    * @name baja.Component#getHandlers
4209    *
4210    * @see baja.Subscriber
4211    * @see baja.Component#detach
4212    * @see baja.Component#attach
4213    * @see baja.Component#hasHandlers
4214    *
4215    * @param {String} hName the name of the handler
4216    * @returns {Array}
4217    */
4218   
4219   /**
4220    * Return true if there any handlers registered for the given handler name.
4221    * <p>
4222    * If no handler name is specified then test to see if there are any handlers registered at all.
4223    * <p>
4224    * Multiple handlers can be tested for by using a space character between the names.
4225    * <p>
4226    * For a list of all the event handlers, please see {@link baja.Subscriber#attach}.
4227    *
4228    * @function
4229    * @name baja.Component#hasHandlers
4230    *
4231    * @see baja.Subscriber
4232    * @see baja.Component#detach
4233    * @see baja.Component#attach
4234    * @see baja.Component#getHandlers
4235    *
4236    * @param {String} [hName] the name of the handler. If undefined, then see if there are any 
4237    *                         handlers registered at all.
4238    * @returns {Boolean}
4239    */
4240     
4241   /**
4242    * Returns the default parameter for an Action.
4243    * <p>
4244    * For unmounted Components, by default it calls <code>Action.getParameterDefault()</code>. 
4245    * If mounted in a Proxy Component Space, this will result in an asynchronous network call.
4246    * <p>
4247    * Here's an example of how to invoke the method...
4248    * <pre>
4249    *   // A resolved and mounted Component...
4250    *   myComp.getActionParameterDefault({
4251    *     slot: "myAction", 
4252    *     ok: function (param) {
4253    *       // Called once we're received the parameter. Note that param can be null
4254    *       // if there's no parameter for the Action.
4255    *     },
4256    *     fail: function (err) {
4257    *       // Called if the method throws an exception on the Server or can't execute for some reason (optional)
4258    *     },
4259    *     batch // if defined, any network calls will be batched into this object (optional)
4260    *   });
4261    * <\pre>
4262    * For callbacks, the 'this' keyword is set to the Component instance.
4263    *
4264    * @param {Object} obj the Object Literal for the method's arguments.
4265    * @param {baja.Action|String} obj.slot the Action or Action name.
4266    * @param {Function} [obj.ok] the ok callback. Called once the Action Parameter Default has been received.
4267    *                                             Any return value is passed to this function (could be null
4268    *                                             if no Action parameter is defined).
4269    * @param {Function} [obj.fail] the fail callback.
4270    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
4271    *
4272    * @returns {baja.Value} if the Component is not mounted in a Proxy Component Space, this method will return a value.
4273    *                      If there's no Action Parameter Default then null will be returned.                    
4274    */
4275   baja.Component.prototype.getActionParameterDefault = function (obj) {
4276     obj = objectify(obj, "slot");
4277     
4278     var cb = new Callback(obj.ok, obj.fail, obj.batch);
4279     
4280     // Ensure 'this' is Component in callbacks...
4281     setContextInOkCallback(this, cb);
4282     setContextInFailCallback(this, cb);
4283     
4284     var slot = this.getSlot(obj.slot);
4285     
4286     try { 
4287       // Check arguments  
4288       if (slot === null) {
4289         throw new Error("Unable to find Action: " + obj.slot);
4290       }
4291       if (!slot.isAction()) {
4292         throw new Error("Slot is not an Action: " + obj.slot);
4293       }
4294       
4295       if (this.isMounted() && this.$space.hasCallbacks()) {
4296         // If mounted then make a network call for the Action invocation
4297         this.$space.getCallbacks().getActionParameterDefault(this, slot, cb);
4298       }
4299       else {
4300         // If not mounted then just return it from the Slot
4301         var def = slot.getParamDefault();
4302         cb.ok(def);
4303         return def;
4304       }
4305     }
4306     catch (err) {
4307       cb.fail(err);
4308     }
4309   };
4310 
4311   /**
4312    * Return the knob count for the Component.
4313    *
4314    * @returns {Number} the number of knobs installed on the Component
4315    */
4316   baja.Component.prototype.getKnobCount = function () {
4317     return this.$knobCount || 0;
4318   };
4319   
4320   /**
4321    * Return the Knobs for a particular Slot or the whole Component.
4322    * <p>
4323    * If no slot is passed in all the knobs for the Component will be returned.
4324    *
4325    * @param {baja.Slot|String} [slot] the Slot or Slot name.
4326    *
4327    * @returns {Array} array of knobs
4328    */
4329   baja.Component.prototype.getKnobs = function (slot) {
4330     if (!this.$knobCount) {
4331       return emptyArray;
4332     }
4333     
4334     if (arguments.length > 0) {
4335       // Find Knobs for a particular Slot
4336       slot = this.getSlot(slot);
4337       
4338       if (!slot) {
4339         throw new Error("Invalid Slot: " + slot);
4340       }
4341     }
4342         
4343     var p, 
4344         knob, 
4345         k = []; 
4346     
4347     // Build up knobs array
4348     for (p in this.$knobs) {
4349       if (this.$knobs.hasOwnProperty(p)) {
4350         knob = this.$knobs[p];
4351         if (slot) {
4352           if (knob.getSourceSlotName() === slot.getName()) {
4353             k.push(knob);
4354           }
4355         }
4356         else {
4357           k.push(knob);
4358         }
4359       }
4360     }
4361     
4362     return k;
4363   };
4364   
4365   /**
4366    * Return the Links for a particular Slot or the whole Component.
4367    * <p>
4368    * If no slot is passed in all the links for the Component will be returned.
4369    *
4370    * @param {baja.Slot|String} [slot] the Slot or Slot name.
4371    *
4372    * @returns {Array} array of links.
4373    */
4374   baja.Component.prototype.getLinks = function (slot) {
4375     var links = [];
4376         
4377     if (arguments.length === 0) {
4378        // If no arguments then return all the knobs for this component
4379       this.getSlots(function (s) {
4380         return s.isProperty() && s.getType().isLink();
4381       }).each(function (s) {
4382         links.push(this.get(s));
4383       });
4384     }
4385     else {
4386       // Find Links for a particular Slot
4387       slot = this.getSlot(slot);
4388       if (slot === null) {
4389         throw new Error("Invalid Slot: " + slot);
4390       }
4391       this.getSlots(function (s) {
4392         return s.isProperty() && s.getType().isLink() && this.get(s).getTargetSlotName() === slot.getName();
4393       }).each(function (s) {
4394         links.push(this.get(s));
4395       });
4396     }
4397     
4398     return links;
4399   };
4400   
4401   /**
4402    * Create an instance of a Link to use for a link to the specified source Component.
4403    * <p>
4404    * For unmounted Components, by default this method returns a plain baja:Link instance. 
4405    * If mounted in a Proxy Component Space, this will result in an asynchronous network call.
4406    * <\pre>
4407    * For callbacks, the 'this' keyword is set to the Component instance.
4408    *
4409    * @param {Object} obj the Object Literal for the method's arguments.
4410    * @param {baja.Component} obj.source the source Component for the link.
4411    * @param {baja.Slot|String} obj.sourceSlot the source Slot or Slot name.
4412    * @param {baja.Slot|String} obj.targetSlot the target Slot or Slot name.
4413    * @param {Function} [obj.ok] the ok callback. A link will be passed as an argument to this function.
4414    * @param {Function} [obj.fail] the fail callback.
4415    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
4416    *
4417    * @returns {baja.Value} if the Component is not mounted in a Proxy Component Space, this method will return a value.                  
4418    */
4419   baja.Component.prototype.makeLink = function (obj) {
4420     obj = objectify(obj);
4421         
4422     var cb = new Callback(obj.ok, obj.fail, obj.batch);
4423     
4424     // Ensure 'this' is Component in callbacks...
4425     setContextInOkCallback(this, cb);
4426     setContextInFailCallback(this, cb);
4427     
4428     try { 
4429       strictArg(obj.source, baja.Component);
4430       
4431       var source = obj.source,
4432           sourceSlot = source.getSlot(obj.sourceSlot),
4433           targetSlot = this.getSlot(obj.targetSlot);
4434       
4435       if (!targetSlot) {
4436         throw new Error("Invalid Source Slot");
4437       }
4438       
4439       if (!sourceSlot) {
4440         throw new Error("Invalid Target Slot");
4441       }
4442       
4443       if (this.isMounted() && this.$space.hasCallbacks()) {
4444         // If mounted then make a network call to get the link
4445         this.$space.getCallbacks().makeLink(source, sourceSlot, this, targetSlot, cb);
4446       }
4447       else {
4448         // If not mounted then just return it from the Slot
4449         var link = baja.$("baja:Link");
4450         cb.ok(link);
4451         return link;
4452       }
4453     }
4454     catch (err) {
4455       cb.fail(err);
4456     }
4457   };
4458   
4459   /**
4460    * Return the permissions for this Component.
4461    *
4462    * @returns {baja.Permissions}
4463    */
4464   baja.Component.prototype.getPermissions = function () {
4465     var p = this.$permissions;
4466     if (!p) {
4467       if (typeof this.$permissionsStr === "string") {
4468         p = this.$permissions = baja.Permissions.make(this.$permissionsStr);
4469       }
4470       else {
4471         p = baja.Permissions.all;
4472       }
4473     }
4474     return p;
4475   };
4476     
4477   ////////////////////////////////////////////////////////////////
4478   // Component Space
4479   //////////////////////////////////////////////////////////////// 
4480          
4481   /**
4482    * @class Represents a baja:ComponentSpace in BajaScript.
4483    *
4484    * @name baja.ComponentSpace
4485    * @extends baja.NavContainer
4486    *
4487    * @param {String} ordInSession
4488    * @param host
4489    */   
4490   baja.ComponentSpace = function (name, ordInSession, host) {
4491     baja.ComponentSpace.$super.call(this, {
4492       navName: name
4493     });
4494     this.$map = {};
4495     this.$root = null;
4496     this.$callbacks = null;
4497     this.$host = bajaDef(host, null);
4498     this.$ordInSession = bajaDef(ordInSession, "");
4499   }.$extend(baja.NavContainer).registerType("baja:ComponentSpace"); 
4500   
4501   /**
4502    * Called to initialize the Component Space.
4503    * 
4504    * @private
4505    */
4506   baja.ComponentSpace.prototype.init = function () {
4507   };
4508   
4509   /**
4510    * Mount a Component in the Component Space.
4511    * <p>
4512    * This is a private internal method to mount Components into a Component Space.
4513    *
4514    * @private
4515    */
4516   var mount = function (comp) {    
4517     var h = comp.getHandle();  
4518     if (h === null) {
4519       // TODO: What about automatically generating a handle for a non Proxy Component Space?
4520       // TODO: Generate error if not a handle?
4521       return;
4522     }
4523     
4524     if (this.$map.hasOwnProperty(h)) {
4525       throw new Error("Fatal error: handle already used in Component Space: " + h);
4526     }
4527     
4528     // Set up the Space and Handle reference
4529     comp.$space = this;
4530     this.$map[h] = comp;
4531         
4532     // Mount any child Components
4533     var cursor = comp.getSlots().properties();
4534     while (cursor.next()) {
4535       if (cursor.get().getType().isComponent()) {
4536         mount.call(this, comp.get(cursor.get()));
4537       }
4538     }
4539   };
4540   
4541   /**
4542    * Unmount a Component in the Component Space.
4543    * <p>
4544    * This is a private internal method to unmount Components into a Component Space.
4545    *
4546    * @private
4547    */
4548   var unmount = function (comp) {    
4549     var h = comp.getHandle();  
4550     if (h === null) {
4551       // TODO: Generate error if not a handle?
4552       return;
4553     }
4554     
4555     if (!this.$map.hasOwnProperty(h)) {
4556       throw new Error("Fatal error: handle not mapped into Space: " + h);
4557     }
4558     
4559     var prevSub = comp.isSubscribed();
4560     
4561     // delete the Space and Handle reference (after this isMounted() will return false)
4562     comp.$space = null;
4563     delete this.$map[h];
4564         
4565     // Unsubscribe Component from all Subscribers
4566     var subs = comp.$subs.slice(), i;
4567     for (i = 0; i < subs.length; ++i) {
4568       subs[i].unsubscribe({
4569         "comps": comp
4570       });
4571     }
4572     
4573     // Make sure we're not leased    
4574     unlease.call(comp);
4575     
4576     // If this was subscribed before, make sure this callback is made
4577     if (prevSub && typeof comp.unsubscribed === "function") {
4578       try {
4579         comp.unsubscribed();
4580       }
4581       catch (err) {
4582         baja.error(err);
4583       }
4584     }
4585     
4586     // TODO: What about nullifying the handle reference on the Component? 
4587              
4588     // Unmount any child Components
4589     var cursor = comp.getSlots().properties();
4590     while (cursor.next()) {
4591       if (cursor.get().getType().isComponent()) {
4592         unmount.call(this, comp.get(cursor.get()));
4593       }
4594     }
4595   };
4596     
4597   /**
4598    * Private framework handler for a Component Space.
4599    * <p>
4600    * This is a private internal method for framework developers.
4601    *
4602    * @private
4603    */
4604   baja.ComponentSpace.prototype.$fw = function (x, a, b, c) {    
4605     if (x === "mount") {
4606       // Mount this Component    
4607       mount.call(this, /*Component*/a);
4608     }
4609     else if (x === "unmount") {
4610       // Unmount this Component    
4611       unmount.call(this, /*Component*/a);
4612     }
4613   };  
4614     
4615   /**
4616    * Return the root Component of the Component Space.
4617    * 
4618    * @returns the root Component for the Space.
4619    */
4620   baja.ComponentSpace.prototype.getRootComponent = function () {
4621     return this.$root;
4622   };
4623   
4624   /**
4625    * Return the ORD in Session for the Component Space.
4626    *
4627    * @returns {baja.Ord}
4628    */
4629   baja.ComponentSpace.prototype.getOrdInSession = function () {
4630     return baja.Ord.make(this.$ordInSession);
4631   };
4632     
4633   /**
4634    * Return absolute ORD for the Component Space.
4635    *
4636    * @returns {baja.Ord}
4637    */
4638   baja.ComponentSpace.prototype.getAbsoluteOrd = function () {
4639     if (this.$host === null) {
4640       return baja.Ord.DEFAULT;
4641     }
4642     return baja.Ord.make(this.$host.getAbsoluteOrd().toString() + "|" + this.$ordInSession);
4643   };
4644     
4645   /**
4646    * Find the Component via its handle (null if not found).
4647    * <p>
4648    * This method does not result in any network calls.
4649    *
4650    * @private
4651    *
4652    * @param {String} handle the Component's handle.
4653    *
4654    * @returns the Component via its handle (null if not found).
4655    */
4656   baja.ComponentSpace.prototype.findByHandle = function (handle) {
4657     strictArg(handle, String);
4658     return bajaDef(this.$map[handle], null);
4659   }; 
4660   
4661   /**
4662    * Find the Component via its handle (null if not found).
4663    * <p>
4664    * This method may result in an <strong>asynchronous</strong> network call if 
4665    * the Component can't be found locally and the Space is a Proxy.
4666    * <p>
4667    * An Object Literal is used for the method's arguments.
4668    *
4669    * @private
4670    *
4671    * @param {Object} [obj] the Object Literal for the method's arguments.
4672    * @param {Function} [obj.ok] the ok callback. Called if the Component is resolved.
4673    *                            The Component instance will be passed to this function.
4674    * @param {Function} [obj.fail] the fail callback. Call if there's an error or the Component
4675    *                              can't be resolved.
4676    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
4677    */
4678   baja.ComponentSpace.prototype.resolveByHandle = function (obj) {
4679     obj = objectify(obj);
4680     
4681     var handle = obj.handle,
4682         cb = new Callback(obj.ok, obj.fail);
4683     
4684     try {
4685       var comp = this.findByHandle(handle);
4686       if (comp !== null) {
4687         cb.ok(comp);
4688       }
4689       else {
4690         throw new Error("Could not find Component from Handle");
4691       }
4692     }
4693     catch (err) {
4694       cb.fail(err);
4695     }
4696   }; 
4697   
4698   /**
4699    * Return true if this Component Space has Space callbacks.
4700    * <p>
4701    * Space callbacks are normally used to make network calls.
4702    *
4703    * @private
4704    *
4705    * @returns {Boolean}
4706    */   
4707   baja.ComponentSpace.prototype.hasCallbacks = function () {
4708     return this.$callbacks !== null;
4709   };
4710   
4711   /**
4712    * Return the Space Callbacks.
4713    *
4714    * @private
4715    *
4716    * @returns Space Callbacks
4717    */ 
4718   baja.ComponentSpace.prototype.getCallbacks = function () {
4719     return this.$callbacks;
4720   };  
4721   
4722   /**
4723    * Sync the Component Space.
4724    * <p>
4725    * If the Space is a Proxy, this method will result in an 
4726    * <strong>asynchronous</strong> network call to sync the master Space with this one.
4727    * <p>
4728    * An Object Literal is used for the method's arguments.
4729    *
4730    * @param {Object} [obj] the Object Literal for the method's arguments.
4731    * @param {Function} [obj.ok] the ok callback. Called once the Component Space has
4732    *                            been successfully synchronized with the Server.
4733    * @param {Function} [obj.fail] the fail callback. Called If the Component Space 
4734    *                              can't be synchronized.
4735    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
4736    */
4737   baja.ComponentSpace.prototype.sync = function (obj) {
4738     obj = objectify(obj, "ok");
4739     new Callback(obj.ok, obj.fail).ok();
4740   };
4741   
4742   /**
4743    * Return the Nav ORD of the Root Component.
4744    *
4745    * @private
4746    *
4747    * @returns {baja.Ord}
4748    */
4749   baja.ComponentSpace.prototype.getNavOrd = function () {
4750     return this.$root === null ? null : this.$root.getNavOrd();
4751   };
4752   
4753   /**
4754    * Access the Nav Children.
4755    *
4756    * @see baja.NavContainer#getNavChildren
4757    *
4758    * @returns {String}
4759    */
4760   baja.ComponentSpace.prototype.getNavChildren = function (obj) {
4761     obj = objectify(obj, "ok");
4762     if (this.$root) {
4763       this.$root.getNavChildren(obj);
4764     }
4765     else {
4766       obj.ok([]);
4767     }
4768   };
4769   
4770   /**
4771    * Return the Nav Icon.
4772    *
4773    * @returns {baja.Icon}
4774    */
4775   baja.ComponentSpace.prototype.getNavIcon = function () {
4776     return this.$root ? this.$root.getNavIcon() : baja.ComponentSpace.$super.prototype.getNavIcon.call(this);
4777   };
4778         
4779   /**
4780    * @class Represents a baja:Host in BajaScript.
4781    *
4782    * @name baja.Host
4783    * @extends baja.Object
4784    */  
4785   baja.Host = function () { 
4786     baja.Host.$super.apply(this, arguments);
4787   }.$extend(baja.NavContainer).registerType("baja:Host");
4788   
4789   /**
4790    * Return a Host's Nav ORD.
4791    *
4792    * @private
4793    *
4794    * @returns {baja.Ord}
4795    */
4796   baja.Host.prototype.getNavOrd = function () {
4797     return this.getAbsoluteOrd();
4798   };
4799   
4800   /**
4801    * Return a Host's Absolute ORD.
4802    *
4803    * @private
4804    *
4805    * @returns {baja.Ord}
4806    */
4807   baja.Host.prototype.getAbsoluteOrd = function () {
4808     return null;
4809   };
4810   
4811   /**
4812    * @class Represents a baja:LocalHost in BajaScript.
4813    *
4814    * @name baja.LocalHost
4815    * @extends baja.Host
4816    */  
4817   baja.LocalHost = function () { 
4818     baja.LocalHost.$super.call(this, {
4819       navName: "localhost",
4820       icon: "module://icons/x16/localhost.png"
4821     });
4822   }.$extend(baja.Host).registerType("baja:LocalHost");
4823   
4824   /**
4825    * Return a Local Host's Absolute ORD.
4826    *
4827    * @private
4828    *
4829    * @returns {baja.Ord}
4830    */
4831   baja.LocalHost.prototype.getAbsoluteOrd = function () {
4832     return baja.Ord.make("local:");
4833   };
4834 
4835   ////////////////////////////////////////////////////////////////
4836   // Subscriber
4837   //////////////////////////////////////////////////////////////// 
4838   
4839   /**
4840    * @class Component Subscriber used to subscribe to multiple Components for events.
4841    *
4842    * @name baja.Subscriber
4843    * @extends BaseBajaObj
4844    */    
4845   baja.Subscriber = function () {
4846     this.$comps = [];
4847   }.$extend(BaseBajaObj);
4848   
4849   // Mix-in the event handlers for baja.Subscriber
4850   baja.event.mixin(baja.Subscriber.prototype);
4851   
4852   // The following comments have been added for the benefit of JsDoc Toolkit...
4853   
4854   /**
4855    * Attach an Event Handler to this Subscriber.
4856    * <p>
4857    * A Subscriber can be used to subscribe to multiple Components
4858    * in the Station and can be used to listen for Component Events. 
4859    * For instance, a common one would be a Property changed event...
4860    * <pre>
4861    *   // sub is a Subscriber and is subscribed to a Component
4862    *   sub.attach("changed", function (prop, cx) {
4863    *     if (prop.getName() === "out") {
4864    *       baja.outln("The output of the point is: " + this.getOutDisplay());
4865    *     }
4866    *   });
4867    * </pre>
4868    * An event handler consists of a name and a function. When the
4869    * function is called, 'this' will map to the target Component.
4870    * <p> 
4871    * Here are some examples of the different event handlers that can be 
4872    * attached to a Component...
4873    * <pre>
4874    *   // Property Changed
4875    *   sub.attach("changed", function (prop, cx) {
4876    *     // prop: the Property that has changed
4877    *     // cx: the Context (used internally)
4878    *   });
4879    *   
4880    *   // Property Added
4881    *   sub.attach("added", function (prop, cx) {
4882    *     // prop: the Property that has been added
4883    *     // cx: the Context (used internally)
4884    *   });
4885    *   
4886    *   // Property Removed
4887    *   sub.attach("removed", function (prop, val, cx) {
4888    *     // prop: the Property that has been removed
4889    *     // val: the old value of the Property
4890    *     // cx: the Context (used internally)
4891    *   });
4892    *   
4893    *   // Property Renamed
4894    *   sub.attach("renamed", function (prop, oldName, cx) {
4895    *     // prop: the Property that has been renamed
4896    *     // oldName: the old slot name
4897    *     // cx: the Context (used internally)
4898    *   });
4899    *   
4900    *   // Dynamic Slots Reordered
4901    *   sub.attach("reordered", function (cx) {
4902    *     // cx: the Context (used internally)
4903    *   });
4904    *   
4905    *   // Topic Fired
4906    *   sub.attach("topicFired", function (topic, event, cx) {
4907    *     // topic: the Topic that has been fired
4908    *     // event: the Topic event data (can be null)
4909    *     // cx: the Context (used internally)
4910    *   });
4911    *   
4912    *   // Slot Flags Changed
4913    *   sub.attach("flagsChanged", function (slot, cx) {
4914    *     // slot: the slot whose flags have changed
4915    *     // cx: the Context (used internally)
4916    *   });
4917    *   
4918    *   // Slot Facets Changed
4919    *   sub.attach("facetsChanged", function (slot, cx) {
4920    *     // slot: the slot whose facets have changed
4921    *     // cx: the Context (used internally)
4922    *   });
4923    * 
4924    *   // Component subscribed
4925    *   sub.attach("subscribed", function (cx) {
4926    *     // cx: the Context (used internally)
4927    *   });
4928    * 
4929    *   // Component unsubscribed
4930    *   sub.attach("unsubscribed", function (cx) {
4931    *     // cx: the Context (used internally)
4932    *   });
4933    *
4934    *   // Component unmounted (called just before Component is removed from parent)
4935    *   sub.attach("unmount", function (cx) {
4936    *     // cx: the Context (used internally)
4937    *   });
4938    * 
4939    *   // Component renamed in parent
4940    *   sub.attach("componentRenamed", function (oldName, cx) {
4941    *     // cx: the Context (used internally)
4942    *   });
4943    * 
4944    *   // Component's flags changed in parent
4945    *   sub.attach("componentFlagsChanged", function (cx) {
4946    *     // cx: the Context (used internally)
4947    *   });
4948    * 
4949    *   // Component's facets changed in parent
4950    *   sub.attach("componentFacetsChanged", function (cx) {
4951    *     // cx: the Context (used internally)
4952    *   });
4953    * 
4954    *   // Component reordered in parent
4955    *   sub.attach("componentReordered", function (cx) {
4956    *     // cx: the Context (used internally)
4957    *   });
4958    * </pre>
4959    * <p>
4960    * An Object Literal can be used to specify multiple handlers. For example...
4961    * <pre>
4962    *   var sub = new baja.Subscriber();
4963    *   sub.attach({
4964    *     changed: function (prop, cx) {
4965    *     },
4966    *     subscribed: function (cx) {
4967    *     }
4968    *   });
4969    * </pre>
4970    * <p>
4971    * Spaces can be used in a name to specify a function for multiple events...
4972    * <pre>
4973    *   var sub = new baja.Subscriber();
4974    *   sub.attach("subscribed changed", function () {
4975    *     updateGui(this);
4976    *   });
4977    * </pre>
4978    *
4979    * @function
4980    * @name baja.Subscriber#attach
4981    *
4982    * @see baja.Component#attach
4983    * @see baja.Subscriber#detach
4984    * @see baja.Subscriber#getHandlers
4985    * @see baja.Subscriber#hasHandlers
4986    * @see baja.Slot
4987    *
4988    * @param {String} event handler name.
4989    * @param {Function} func the event handler function.
4990    */  
4991   
4992   /**
4993    * Detach an Event Handler from the Subscriber.
4994    * <p>
4995    * If no arguments are used with this method then all events are removed.
4996    * <p>
4997    * For a list of all the event handlers, please see {@link baja.Subscriber#attach}.
4998    * <p>
4999    * A String with spaces can be supplied to remove multiple event handlers. For example...
5000    * <pre>
5001    *   sub.detach("subscribed changed"); // Remove all subscribed and changed event handlers
5002    * </pre>
5003    * 
5004    * @function
5005    * @name baja.Subscriber#detach
5006    *
5007    * @see baja.Component#attach
5008    * @see baja.Subscriber#attach
5009    * @see baja.Subscriber#getHandlers
5010    * @see baja.Subscriber#hasHandlers
5011    *
5012    * @param {String} [hName] the name of the handler to detach from the Subscriber.
5013    * @param {Function} [func] the function to remove from the Subscriber. It's recommended to supply this just in case
5014    *                          other scripts have added event handlers.
5015    */
5016     
5017   /**
5018    * Return an array of event handlers.
5019    * <p>
5020    * For a list of all the event handlers, please see {@link baja.Subscriber#attach}.
5021    * <p>
5022    * To access multiple handlers, insert a space between the handler names.
5023    *
5024    * @function
5025    * @name baja.Subscriber#getHandlers
5026    *
5027    * @see baja.Component#attach
5028    * @see baja.Subscriber#detach
5029    * @see baja.Subscriber#attach
5030    * @see baja.Subscriber#hasHandlers
5031    *
5032    * @param {String} hName the name of the handler
5033    * @returns {Array}
5034    */
5035   
5036   /**
5037    * Return true if there any handlers registered for the given handler name.
5038    * <p>
5039    * If no handler name is specified then test to see if there are any handlers registered at all.
5040    * <p>
5041    * For a list of all the event handlers, please see {@link baja.Subscriber#attach}.
5042    * <p>
5043    * Multiple handlers can be tested for by using a space character between the names.
5044    *
5045    * @function
5046    * @name baja.Subscriber#hasHandlers
5047    *
5048    * @see baja.Component#attach
5049    * @see baja.Subscriber#detach
5050    * @see baja.Subscriber#attach
5051    * @see baja.Subscriber#getHandlers
5052    *
5053    * @param {String} [hName] the name of the handler. If undefined, then see if there are any 
5054    *                         handlers registered at all.
5055    * @returns {Boolean}
5056    */
5057     
5058   /**
5059    * Return an array of the Components currently being 
5060    * subscribed to by this Subscriber.
5061    *
5062    * @returns {Array} a copy of the array used to subscribe Components (baja.Component).
5063    */
5064   baja.Subscriber.prototype.getComponents = function () {
5065     return this.$comps.slice();
5066   };
5067   
5068   /**
5069    * Subscribe a Component or a number of Components.
5070    * <p>
5071    * This will put the Components into subscription if they are not already subscribed and are mounted.
5072    * The Subscription will last until the page is refreshed or unsubscribe is called.
5073    * <p>
5074    * If the Components are mounted and able to be subscribed, this will result in 
5075    * an <strong>asynchronous</strong> network call.
5076    * <p>
5077    * A Component instance, array of Components or an optional Object Literal can be used to 
5078    * specify the method's arguments...
5079    * <pre>
5080    *   // Subscribe a single Component
5081    *   sub.subscribe(aComp);
5082    *
5083    *   // ...or subscribe an array of Components...
5084    *   sub.subscribe([aComp1, aComp2]);
5085    *
5086    *   // ...or use an Object Literal for more arguments...
5087    *   sub.subscribe({
5088    *     comps: [aComp1, aComp2], // Can also just be an singular Component instance
5089    *     ok: function () {
5090    *       // Called once the Components are subscribed (optional)
5091    *     },
5092    *     fail: function (err) {
5093    *       // Called if the Components fail to subscribe (optional)
5094    *     },
5095    *     batch // if defined, any network calls will be batched into this object (optional)
5096    *   });
5097    * </pre>
5098    * For callbacks, the 'this' keyword is set to the comps property.
5099    *
5100    * @param {Object} [obj] the Object Literal used for the method's arguments.
5101    * @param {Function} [obj.ok] the ok callback. Called once the Components have been subscribed.
5102    * @param {Function} [obj.fail] the fail callback. Called if the Components fail to subscribe.
5103    *                              Any errors will be passed to this function.
5104    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
5105    */
5106   baja.Subscriber.prototype.subscribe = function (obj) {  
5107     obj = objectify(obj, "comps");
5108     
5109     var comps = obj.comps,
5110         cb = new Callback(obj.ok, obj.fail, obj.batch);
5111         
5112     // Ensure 'this' is Component in callbacks...
5113     setContextInOkCallback(comps, cb);
5114     setContextInFailCallback(comps, cb);
5115     
5116     try {    
5117       // Ensure we have an array of valid Components to subscribe too
5118       if (!(comps instanceof Array)) {
5119         comps = [comps];
5120       }
5121       
5122       var c, i;
5123 
5124       // Check for errors first
5125       for (i = 0; i < comps.length; ++i) {
5126         c = comps[i];
5127       
5128         // Make sure we have a Component
5129         strictArg(c, baja.Component); 
5130         
5131         if (!c.isMounted()) {
5132           throw new Error("Cannot subscribe unmounted Component!");
5133         }
5134       } 
5135 
5136       var space = null,   
5137           ords = [],  
5138           compsToSub = [],          
5139           prevSub;    
5140       
5141       // Remove references and see if we need a network call    
5142       for (i = 0; i < comps.length; ++i) {
5143         c = comps[i];
5144             
5145         if (!space) {
5146           space = c.getComponentSpace();
5147         }
5148         
5149         prevSub = c.isSubscribed();
5150               
5151         // Add to this Subscribers Component array
5152         if (!this.$comps.contains(c)) {
5153           this.$comps.push(c);
5154         }
5155       
5156         // Make sure this Subscriber is listed in the Subscribed Component
5157         if (!c.$subs.contains(this)) {
5158           c.$subs.push(this);
5159         }
5160         
5161         // If the Component is now subscribed but was previously unsubscribed
5162         // then make a network call
5163         if (c.isSubscribed() && !prevSub) {
5164           ords.push("h:" + c.getHandle());
5165           compsToSub.push(c);
5166         }
5167       }
5168           
5169       // If there is nothing to subscribe to at this point then just bail
5170       if (ords.length > 0 && space.hasCallbacks() && bajaDef(obj.netCall, true)) { 
5171       
5172         // Signal that each Component has been subscribed
5173         cb.addOk(function (ok, fail, resp) {
5174           var i;
5175           for (i = 0; i < compsToSub.length; ++i) {
5176             try {
5177                compsToSub[i].$fw("fwSubscribed");
5178             }
5179             catch (err) {
5180               baja.error(err);
5181             }
5182           }
5183           
5184           ok();
5185         });
5186         
5187         // Make the network call through the Space    
5188         space.getCallbacks().subscribe(ords, cb, obj.importAsync);
5189       }
5190       else {
5191         cb.ok();
5192       }
5193     }
5194     catch (err) {
5195       cb.fail(err);
5196     }
5197   };
5198   
5199   /**
5200    * An internal private method for subscribing Components via their ORDs. Please note, this method
5201    * is strictly intended for Tridium developers only!
5202    *
5203    * @private
5204    * @internal
5205    *
5206    * @param {Object} [obj] the Object Literal used for the method's arguments.
5207    * @param {Array} ords an Array of String ORDs that should resolve to Components for subscription.
5208    * @param {baja.ComponentSpace} space the Component Space used for ORD resolution.
5209    * @param {Function} [obj.ok] the ok callback. Called once the Components have been subscribed.
5210    * @param {Function} [obj.fail] the fail callback. Called if the Components fail to subscribe.
5211    *                              Any errors will be passed to this function.
5212    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
5213    */
5214   baja.Subscriber.prototype.$ordSubscribe = function (obj) {      
5215     var ords = obj.ords,
5216         space = obj.space,
5217         cb = new Callback(obj.ok, obj.fail, obj.batch),
5218         that = this;
5219     
5220     try {     
5221       // Ensure these Components are all subscribed
5222       cb.addOk(function(ok, fail, handles) {
5223         // Remove references and see if we need a network call    
5224         var i,
5225             c,
5226             prevSub;
5227             
5228         for (i = 0; i < handles.length; ++i) {
5229           // Attempt to find the Component locally
5230           c = space.findByHandle(handles[i]);     
5231           
5232           if (c) {
5233             // Mark the Component as subscribed         
5234             prevSub = c.isSubscribed();
5235                       
5236             // Add to this Subscribers Component array
5237             if (!that.$comps.contains(c)) {
5238               that.$comps.push(c);
5239             }
5240           
5241             // Make sure this Subscriber is listed in the Subscribed Component
5242             if (!c.$subs.contains(that)) {
5243               c.$subs.push(that);
5244             }
5245             
5246             // If this is now subscribed then fire the relevant callback
5247             if (c.isSubscribed() && !prevSub) {
5248               try {
5249                  c.$fw("fwSubscribed");
5250               }
5251               catch (err) {
5252                 baja.error(err);
5253               }
5254             }
5255           }
5256           else {
5257             baja.error("Could not Batch Resolve Subscribe: " + handles[i]);
5258           }
5259           
5260           ok();
5261         }
5262       });
5263              
5264       // Make the network call through the Space    
5265       space.getCallbacks().subscribe(ords, cb, /*importAsync*/false);
5266     }
5267     catch (err) {
5268       cb.fail(err);
5269     }
5270   };
5271   
5272   /**
5273    * Unsubscribe a Component or a number of Components.
5274    * <p>
5275    * This will unsubscribe the mounted Components if they are not already unsubscribed.
5276    * <p>
5277    * If the Components are able to be unsubscribed, this will result in 
5278    * an <strong>asynchronous</strong> network call.
5279    * <p>
5280    * A Component instance, array of Components or an optional Object Literal can be used to 
5281    * specify the method's arguments...
5282    * <pre>
5283    *   // Unsubscribe a single Component
5284    *   sub.unsubscribe(aComp);
5285    *
5286    *   // ...or unsubscribe an array of Components...
5287    *   sub.unsubscribe([aComp1, aComp2]);
5288    *
5289    *   // ...or use an Object Literal for more arguments...
5290    *   sub.unsubscribe({
5291    *     comps: [aComp1, aComp2], // Can also just be an singular Component instance
5292    *     ok: function () {
5293    *       // Called once the Components are unsubscribed (optional)
5294    *     },
5295    *     fail: function (err) {
5296    *       // Called if the Components fail to unsubscribe (optional)
5297    *     },
5298    *     batch // if defined, any network calls will be batched into this object (optional)
5299    *   });
5300    * </pre>
5301    * For callbacks, the 'this' keyword is set to the comps property.
5302    *
5303    * @param {Object} [obj] the Object Literal used for the method's arguments.
5304    * @param {Function} [obj.ok] the ok callback. Called once the Components have been unsubscribed.
5305    * @param {Function} [obj.fail] the fail callback. Called if the Components fail to subscribe.
5306    *                              Any errors will be passed to this function.
5307    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
5308    */
5309   baja.Subscriber.prototype.unsubscribe = function (obj) { 
5310     obj = objectify(obj, "comps");
5311   
5312     var comps = obj.comps,
5313         cb = new Callback(obj.ok, obj.fail, obj.batch);
5314     
5315     // Ensure 'this' is Component in callbacks...
5316     setContextInOkCallback(this, cb);
5317     setContextInFailCallback(this, cb);
5318           
5319     try {        
5320       // Ensure we have an array of valid Components to subscribe too
5321       if (!(comps instanceof Array)) {
5322         comps = [comps];
5323       }
5324       
5325       var c, i, j, k;
5326       
5327       // Check for errors first
5328       for (i = 0; i < comps.length; ++i) {
5329         c = comps[i];
5330       
5331         // Make sure we have a Component
5332         strictArg(c, baja.Component); 
5333       }
5334       
5335       var space = null,    
5336           ords = [],
5337           prevSub;
5338       
5339       // Add references and see if we need a network call
5340       for (i = 0; i < comps.length; ++i) {
5341         c = comps[i];
5342             
5343         if (!space) {
5344           space = c.getComponentSpace();
5345         }
5346         
5347         prevSub = c.isSubscribed();
5348                     
5349         // Attempt to remove Component from this Subscribers Component list
5350         for (j = 0; j < this.$comps.length; ++j) {
5351           if (this.$comps[j] === c) {
5352             this.$comps.splice(j, 1);
5353             break;
5354           }
5355         }
5356         
5357         // Remove this Subscriber from the Component
5358         for (k = 0; k < c.$subs.length; ++k) { 
5359           if (c.$subs[k] === this) {        
5360             c.$subs.splice(k, 1);
5361             break;
5362           }
5363         }
5364         
5365         // If the Component is not subscribed but was previously subscribed then make a network call
5366         if (!c.isSubscribed() && prevSub && c.isMounted()) {
5367           ords.push("h:" + c.getHandle());
5368         }
5369       }
5370       
5371       // If there is nothing to unsubscribe at this point then just bail
5372       if (ords.length > 0 && space.hasCallbacks()) {   
5373         // Make the network call through the Space    
5374         space.getCallbacks().unsubscribe(ords, cb);
5375       }
5376       else {
5377         cb.ok();
5378       }
5379     }
5380     catch (err) {
5381       cb.fail(err);
5382     }
5383   };
5384   
5385   /**
5386    * Unsubscribe all Components from a Subscriber.
5387    * <p>
5388    * This will unregister all Components from this Subscriber.
5389    * <p>
5390    * If the Components are able to be unsubscribed, this will result in 
5391    * an <strong>asynchronous</strong> network call.
5392    * <p>
5393    * An Object Literal is used to specify the method's arguments...
5394    * <pre>
5395    *   // ...or use an Object Literal for more arguments...
5396    *   sub.unsubscribeAll({
5397    *     ok: function () {
5398    *       // Called once the Components are unsubscribed (optional)
5399    *     },
5400    *     fail: function (err) {
5401    *       // Called if the Components fail to unsubscribe (optional)
5402    *     },
5403    *     batch // if defined, any network calls will be batched into this object (optional)
5404    *   });
5405    * </pre>
5406    * For callbacks, the 'this' keyword is set to the internal component array.
5407    *
5408    * @param {Object} [obj] the Object Literal used for the method's arguments.
5409    * @param {Function} [obj.ok] the ok callback. Called once the Components have been unsubscribed.
5410    * @param {Function} [obj.fail] the fail callback. Called if the Components fail to subscribe.
5411    *                              Any errors will be passed to this function.
5412    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
5413    */
5414   baja.Subscriber.prototype.unsubscribeAll = function (obj) {
5415     obj = objectify(obj);
5416     obj.comps = this.$comps.slice();
5417     this.unsubscribe(obj);
5418   };
5419     
5420   /**
5421    * Return true if the Component is subscribed in this Subscriber.
5422    *
5423    * @param {Object} comp  the Component to be tested for Subscription.
5424    * @returns {Boolean}
5425    */
5426   baja.Subscriber.prototype.isSubscribed = function (comp) {
5427     strictArg(comp, baja.Component);
5428     return this.$comps.contains(comp);
5429   };
5430   
5431   ////////////////////////////////////////////////////////////////
5432   // ControlPoint
5433   //////////////////////////////////////////////////////////////// 
5434   
5435   /**
5436    * @class Represents a control:ControlPoint in BajaScript.
5437    *
5438    * @name baja.ControlPoint
5439    * @private
5440    * @inner
5441    * @extends baja.Component
5442    */  
5443   var ControlPoint = function () {
5444     ControlPoint.$super.apply(this, arguments);
5445   }.$extend(baja.Component).registerType("control:ControlPoint");
5446   
5447   /**
5448    * Return the Facets for a Slot.
5449    * <p>
5450    * If no arguments are provided and the Complex has a parent, the 
5451    * facets for the parent's Property will be returned. 
5452    *
5453    * @name baja.ControlPoint#getFacets
5454    * @function
5455    * @private
5456    * @inner
5457    *
5458    * @param {baja.Slot|String} [slot]  the Slot or Slot name.
5459    * @returns {baja.Facets} the Facets for the Slot (or null if Slot not found) or
5460    *                        the parent's Property facets.
5461    */
5462   ControlPoint.prototype.getFacets = function (slot) {
5463     // Attempt to match Station Component's 'getSlotFacets' implementation...
5464     if (slot) {
5465       slot = this.getSlot(slot);
5466       if (slot) {
5467         var nm = slot.getName();
5468         if (nm.match(/^(out|in[0-9][1-6]?|fallback|override|emergencyOverride|set)/)) {
5469           return this.get("facets");
5470         }
5471       }      
5472     }    
5473     
5474     // Call base class 'getFacets'
5475     return ControlPoint.$super.prototype.getFacets.apply(this, arguments);
5476   };
5477   
5478 }(baja, BaseBajaObj));