1 //
  2 // Copyright 2010, Tridium, Inc. All Rights Reserved.
  3 //
  4 
  5 /**
  6  * Box Component Space Architecture for BajaScript.
  7  *
  8  * @author Gareth Johnson
  9  * @version 1.0.0.0
 10  */
 11 
 12 //JsLint options (see http://www.jslint.com )
 13 /*jslint rhino: true, onevar: false, plusplus: true, white: true, undef: false, nomen: false, eqeqeq: true, 
 14 bitwise: true, regexp: true, newcap: true, immed: true, strict: false, indent: 2, vars: true, continue: true */
 15 
 16 // Globals for JsLint to ignore 
 17 /*global baja, syncVal, BaseBajaObj, syncProps, syncComp*/ 
 18   
 19 (function boxcs(baja) {
 20 
 21   // Use ECMAScript 5 Strict Mode
 22   "use strict";
 23   
 24   //
 25   // Please note, a context may a commit flag that indicates not to trap
 26   // the update to a network call. The serverDecode flag is used as an 
 27   // optimization by BajaScript's Component safety checks. It's assumed
 28   // any BSON decoded from the Server is probably ok and hence we
 29   // can short circuit some of the checks. The syncStructVals
 30   // flag is used exclusively by Complex 'set' to determine whether
 31   // any incoming struct values should be synced instead being
 32   // replaced outright.
 33   //
 34   
 35   // Create local for improved minification
 36   var strictArg = baja.strictArg,
 37       bajaDef = baja.def,
 38       serverDecodeContext = baja.$serverDecodeContext,
 39       bsonDecodeValue = baja.bson.decodeValue,
 40       Callback = baja.comm.Callback;
 41   
 42   ////////////////////////////////////////////////////////////////
 43   // SyncOps
 44   //////////////////////////////////////////////////////////////// 
 45   
 46   // A map of of SyncOp constructors used in decoding SyncOps
 47   var syncOps = {};
 48   
 49   /**
 50    * @class Base SyncOp.
 51    * <p>
 52    * All other SyncOps extend from this constructor.
 53    *
 54    * @name SyncOp
 55    * @extends BaseBajaObj
 56    * @inner
 57    * @private
 58    */ 
 59   var SyncOp = function () {
 60     this.$arg = null;
 61   }.$extend(BaseBajaObj); 
 62   
 63   // The unique identifier for the SyncOp  
 64   SyncOp.id = "";
 65   
 66   /**
 67    * Return the id for the SyncOp.
 68    * <p>
 69    * The id is used for encoding and decoding a SyncOp.
 70    *
 71    * @private
 72    *
 73    * @returns {String}
 74    */
 75   SyncOp.prototype.getId = function () {
 76     return this.constructor.id;
 77   };
 78     
 79   /**
 80    * Perform a syncTo network call.
 81    *
 82    * @private
 83    *
 84    * @param space the Component Space we're syncing too.
 85    * @param {baja.comm.Callback} cb the callback handler.
 86    */    
 87   SyncOp.prototype.syncTo = function (space, cb) {
 88 
 89     // Create BSON SyncOp data structure
 90     var sync = {
 91       nm: "sync",
 92       ver: 1.0,
 93       ops: [ this.$arg ]
 94     };
 95     
 96     // TODO: at some point, we'll want to batch up the syncTo requests and have one syncFrom request
 97     // for great bandwidth and efficiency. At the same time, we should also try and introduce the idea
 98     // of unsolicited messages that we'll subscribe too. All of the syncFrom events would then come from
 99     // unsolicited messages that have been registered for with a topic name. This sets us up for eventually
100     // using a WebSocket instead of constant HTTP request/responses. When receiving a BOX frame with unsolicited
101     // messages, the unsolicited messages should be processed first before dealing with the responses
102      
103     var failedCalled = false,
104         syncToResp;
105     
106     // Only call the ok if fail hasn't been called on the syncTo handler
107     cb.addOk(function (ok, fail, resp) {
108       if (!failedCalled) {
109         // Pass results of the committed syncOps into the result of the original callback
110         ok(syncToResp);      
111       }
112     });
113         
114     var syncToCb = new Callback(baja.ok, cb.fail, cb.getBatch());
115     
116     syncToCb.addOk(function (ok, fail, resp) {
117       // Get results of syncTo (which are the results of the committed syncOps)
118       syncToResp = resp;
119       ok(resp);
120     });
121 
122     // Mark that fail has been called
123     syncToCb.addFail(function (ok, fail, err) {
124       failedCalled = true;
125       fail(err);
126     });
127      
128     // syncTo  
129     baja.comm.serverHandlerCall(space.getAbsoluteOrd().toString(),
130                                 "syncTo",
131                                 sync,
132                                 syncToCb);  
133     
134     // syncFrom
135     space.getCallbacks().poll(cb);                                  
136   };
137     
138   /**
139    * @class Add SyncOp.
140    *
141    * @name AddOp
142    * @extends SyncOp
143    * @inner
144    * @private
145    *
146    * @param comp the Component the add is happening upon.
147    * @param {String} slotName the name of the slot being added.
148    * @param val the value for the add operation.
149    * @param {Number} flags the slot facets.
150    * @param {baja.Facets} facets the slot facets.
151    */ 
152   var AddOp = function (comp, slotName, val, flags, facets) {
153     AddOp.$super.apply(this, arguments);
154     
155     // TODO: What about getting the name of the Property that was created 
156     // from the Server???
157     
158     // Encode argument to a data structure
159     var a = {
160       nm: this.getId(),
161       h: comp.getHandle(),
162       b: baja.bson.encodeValue(val) // Encode the value to BSON
163     };
164         
165     if (slotName !== null) {
166       a.n = slotName;
167     }  
168     
169     if (flags !== 0) {
170       a.f = baja.Flags.encodeToString(flags);
171     }
172     
173     if (facets !== null && facets !== baja.Facets.DEFAULT) {
174       a.facets = facets.encodeToString();
175     }
176 
177     this.$arg = a;    
178   }.$extend(SyncOp); 
179   
180   AddOp.id = "a";
181   syncOps[AddOp.id] = AddOp; 
182   
183   /**
184    * Decode and commit the SyncOp.
185    *
186    * @private
187    *
188    * @param comp the Component being added too.
189    * @param sp the syncOp data structure to be decoded.
190    */
191   AddOp.decodeAndCommit = function (comp, sp) {
192     // TODO: Shouldn't need to add the 'get' check on the end of this if statement but it'll
193     // have to do for now. Really the Server should keep track of what Components the client
194     // has loaded instead of just firing everything down it gets!
195   
196     if (comp !== null && comp.get(sp.n) === null) {
197       var name = bajaDef(sp.n, null),
198           displayName = sp.dn,
199           display = sp.b.d,
200           flags = baja.Flags.decodeFromString(bajaDef(sp.f, "")),  
201           fcts = baja.Facets.DEFAULT.decodeFromString(bajaDef(sp.facets, ""));
202     
203       // Perform Component add with Commit Context
204       comp.add({
205         "slot": name, 
206         "value": bsonDecodeValue(sp.b, serverDecodeContext), 
207         "flags": flags, 
208         "facets": fcts, 
209         "cx": { commit: true, displayName: displayName, display: display, serverDecode: true }
210       });
211     }
212   };
213   
214   /**
215    * @class Set SyncOp
216    *
217    * @name SetOp
218    * @extends SyncOp
219    * @inner
220    * @private
221    *
222    * @param comp the Component the set is happening upon.
223    * @param {Array} propPath an array of Property names for the set.
224    * @param val the value being used in the set.
225    */ 
226   var SetOp = function (comp, propPath, val) {
227     SetOp.$super.apply(this, arguments); 
228     
229     // Encode argument to a data structure
230     var a = {
231       nm: this.getId(),
232       h: comp.getHandle(),
233       n: propPath.reverse().join("/"),
234       b: baja.bson.encodeValue(val) // Encode the value to BSON
235     };
236     
237     this.$arg = a; 
238     
239   }.$extend(SyncOp); 
240   
241   SetOp.id = "s";
242   syncOps[SetOp.id] = SetOp; 
243   
244   /**
245    * Decode and commit the SyncOp.
246    *
247    * @private
248    *
249    * @param comp the Component the set is happening on.
250    * @param sp the syncOp data structure to be decoded.
251    */
252   SetOp.decodeAndCommit = function (comp, sp) {  
253     if (comp !== null) {  
254       // Decode the value and call 'set'
255       var names = sp.n.split("/"),
256           displayName = sp.dn,
257           display = sp.b.d,
258           target = comp,
259           n = null,
260           i;
261           
262       for (i = 0; i < names.length; ++i) {
263         if (n !== null) {
264           target = target.get(n);
265         }
266         n = names[i];
267       }
268       
269       // Set the desired target      
270       target.set({
271         "slot": n,
272         "value": bsonDecodeValue(sp.b, serverDecodeContext), 
273         "cx": { commit: true, serverDecode: true, syncStructVals: true, displayName: displayName, display: display }
274       });
275     }
276   };
277   
278   /**
279    * @class Remove SyncOp.
280    *
281    * @name RemoveOp
282    * @extends SyncOp
283    * @inner
284    * @private
285    *
286    * @param comp the Component the remove is happening upon.
287    * @param {baja.Slot} slot the Slot to remove from the Component.
288    */ 
289   var RemoveOp = function (comp, slot) {
290     RemoveOp.$super.apply(this, arguments); 
291 
292     // Encode argument to a data structure
293     var a = {
294       nm: this.getId(),
295       h: comp.getHandle(),
296       n: slot.getName()
297     };
298     
299     this.$arg = a; 
300     
301   }.$extend(SyncOp); 
302   
303   RemoveOp.id = "v";
304   syncOps[RemoveOp.id] = RemoveOp; 
305   
306   /**
307    * Decode and commit the SyncOp.
308    *
309    * @name RemoveOp.decodeAndCommit
310    * @function
311    * @private
312    *
313    * @param comp the Component the remove is happening on.
314    * @param sp the syncOp data structure to be decoded.
315    */
316   RemoveOp.decodeAndCommit = function (comp, sp) {  
317     if (comp !== null) {  
318       var name = sp.n,
319           slot = comp.getSlot(name);
320       
321       if (slot !== null) {
322         comp.remove({
323           "slot": slot, 
324           "cx": { commit: true, serverDecode: true }
325         });
326       }
327     }
328   };
329   
330   /**
331    * @class Fire Topic SyncOp.
332    *
333    * @name FireTopicOp
334    * @extends SyncOp
335    * @inner
336    * @private
337    *
338    * @param comp the Component the Topic is being fired from.
339    * @param {baja.Slot} slot the Topic Slot.
340    * @param event the event to be fired (can be null).
341    */ 
342   var FireTopicOp = function (comp, slot, event) {
343     FireTopicOp.$super.apply(this, arguments);   
344     
345     // Encode argument to a data structure
346     var a = {
347       nm: this.getId(),
348       h: comp.getHandle(),
349       n: slot.getName()
350     };
351     
352     if (event !== null) {
353       a.b = baja.bson.encodeValue(event); // Encode the value to BSON
354     }
355     
356     this.$arg = a;     
357   }.$extend(SyncOp); 
358   
359   FireTopicOp.id = "t";
360   syncOps[FireTopicOp.id] = FireTopicOp; 
361   
362   /**
363    * Decode and commit the SyncOp.
364    *
365    * @private
366    *
367    * @param comp the Component the Topic is being fired from.
368    * @param sp the syncOp data structure to be decoded.
369    */
370   FireTopicOp.decodeAndCommit = function (comp, sp) {  
371     if (comp !== null) {  
372       // Decode and fire the Component event        
373       // TODO: Propogate amoungst Knobs
374       
375       var name = sp.n,
376           slot = comp.getSlot(name),
377           display = "",
378           event = null;
379       
380       if (sp.b !== undefined) {
381         event = bsonDecodeValue(sp.b, serverDecodeContext);
382         display = sp.b.d;
383       }
384       
385       // Only fire this if the Topic Slot is loaded
386       if (slot !== null) {
387       
388         // Fire the Topic on the Component
389         comp.fire({
390           "slot": slot, 
391           "value": event, 
392           "cx": { commit: true, display: display, serverDecode: true }
393         }); 
394       }
395     }
396   };
397   
398   /**
399    * @class Load a Component's Slots SyncOp.
400    *
401    * @name LoadOp
402    * @extends SyncOp
403    * @inner
404    * @private
405    */
406   var LoadOp = function () {
407     LoadOp.$super.apply(this, arguments);   
408   }.$extend(SyncOp); 
409   
410   LoadOp.id = "l";
411   syncOps[LoadOp.id] = LoadOp; 
412   
413   /**
414    * Decode and commit the SyncOp.
415    *
416    * @name LoadOp.decodeAndCommit
417    * @function
418    * @private
419    *
420    * @param comp the Component to be loaded.
421    * @param sp the syncOp data structure to be decoded.
422    */
423   LoadOp.decodeAndCommit = function (comp, sp) {  
424     if (comp !== null) {
425       // Synchronize the two components together      
426       syncComp(bsonDecodeValue(sp.b, serverDecodeContext), comp);    
427     }
428   };
429   
430   /**
431    * @class Rename a dynamic Slot SyncOp.
432    *
433    * @name RenameOp
434    * @extends SyncOp
435    * @inner
436    * @private
437    *
438    * @param comp the Component the Topic is being fired from.
439    * @param {String} oldName the old name of the Slot.
440    * @param {String} newName the new name of the Slot.
441    */ 
442   var RenameOp = function (comp, oldName, newName) {
443     RenameOp.$super.apply(this, arguments);   
444     
445     // Encode argument to a data structure
446     var a = {
447       nm: this.getId(),
448       h: comp.getHandle(),
449       o: oldName,
450       n: newName
451     };
452     
453     this.$arg = a; 
454     
455   }.$extend(SyncOp); 
456   
457   RenameOp.id = "r";
458   syncOps[RenameOp.id] = RenameOp; 
459   
460   /**
461    * Decode and commit the SyncOp.
462    *
463    * @private
464    *
465    * @param comp the Component the rename will happen upon.
466    * @param sp the syncOp data structure to be decoded.
467    */
468   RenameOp.decodeAndCommit = function (comp, sp) {  
469     if (comp !== null) { 
470       var name = sp.n,
471           oldName = sp.o,
472           displayName = sp.dn,
473           display = sp.d,
474           slot = comp.getSlot(oldName);
475                 
476       if (slot !== null) {
477         comp.rename({
478           "slot": slot,
479           "newName": name, 
480           "cx": { commit: true, displayName: displayName, display: display, serverDecode: true }
481         });
482       }    
483     }
484   };
485   
486   /**
487    * @class Reorder a Component's dynamic Slots SyncOp.
488    *
489    * @name ReorderOp
490    * @extends SyncOp
491    * @inner
492    * @private
493    *
494    * @param comp the Component the reorder is happening on.
495    * @param {Array} a String array of dynamic Property names that specifies the new order.
496    */ 
497   var ReorderOp = function (comp, dynamicProperties) {
498     ReorderOp.$super.apply(this, arguments); 
499         
500     // Encode argument to a data structure
501     var a = {
502       nm: this.getId(),
503       h: comp.getHandle(),
504       o: dynamicProperties.join(";")
505     };
506     
507     this.$arg = a; 
508         
509   }.$extend(SyncOp); 
510   
511   ReorderOp.id = "o";
512   syncOps[ReorderOp.id] = ReorderOp; 
513   
514   /**
515    * Decode and commit the SyncOp.
516    *
517    * @name ReorderOp.decodeAndCommit
518    * @function
519    * @private
520    *
521    * @param comp the Component the reorder will happen upon.
522    * @param sp the syncOp data structure to be decoded.
523    */
524   ReorderOp.decodeAndCommit = function (comp, sp) {  
525     if (comp !== null) { 
526       var order = sp.o;     
527       comp.reorder({
528         "dynamicProperties": order.split(";"), 
529         "cx": { commit: true, serverDecode: true }
530       });
531     }
532   };
533   
534   /**
535    * @class Set Slot Flags SyncOp.
536    *
537    * @name SetFlagsOp
538    * @extends SyncOp
539    * @inner
540    * @private
541    *
542    * @param comp the Component for the slot the flags are being set upon.
543    * @param {baja.Slot} slot the target slot for the flags change.
544    * @param {Number} flags the new Slot flags.
545    */ 
546   var SetFlagsOp = function (comp, slot, flags) {
547     SetFlagsOp.$super.apply(this, arguments); 
548 
549     // Encode argument to a data structure
550     var a = {
551       nm: this.getId(),
552       h: comp.getHandle(),
553       n: slot.getName(),
554       f: baja.Flags.encodeToString(flags)
555     };
556     
557     this.$arg = a; 
558     
559   }.$extend(SyncOp); 
560   
561   SetFlagsOp.id = "f";
562   syncOps[SetFlagsOp.id] = SetFlagsOp; 
563   
564   /**
565    * Decode and commit the SyncOp.
566    *
567    * @private
568    *
569    * @param comp the Component the set flags op will happen upon.
570    * @param sp the syncOp data structure to be decoded.
571    */
572   SetFlagsOp.decodeAndCommit = function (comp, sp) {  
573     if (comp !== null) { 
574       var name = sp.n,
575           flags = baja.Flags.decodeFromString(sp.f),     
576           displayName = sp.dn,
577           display = sp.d;
578           
579       comp.setFlags({
580         "slot": name, 
581         "flags": flags, 
582         "cx": { commit: true, displayName: displayName, display: display, serverDecode: true }
583       });
584     }
585   };
586   
587   /**
588    * @class Set dynamic Slot Facets SyncOp.
589    *
590    * @name SetFacetsOp
591    * @extends SyncOp
592    * @inner
593    * @private
594    *
595    * @param comp the Component for the slot the facets are being set upon.
596    * @param {baja.Slot} slot the target dynamic slot for the facets change.
597    * @param {baja.Facets} facets the new Slot facets.
598    */
599   var SetFacetsOp = function (comp, slot, facets) {
600     SetFacetsOp.$super.apply(this, arguments); 
601 
602     // Encode argument to a data structure
603     var a = {
604       nm: this.getId(),
605       h: comp.getHandle(),
606       n: slot.getName(),
607       x: facets.encodeToString()
608     };
609     
610     this.$arg = a; 
611     
612   }.$extend(SyncOp); 
613   
614   SetFacetsOp.id = "x";
615   syncOps[SetFacetsOp.id] = SetFacetsOp; 
616   
617   /**
618    * Decode and commit the SyncOp.
619    *
620    * @private
621    *
622    * @param comp the Component the set facets op will happen upon.
623    * @param sp the syncOp data structure to be decoded.
624    */
625   SetFacetsOp.decodeAndCommit = function (comp, sp) {  
626     if (comp !== null) {
627       var name = sp.n,
628           fcts = baja.Facets.DEFAULT.decodeFromString(bajaDef(sp.x, "")),    
629           displayName = sp.dn,
630           display = sp.d;
631       
632       comp.setFacets({
633         "slot": name, 
634         "facets": fcts,
635         "cx": { commit: true, displayName: displayName, display: display, serverDecode: true }
636       });    
637     }
638   };
639   
640   /**
641    * @class Add Knob SyncOp
642    *
643    * @name AddKnobOp
644    * @extends SyncOp
645    * @inner
646    * @private
647    */
648   var AddKnobOp = function () {
649     AddKnobOp.$super.apply(this, arguments); 
650     throw new Error("Unsupported");
651   }.$extend(SyncOp); 
652   
653   AddKnobOp.id = "k";
654   syncOps[AddKnobOp.id] = AddKnobOp; 
655   
656   /**
657    * Decode and commit the SyncOp.
658    *
659    * @name AddKnobOp.decodeAndCommit
660    * @function
661    * @private
662    *
663    * @param comp the Component the knob will be added too.
664    * @param sp the syncOp data structure to be decoded.
665    */
666   AddKnobOp.decodeAndCommit = function (comp, sp) {  
667     if (comp !== null) {
668       comp.$fw("installKnob", baja.bson.decodeKnob(sp.nk), { commit: true, serverDecode: true });    
669     }
670   };
671   
672   /**
673    * @class Remove Knob SyncOp.
674    *
675    * @name RemoveKnobOp
676    * @extends SyncOp
677    * @inner
678    * @private
679    */
680   var RemoveKnobOp = function () {
681     RemoveKnobOp.$super.apply(this, arguments); 
682     throw new Error("Unsupported");
683   }.$extend(SyncOp); 
684   
685   RemoveKnobOp.id = "j";
686   syncOps[RemoveKnobOp.id] = RemoveKnobOp; 
687   
688   /**
689    * Decode and commit the SyncOp.
690    *
691    * @private
692    *
693    * @param comp the Component the knob will be added too.
694    * @param sp the syncOp data structure to be decoded.
695    */
696   RemoveKnobOp.decodeAndCommit = function (comp, sp) {  
697     if (comp !== null) {
698       comp.$fw("uninstallKnob", sp.id, sp.ss, { commit: true, serverDecode: true });    
699     }
700   };
701            
702   ////////////////////////////////////////////////////////////////
703   // BOX Component Space Callbacks
704   //////////////////////////////////////////////////////////////// 
705   
706   /**
707    * @class BOX Callbacks plugs into a Component Space so it can make network calls.
708    *
709    * @name BoxCallbacks
710    * @inner
711    * @private
712    */   
713   var BoxCallbacks = function (space) {
714     this.$space = space;
715   };
716   
717   /**
718    * Load Slots.
719    *
720    * @private
721    *
722    * @param {String} ord
723    * @param {Number} depth
724    * @param {baja.comm.Callback} cb
725    */
726   BoxCallbacks.prototype.loadSlots = function (ord, depth, cb) {    
727     // Intermediate callback
728     var space = this.$space;
729     
730     // Load Slots Argument
731     var arg = { 
732       o: ord,
733       d: depth
734     };
735     
736     var failedCalled = false;
737     
738     // Only call the ok if fail hasn't been called on the syncTo handler
739     cb.addOk(function (ok, fail, resp) {
740       if (!failedCalled) {
741         // Pass results of the committed syncOps into the result of the original callback
742         ok();      
743       }
744     });
745         
746     var loadSlotsCb = new Callback(baja.ok, cb.fail, cb.getBatch());
747 
748     // Mark that fail has been called
749     loadSlotsCb.addFail(function (ok, fail, err) {
750       failedCalled = true;
751       fail(err);
752     });
753     
754     // Make a call on the Server
755     baja.comm.serverHandlerCall(space.getAbsoluteOrd().toString(),
756                                 "loadSlots",
757                                 arg,
758                                 loadSlotsCb);
759     
760     // Make a component space sync    
761     this.poll(cb);
762   };
763   
764   /**
765    * Load Slot Path.
766    *
767    * @private
768    *
769    * @param {Array} slotPathInfo
770    * @param container
771    * @param {baja.comm.Callback} cb
772    * @param {Boolean} [importAsync] import any extra types asynchronously (true by default).
773    */
774   BoxCallbacks.prototype.loadSlotPath = function (slotPathInfo, container, cb, importAsync) {
775     var space = this.$space;
776     
777     importAsync = baja.def(importAsync, true);
778     
779     // Intermediate callback
780     cb.addOk(function (ok, fail, resp) {
781     
782       var newOk = function () {  
783         // Attempt to load the Slot in the Space
784         space.$fw("commitSlotInfo", resp); 
785         ok();
786       };
787       
788       var newFail = function (err) {
789         fail(err);
790       };
791       
792       if (resp) {  
793 
794         // Pre-emptively scan the BSON for Types that don't exist yet or have Contracts loaded
795         // and request them in one network call            
796         var unknownTypes = baja.bson.scanForUnknownTypes(resp),
797             importBatch = new baja.comm.Batch();
798         
799         if (unknownTypes.length > 0) {
800           baja.importTypes({
801             "typeSpecs": unknownTypes, 
802             "ok": newOk, 
803             "fail": newFail,
804             "batch": importBatch
805           });
806           
807           if (importAsync) {
808             importBatch.commit();
809           }
810           else {
811             importBatch.commitSync();
812           }
813         }
814         else {
815           newOk();
816         }
817       }
818       else {
819         newOk();
820       }
821     });
822     
823     // Build up the argument to send to the Server
824     var arg = {
825       spi: slotPathInfo,
826       bo: container.getNavOrd().toString()
827     };
828                 
829     // Make a call on the Server  
830     baja.comm.serverHandlerCall(space.getAbsoluteOrd().toString(),
831                                 "loadSlotPath",
832                                 arg,
833                                 cb);    
834   };
835     
836   /**
837    * Component Subscription.
838    *
839    * @private
840    *
841    * @param {Array} ords an array of ORDs to Components.
842    * @param {Boolean} [importAsync] make any Type and Contract imports asynchronous (false by default).
843    * @param {baja.comm.Callback} cb
844    */
845   BoxCallbacks.prototype.subscribe = function (ords, cb, importAsync) {
846     strictArg(ords, Array);
847     importAsync = bajaDef(importAsync, false);
848         
849     // Check there is something to subscribe too
850     if (ords.length === 0) {
851       throw new Error("Cannot Subscribe: nothing to subscribe too");
852     }
853                   
854     // Intermediate callback
855     var space = this.$space;
856     cb.addOk(function (ok, fail, resp) {  
857       var importOk = function () {
858         // Commit the sync ops
859         space.$fw("commitSyncOps", resp.e.ops);
860            
861         // Pass back an array of handles that were subscribed   
862         ok(resp.h);  
863       };
864 
865       // Pre-emptively scan the BSON for Types that don't exist yet or have Contracts loaded
866       // and request them in one network call            
867       var unknownTypes = baja.bson.scanForUnknownTypes(resp);            
868       if (unknownTypes.length > 0) {
869         var importBatch = new baja.comm.Batch();
870         baja.importTypes({
871           "typeSpecs": unknownTypes, 
872           "ok": importOk,
873           "fail": fail,
874           "batch": importBatch
875         });
876         
877         if (importAsync) {
878           importBatch.commit();
879         }
880         else {
881           importBatch.commitSync();
882         }
883       }
884       else {
885         importOk();
886       }      
887     });
888         
889     // Make a call on the Server
890     baja.comm.serverHandlerCall(space.getAbsoluteOrd().toString(),
891                                 "sub",
892                                 ords,
893                                 cb);   
894   };
895   
896   /**
897    * Component Unsubscription.
898    *
899    * @private
900    *
901    * @param {Array} ords an array of Components to unsubscribe.
902    * @param {baja.comm.Callback} cb
903    */
904   BoxCallbacks.prototype.unsubscribe = function (ords, cb) {
905     strictArg(ords, Array);
906         
907     // Check there is something to subscribe too
908     if (ords.length === 0) {
909       throw new Error("Cannot Unsubscribe: nothing to unsubscribe too");
910     }
911        
912     var newOk = function (comp) {
913       try {
914         comp.$fw("fwUnsubscribed");
915       }
916       catch (err) {
917         baja.error(err);
918       }
919     };
920        
921     // Intermediate callback
922     var space = this.$space;
923     cb.addOk(function (ok, fail, resp) {               
924       // Call unsubscribed callbacks
925       var i;
926       for (i = 0; i < ords.length; ++i) {
927         baja.Ord.make(ords[i]).get({
928           ok: newOk,
929           base: space
930         });
931       }
932       
933       ok();      
934     });
935         
936     // Make a call on the Server
937     baja.comm.serverHandlerCall(this.$space.getAbsoluteOrd().toString(),
938                                 "unsub",
939                                 ords,
940                                 cb);      
941   };
942   
943   /**
944    * Invoke an Action.
945    *
946    * @private
947    *
948    * @param comp the Component the Action will be invoked upon.
949    * @param {baja.Slot} the Action Slot.
950    * @param val the argument for the Action (can be null).
951    * @param {baja.comm.Callback} cb
952    */
953   BoxCallbacks.prototype.invokeAction = function (comp, action, val, cb) {
954         
955     // Intermediate callback
956     cb.addOk(function (ok, fail, resp) {
957       // Decode the value returned
958       var v = null;
959       if (resp !== null) {     
960         // TODO: scan response for unknown Types once batch end callback is fixed     
961         v = bsonDecodeValue(resp, serverDecodeContext);
962       }
963       ok(v);
964     });
965     
966     var arg = {
967       h: comp.getHandle(),
968       a: action.getName()
969     };
970     
971     // Encode value if available
972     if (val !== null) {
973       arg.b = baja.bson.encodeValue(val);
974     }
975     
976     // Make a call on the Server Side Component  
977     baja.comm.serverHandlerCall(this.$space.getAbsoluteOrd().toString(),
978                                 "invokeAction",
979                                 arg,
980                                 cb); 
981   }; 
982   
983   /**
984    * Get the Action Parameter Default Value.
985    *
986    * @private
987    *
988    * @param {baja.Component} comp
989    * @param {baja.Action} action
990    * @param {baja.comm.Callback} cb
991    */
992   BoxCallbacks.prototype.getActionParameterDefault = function (comp, action, cb) {    
993     // Intermediate callback to decode Action parameter default
994     cb.addOk(function (ok, fail, resp) {
995       // TODO: What about decoding Types in bulk here?
996       ok(resp === null ? null : bsonDecodeValue(resp, serverDecodeContext));
997     });
998   
999     // Make a call to get the Action Parameter Default for this Slot
1000     baja.comm.serverHandlerCall(this.$space.getAbsoluteOrd().toString(),
1001                                 "getActionParameterDefault",
1002                                 { "h": comp.getHandle(), "a": action.getName() },
1003                                 cb); 
1004   };
1005   
1006   /**
1007    * Invoke a Server Side Call.
1008    *
1009    * @private
1010    *
1011    * @param comp the Component for the Server Side Call.
1012    * @param {String} typeSpec
1013    * @param {String} methodName
1014    * @param val the argument for the Server Side Call (can be null).
1015    * @param {baja.comm.Callback} cb
1016    */
1017   BoxCallbacks.prototype.serverSideCall = function (comp, typeSpec, methodName, val, cb) {        
1018     // Add intermediate callback
1019     cb.addOk(function (ok, fail, resp) {
1020       if (resp !== null) {
1021         var importOk = function () {
1022           ok(bsonDecodeValue(resp, serverDecodeContext));
1023         };
1024       
1025         // Pre-emptively scan the BSON for Types that don't exist yet or have Contracts loaded
1026         // and request them in one network call            
1027         var unknownTypes = baja.bson.scanForUnknownTypes(resp);            
1028         if (unknownTypes.length > 0) {
1029           var importBatch = new baja.comm.Batch();
1030           baja.importTypes({
1031             "typeSpecs": unknownTypes, 
1032             "ok": importOk,
1033             "fail": fail,
1034             "batch": importBatch
1035           });
1036           
1037           if (cb.getBatch().isAsync()) {
1038             importBatch.commit();
1039           }
1040           else {
1041             importBatch.commitSync();
1042           }
1043         }
1044         else {
1045           importOk();
1046         }
1047       }
1048       else {
1049         ok(null);
1050       }
1051     });
1052     
1053     // Arguments    
1054     var arg = {
1055       h:  comp.getHandle(),
1056       ts: typeSpec,
1057       m:  methodName
1058     };
1059     
1060     // Encode value if available
1061     if (val !== null) {
1062       arg.b = baja.bson.encodeValue(val);
1063     }
1064     
1065     // Make a call on the Server
1066     baja.comm.serverHandlerCall(this.$space.getAbsoluteOrd().toString(),
1067                                 "serverSideCall",
1068                                 arg,
1069                                 cb);       
1070   };
1071     
1072   /**
1073    * Poll the Server for events.
1074    *
1075    * @private
1076    *
1077    * @param {baja.comm.Callback} cb
1078    */  
1079   BoxCallbacks.prototype.poll = function (cb) {    
1080     baja.comm.poll(cb);
1081   };
1082   
1083   /**
1084    * Convert a handle to a Slot Path.
1085    *
1086    * @private
1087    *
1088    * @param {String} handle
1089    * @param {baja.comm.Callback} cb
1090    */
1091   BoxCallbacks.prototype.handleToPath = function (handle, cb) {   
1092     // Intermediate callback to pass in SlotPath to callback
1093     cb.addOk(function (ok, fail, slotPathStr) {
1094       ok(new baja.SlotPath(slotPathStr));
1095     });
1096   
1097     // Make a call on the Server Side Component to unsubscribe  
1098     baja.comm.serverHandlerCall(this.$space.getAbsoluteOrd().toString(),
1099                                 "handleToPath",
1100                                 handle,
1101                                 cb); 
1102   };
1103   
1104   /**
1105    * Resolve a Service via its TypeSpec (moduleName:typeName).
1106    *
1107    * @private
1108    *
1109    * @param {String} typeSpec
1110    * @param {baja.comm.Callback} cb
1111    */
1112   BoxCallbacks.prototype.getService = function (typeSpec, cb) {
1113     strictArg(typeSpec, String);
1114     
1115     // Intermediate callback to resolve the SlotPath into the target Component
1116     var that = this;
1117     cb.addOk(function (ok, fail, slotPath) {
1118       // Resolve the SlotPath ORD
1119       baja.Ord.make(slotPath.toString()).get({
1120         "base": that.$space, 
1121         "ok": ok, 
1122         "fail": fail
1123       });
1124     });
1125   
1126     this.serviceToPath(typeSpec, cb);
1127   };
1128   
1129   /**
1130    * Resolve a Service to its SlotPath via a TypeSpec.
1131    *
1132    * @private
1133    *
1134    * @param {String} typeSpec
1135    * @param {baja.comm.Callback} cb
1136    */
1137   BoxCallbacks.prototype.serviceToPath = function (typeSpec, cb) {    
1138     // Intermediate callback to pass in SlotPath to callback
1139     cb.addOk(function (ok, fail, slotPathStr) {
1140       ok(new baja.SlotPath(slotPathStr));
1141     });
1142   
1143     // Make a call on the Server Side Component to unsubscribe  
1144     baja.comm.serverHandlerCall(this.$space.getAbsoluteOrd().toString(),
1145                                 "serviceToPath",
1146                                 typeSpec,
1147                                 cb); 
1148   };
1149   
1150   /**
1151    * Make a Link.
1152    *
1153    * @private
1154    *
1155    * @param {baja.Component} source Component for the link.
1156    * @param {baja.Slot} sourceSlot source Slot for the link.
1157    * @param {baja.Component} target Component for the link.
1158    * @param {baja.Slot} targetSlot target Slot for the link.
1159    * @param {baja.comm.Callback} cb 
1160    */
1161   BoxCallbacks.prototype.makeLink = function (source, sourceSlot, target, targetSlot, cb) {     
1162     // Add intermediate callback
1163     cb.addOk(function (ok, fail, resp) {    
1164       // TODO: Scan response for unknown Types
1165       ok(bsonDecodeValue(resp, serverDecodeContext));
1166     });
1167     
1168     // Arguments    
1169     var arg = {
1170       s: source.getHandle(),
1171       ss: sourceSlot.getName(),
1172       t: target.getHandle(),
1173       ts: targetSlot.getName()
1174     };
1175         
1176     // Make a call on the Server
1177     baja.comm.serverHandlerCall(this.$space.getAbsoluteOrd().toString(),
1178                                 "makeLink",
1179                                 arg,
1180                                 cb);   
1181   }; 
1182   
1183   /**
1184    * Get the Nav Children of a Component.
1185    *
1186    * @private
1187    *
1188    * @param {String} handle
1189    * @param {baja.comm.Callback} cb
1190    */
1191   BoxCallbacks.prototype.getNavChildren = function (handle, cb) {   
1192     // Intermediate callback to resolve Nav ORDs
1193     cb.addOk(function (ok, fail, navOrds) {
1194       // Resolve each of the Nav ORDs
1195       new baja.BatchResolve(navOrds).resolve({
1196         ok: function () {
1197           // Pass the resolved Components to the callback handler
1198           ok(this.getTargetObjects());
1199         },
1200         fail: fail
1201       });
1202     });
1203   
1204     // Make a call on the Server Side to get the Nav Children 
1205     baja.comm.serverHandlerCall(this.$space.getAbsoluteOrd().toString(),
1206                                 "navChildren",
1207                                 handle,
1208                                 cb); 
1209   };
1210     
1211   ////////////////////////////////////////////////////////////////
1212   // sendToMaster SyncOps
1213   //////////////////////////////////////////////////////////////// 
1214   
1215   /**
1216    * Server Add.
1217    *
1218    * @private
1219    *
1220    * @param comp the Component being added too.
1221    * @param {String} slotName
1222    * @param val the value to be added.
1223    * @param {Number} flags slot flags.
1224    * @param {baja.Facets} facets slot facets.
1225    * @param {baja.comm.Callback} cb   
1226    */
1227   BoxCallbacks.prototype.add = function (comp, slotName, val, flags, facets, cb) {
1228     // Add intermediate callback to pass back newly added Property
1229     cb.addOk(function (ok, fail, resp) {
1230       // Attempt to get newly added Property name from server response
1231       var newName = slotName;
1232       if (resp && resp instanceof Array && resp.length > 0 && resp[0].nn) {
1233         newName = resp[0].nn;
1234       }
1235     
1236       // Please note: if the slot name had a wildcard in it, this won't work (i.e. 'test?')
1237       ok(comp.getSlot(newName));
1238     });
1239   
1240     // Send the op to the Server
1241     new AddOp(comp, slotName, val, flags, facets).syncTo(comp.getComponentSpace(), cb);   
1242   };  
1243   
1244   /**
1245    * Server Set.
1246    *
1247    * @private
1248    *
1249    * @param comp the Component being added too.
1250    * @param {Array} propPath array of Property names used for the set.
1251    * @param val the value for the set.
1252    * @param {baja.comm.Callback} cb   
1253    */
1254   BoxCallbacks.prototype.set = function (comp, propPath, val, cb) {     
1255     // Send the op to the Server
1256     new SetOp(comp, propPath, val).syncTo(comp.getComponentSpace(), cb);   
1257   };  
1258   
1259   /**
1260    * Server Remove.
1261    *
1262    * @private
1263    *
1264    * @param comp the Component being removed from.
1265    * @param {baja.Slot} slot the slot to be removed.
1266    * @param {baja.comm.Callback} cb   
1267    */
1268   BoxCallbacks.prototype.remove = function (comp, slot, cb) {     
1269     // Send the op to the Server
1270     new RemoveOp(comp, slot).syncTo(comp.getComponentSpace(), cb);   
1271   };  
1272   
1273   /**
1274    * Server Rename.
1275    *
1276    * @private
1277    *
1278    * @param comp the Component the slot is being renamed on.
1279    * @param {String} oldName the old name of the slot.
1280    * @param {String} newName the new name of the slot.
1281    * @param {baja.comm.Callback} cb
1282    */
1283   BoxCallbacks.prototype.rename = function (comp, oldName, newName, cb) {     
1284     // Send the op to the Server
1285     new RenameOp(comp, oldName, newName).syncTo(comp.getComponentSpace(), cb);   
1286   };
1287   
1288   /**
1289    * Server Reorder.
1290    *
1291    * @private
1292    *
1293    * @param comp the Component the dynamic slots are being reordered upon.
1294    * @param {Array} dynamicProperties an array of Property names that specify the new order.
1295    * @param {baja.comm.Callback} cb
1296    */
1297   BoxCallbacks.prototype.reorder = function (comp, dynamicProperties, cb) {     
1298     // Send the op to the Server
1299     new ReorderOp(comp, dynamicProperties).syncTo(comp.getComponentSpace(), cb);   
1300   };
1301   
1302   /**
1303    * Server Set Flags.
1304    *
1305    * @private
1306    *
1307    * @param comp the Component for the slot the flags will be set upon.
1308    * @param {baja.Slot} slot the slot the flags are being set upon.
1309    * @param {Number} flags the new slot flags.
1310    * @param {baja.comm.Callback} cb 
1311    */
1312   BoxCallbacks.prototype.setFlags = function (comp, slot, flags, cb) {     
1313     // Send the op to the Server
1314     new SetFlagsOp(comp, slot, flags).syncTo(comp.getComponentSpace(), cb);   
1315   };
1316   
1317   /**
1318    * Server Set Facets.
1319    *
1320    * @private
1321    *
1322    * @param comp the Component for the slot the facets will be set upon.
1323    * @param {baja.Slot} slot the dynamic slot the facets are being set upon.
1324    * @param {baja.Facets} facets the new slot facets.
1325    * @param {baja.comm.Callback} cb 
1326    */
1327   BoxCallbacks.prototype.setFacets = function (comp, slot, facets, cb) {     
1328     // Send the op to the Server
1329     new SetFacetsOp(comp, slot, facets).syncTo(comp.getComponentSpace(), cb);   
1330   };
1331   
1332   /**
1333    * Server Topic Fire.
1334    *
1335    * @private
1336    *
1337    * @param comp the Component the Topic will be fired from.
1338    * @param {baja.Slot} slot the Topic Slot.
1339    * @param event the Topic event (can be null).
1340    * @param {baja.comm.Callback} cb 
1341    */
1342   BoxCallbacks.prototype.fire = function (comp, slot, event, cb) {     
1343     // Send the op to the Server
1344     new FireTopicOp(comp, slot, event).syncTo(comp.getComponentSpace(), cb);   
1345   };  
1346   
1347   ////////////////////////////////////////////////////////////////
1348   // BOX Component Space
1349   //////////////////////////////////////////////////////////////// 
1350    
1351   /**
1352    * @class BOX Component Space.
1353    * <p>
1354    * A BOX Component Space is a Proxy Component Space that's linked to another 
1355    * Component Space in another host elsewhere.
1356    *
1357    * @name baja.BoxComponentSpace
1358    * @extends baja.ComponentSpace
1359    * @private
1360    *
1361    * @param {String} name
1362    * @param {String} ordInSession
1363    * @param host
1364    */   
1365   baja.BoxComponentSpace = function (name, ordInSession, host) {
1366     baja.BoxComponentSpace.$super.apply(this, arguments);
1367     this.$callbacks = new BoxCallbacks(this);
1368   }.$extend(baja.ComponentSpace); 
1369   
1370   /**
1371    * Call to initialize a Component Space.
1372    *
1373    * @private
1374    * 
1375    * @param {baja.comm.Batch} batch
1376    */
1377   baja.BoxComponentSpace.prototype.init = function (batch) {
1378           
1379     // Any events are sync ops so process then in the normal way
1380     var that = this;
1381     function eventHandler(events) {
1382       that.$fw("commitSyncOps", events.ops);
1383     }
1384     
1385     try {   
1386       // Make the server side Handler for this Component Space   
1387       baja.comm.makeServerHandler(this.getAbsoluteOrd().toString(), // The id of the Server Session Handler to be created
1388                                   "box:ComponentSpaceSessionHandler", // Type Spec of the Server Session Handler
1389                                   this.getAbsoluteOrd().toString(), // Initial argument for the Server Session Handler
1390                                   eventHandler,
1391                                   new Callback(baja.ok, baja.fail, batch),
1392                                   /*makeInBatch*/true);         
1393               
1394       // Load Root Component of Station
1395       var cb = new Callback(function ok(resp) {
1396       
1397         // Create the root of the Station
1398         that.$root = baja.$(resp.t);
1399         
1400         // Set the core handle of the Station
1401         that.$root.$handle = resp.h;
1402         
1403         // Mount the local Station root
1404         that.$fw("mount", that.$root);
1405       }, 
1406       baja.fail, batch);
1407           
1408       // Make a call on the Server Side Handler  
1409       baja.comm.serverHandlerCall(this.getAbsoluteOrd().toString(),
1410                                   "loadRoot",
1411                                   /*Server Component Arg Call*/null,
1412                                   cb,
1413                                   /*makeInBatch*/true); 
1414     }
1415     catch (err) {
1416       baja.fail(err);
1417     }
1418   };
1419   
1420   /**
1421    * Sync the Component Space.
1422    * <p>
1423    * This method will result in a network call to sync the master Space with this one.
1424    * <p>
1425    * An Object Literal is used for the method's arguments.
1426    *
1427    * @private
1428    *
1429    * @param {Object} [obj] the Object Literal for the method's arguments.
1430    * @param {Function} [obj.ok] the ok callback. Called once the Component Space has
1431    *                            been successfully synchronized with the Server.
1432    * @param {Function} [obj.fail] the fail callback. Called If the Component Space 
1433    *                              can't be synchronized.
1434    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
1435    */
1436   baja.BoxComponentSpace.prototype.sync = function (obj) {
1437     obj = baja.objectify(obj, "ok");
1438     var cb = new Callback(obj.ok, obj.fail, obj.batch);
1439     try {
1440       this.$callbacks.poll(cb);
1441     }
1442     catch (err) {
1443       cb.fail(err);
1444     }
1445   };
1446   
1447   /**
1448    * Find the Component via its handle (null if not found).
1449    * <p>
1450    * An Object Literal is used for the method's arguments.
1451    *
1452    * @private
1453    *
1454    * @param {Object} [obj] the Object Literal for the method's arguments.
1455    * @param {Function} [obj.ok] the ok callback. Called if the Component is resolved.
1456    *                            The Component instance will be passed to this function.
1457    * @param {Function} [obj.fail] the fail callback. Call if there's an error or the Component
1458    *                              can't be resolved.
1459    * @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be batched into this object.
1460    */  
1461   baja.BoxComponentSpace.prototype.resolveByHandle = function (obj) {
1462     obj = baja.objectify(obj);
1463     
1464     var handle = obj.handle,
1465         cb = new Callback(obj.ok, obj.fail, obj.batch),
1466         that = this;
1467     
1468     try {    
1469       var comp = this.findByHandle(handle);    
1470       if (comp !== null) {
1471         cb.ok(comp);
1472       }
1473       else {
1474       
1475         // Intermediate callback to resolve the SlotPath into the target Component
1476         cb.addOk(function (ok, fail, slotPath) {
1477           // Resolve the SlotPath ORD
1478           baja.Ord.make(slotPath.toString()).get({
1479             "base": that,
1480             "ok": ok, 
1481             "fail": fail
1482           });
1483         });
1484       
1485         this.$callbacks.handleToPath(handle, cb);
1486       }
1487     }
1488     catch (err) {
1489       cb.fail(err);
1490     }
1491   }; 
1492     
1493   /**
1494    * Private fw method used by LoadOp for synchronizing two Components.
1495    *
1496    * @param from  the from Component.
1497    * @param to    the to Component.
1498    *
1499    * @private
1500    */   
1501   var emptyCommitCx = { commit: true, serverDecode: true, syncStructVals: true };
1502   function syncComp(from, to) {
1503   
1504     // Sanity check - must be same Type
1505     if (from.getType().getTypeSpec() !== to.getType().getTypeSpec()) {
1506       throw new Error("LoadOp Types differ: " + from.getType() + " - " + to.getType());
1507     }
1508     
1509     // Sanity check - must have same handle
1510     if (to.getHandle() !== from.getHandle()) {
1511       throw new Error("LoadOp Handle Error: " + from.getHandle() + " - " + to.getHandle());
1512     }
1513     
1514     // Update display name
1515     if (from.getPropertyInParent() !== null) {
1516       to.getPropertyInParent().$setDisplayName(from.getPropertyInParent().$getDisplayName());
1517       to.getPropertyInParent().$setDisplay(from.getPropertyInParent().$getDisplay());
1518     }
1519     
1520     // Map over cached permissions
1521     to.$fw("setPermissions", from.$permissionsStr);
1522     
1523     // If this component isn't loaded then don't try to sync its slots
1524     if (!from.$bPropsLoaded) {
1525       // TODO: Sync icon
1526       return;
1527     }
1528     
1529     // Signal the broker properties are loaded
1530     to.$bPropsLoaded = true;
1531     
1532     // Note down Properties before synchronization
1533     var pb = {}, // Properties before
1534         tslots = to.$map.$map, // Access internal OrderedMap instead of cursor for speed
1535         p;
1536     
1537     for (p in tslots) {
1538       if (tslots.hasOwnProperty(p) && tslots[p].isProperty()) {
1539         pb[p] = tslots[p];
1540       }
1541     }    
1542     
1543     var fslots = from.$map.$map, // Access internal OrderedMap for speed
1544         name,
1545         fromSlot,
1546         toSlot,
1547         fromFlags,
1548         fromValue,
1549         cx,
1550         reorder = false,
1551         reorderSlots;
1552     
1553     for (p in fslots) {
1554       if (fslots.hasOwnProperty(p)) {
1555       
1556         fromSlot = fslots[p];
1557         name = fromSlot.getName();
1558         toSlot = to.getSlot(name);
1559         fromFlags = from.getFlags(fromSlot);
1560         
1561         if (!fromSlot.isFrozen()) {
1562           reorderSlots = reorderSlots || [];
1563           reorderSlots.push(name);
1564         }
1565         
1566         // If to slot is not present then we need to add
1567         if (toSlot === null) {
1568         
1569           // TODO: Handle display String
1570           cx = { commit: true, serverDecode: true, displayName: fromSlot.$getDisplayName(), display: fromSlot.$getDisplay() };
1571                 
1572           fromValue = from.get(name);
1573           if (fromValue.$parent) {
1574             fromValue.$parent = null; // TODO: Hack to get around any parenting problems
1575           }
1576           to.add({
1577             "slot": name, 
1578             "value": fromValue, 
1579             "flags": fromFlags, 
1580             "facets": from.getFacets(fromSlot), 
1581             "cx": cx
1582           });
1583           
1584           continue;
1585         }
1586         
1587         // If there's already a dynamic slot on the 'to' Component then attempt a reorder
1588         if (!fromSlot.isFrozen()) {
1589           reorder = true;
1590         }
1591               
1592         delete pb[name];
1593         
1594         // Set the flags if they differ
1595         if (fromFlags !== toSlot.getFlags()) {
1596           to.setFlags({
1597             "slot": toSlot, 
1598             "flags": fromFlags, 
1599             "cx": emptyCommitCx
1600           });
1601         }
1602         
1603         if (!fromSlot.isProperty()) {
1604           continue;
1605         }
1606                                 
1607         syncProps(from, fromSlot, to, toSlot);
1608       }
1609     }
1610     
1611     // at this point if there were any props before that we didn't 
1612     // sync we need to remove them now; since they weren't found 
1613     // on "from" that means they have since been removed
1614     var removeName;
1615     for (removeName in pb) {
1616       if (pb.hasOwnProperty(removeName)) {
1617         to.remove({
1618           "slot": pb[removeName], 
1619           "cx": emptyCommitCx
1620         });
1621       }
1622     }
1623     
1624     // Sync Knobs
1625     var fromKnobs = from.$knobs,
1626         toKnobs = to.$knobs,
1627         installKnobs, uninstallKnobs, i, x;
1628         
1629     if (fromKnobs) {
1630       installKnobs = [];
1631     
1632       // Install any knobs missing from the to Component
1633       for (p in fromKnobs) {
1634         if (fromKnobs.hasOwnProperty(p)) {
1635           if (!toKnobs) {
1636             installKnobs.push(fromKnobs[p]);
1637           }
1638           else if (!toKnobs.hasOwnProperty(p)) {
1639             installKnobs.push(fromKnobs[p]);
1640           }
1641         }
1642       }
1643     }
1644     
1645     if (toKnobs) {
1646       uninstallKnobs = [];
1647       
1648       // Install any knobs missing from the to Component
1649       for (p in toKnobs) {
1650         if (toKnobs.hasOwnProperty(p)) {
1651           if (!fromKnobs) {
1652             uninstallKnobs.push(toKnobs[p]);
1653           }
1654           else if (!fromKnobs.hasOwnProperty(p)) {
1655             uninstallKnobs.push(toKnobs[p]);
1656           }
1657         }
1658       }
1659     }
1660     
1661     if (installKnobs) {
1662       for (i = 0; i < installKnobs.length; ++i) {
1663         to.$fw("installKnob", installKnobs[i], emptyCommitCx);
1664       }
1665     }
1666     
1667     if (uninstallKnobs) {
1668       for (x = 0; x < uninstallKnobs.length; ++x) {
1669         to.$fw("uninstallKnob", uninstallKnobs[x].getId(), uninstallKnobs[x].getSourceSlotName(), emptyCommitCx);
1670       }
1671     }
1672     
1673     // Attempt reorder
1674     if (reorder && reorderSlots) {
1675       to.reorder({
1676         dynamicProperties: reorderSlots,
1677         cx: emptyCommitCx
1678       });
1679     }    
1680   }
1681   
1682   /**
1683    * Private fw method used by LoadOp for synchronizing Properties between two Components.
1684    *
1685    * @param from      the from Component.
1686    * @param fromProp  the from Component Property.
1687    * @param to        the to Component.
1688    * @param toProp    the to Component Property.
1689    *
1690    * @private
1691    */ 
1692   function syncProps(from, fromProp, to, toProp) {
1693     // If neither of these Slots have been decoded then skip trying to sync!
1694     if (fromProp.isFrozen() && 
1695        !fromProp.$isValueDecoded() &&
1696        !toProp.$isValueDecoded() &&
1697        fromProp.getType().getTypeSpec() === toProp.getType().getTypeSpec()) {
1698       return;
1699     }
1700     
1701     // TODO: Update display string
1702     syncVal(from.get(fromProp), to, toProp, fromProp.$getDisplayName(), fromProp.$getDisplay());
1703   }
1704     
1705   /**
1706    * Private fw method used to synchronize a Property value in a Component.
1707    *
1708    * @param fromValue  the from Value.
1709    * @param to        the to Component.
1710    * @param toProp    the to Component Property.
1711    * @param displayName  the from display name.
1712    * @param display  the from display.
1713    *
1714    * @private
1715    */   
1716   function syncVal(fromValue, to, toProp, displayName, display) {
1717     
1718     var toValue = to.get(toProp),
1719         isFromValComp = fromValue.getType().isComponent();
1720     
1721     toProp.$setDisplayName(displayName);
1722     toProp.$setDisplay(display);
1723     // TODO: Update display string
1724     
1725     // we need to do a full replace if:
1726     //  - the type has changed 
1727     //  - the value is a simple
1728     //  - we need to assign component handle 
1729     if (fromValue.getType().getTypeSpec() !== toValue.getType().getTypeSpec() || 
1730         fromValue.getType().isSimple() || 
1731         (toValue.getType().isComponent() && toValue.getHandle() === null)) {
1732           
1733       if (fromValue.$parent) {
1734         fromValue.$parent = null; // TODO: Hack to get around the fact that we haven't implemented newCopy yet
1735       }     
1736       
1737       // TODO: What about display name and display getting passed through here?
1738       to.set({
1739         "slot": toProp,
1740         "value": fromValue,  
1741         "cx": emptyCommitCx
1742       });      
1743       return;
1744     }
1745     
1746     // Do a full sync if this is a Component
1747     if (isFromValComp) {
1748       syncComp(fromValue, toValue);
1749       return;
1750     }
1751     
1752     // Otherwise this is a Struct so do a sync in place
1753     var fslots = fromValue.$map.$map, // Use internal OrderedMap instead of Cursors for speed
1754         nextFromProp, // From Property
1755         p;
1756     
1757     for (p in fslots) {
1758       if (fslots.hasOwnProperty(p) && fslots[p].isProperty()) {
1759         nextFromProp = fslots[p];
1760         syncProps(fromValue, nextFromProp, toValue, toValue.getSlot(nextFromProp.getName()));
1761       }
1762     }
1763   }
1764                  
1765   /**
1766    * Commit Slots to the Component Space.
1767    *
1768    * @param {Array} slotInfo.
1769    *
1770    * @private
1771    */
1772   var commitSlotInfo = function (slotInfo) {
1773     var comp, cx, newVal, slot, bson, i;
1774     
1775     for (i = 0; i < slotInfo.length; ++i) {
1776   
1777       bson = slotInfo[i];
1778       
1779       // Attempt to find the Component
1780       comp = this.findByHandle(bson.h);
1781             
1782       // Only load a singular Slot if the Component isn't already loaded
1783       // TODO: Ensure we sync with master before loadSlot is processed in
1784       // ORD resolution
1785       if (comp !== null && !comp.$bPropsLoaded) {
1786       
1787         // What about mounting a Component???
1788       
1789         // Decode the Value
1790         newVal = bsonDecodeValue(bson.v, serverDecodeContext);
1791         
1792         // Force any Component to be stubbed
1793         if (newVal.getType().isComponent()) {
1794           newVal.$bPropsLoaded = false;
1795         }
1796       
1797         cx = { commit: true, serverDecode: true };
1798         
1799         // Add the display name if we've got one
1800         if (bson.dn) {    
1801           cx.displayName = bson.dn; 
1802         }
1803         
1804         // Add the display string if we've got one
1805         if (bson.d) {    
1806           cx.display = bson.d; 
1807         }
1808               
1809         // TODO: What if the Component is already fully loaded?
1810       
1811         // Does the Slot currently exist?
1812         slot = comp.getSlot(bson.n);
1813         if (slot === null) {
1814         
1815           // Add the Slot if it doesn't currently exist
1816           comp.add({
1817             "slot": bson.n, 
1818             "value": newVal, 
1819             "flags": baja.Flags.decodeFromString(bajaDef(bson.f, "")), 
1820             "facets": baja.Facets.DEFAULT.decodeFromString(bajaDef(bson.x, "")),
1821             "cx": cx
1822           });
1823         }
1824         else {
1825         
1826           // Synchronize the value
1827           syncVal(newVal, comp, slot, bson.dn, bson.d);
1828         }
1829       }
1830     }
1831   };
1832       
1833   /**
1834    * Commit the sync ops to the Component Space.
1835    *
1836    * @private
1837    *
1838    * @param {Array} syncOpsArray  an array of Sync Ops to be committed to the Component Space.
1839    */
1840   var commitSyncOps = function (syncOpsArray) {
1841     strictArg(syncOpsArray, Array);
1842     
1843     // Commit all of the SyncOps
1844     var sp,   // SyncOp
1845         comp, // Component, the Op happens on
1846         i;
1847         
1848     for (i = 0; i < syncOpsArray.length; ++i) {
1849       sp = syncOpsArray[i];
1850       
1851       try {
1852         
1853         // Get Component from SyncOp
1854         if (sp.h !== undefined) { // Was the handle encoded?
1855           comp = this.findByHandle(sp.h);
1856           
1857           // Update the display name of the component
1858           if (comp && comp.getPropertyInParent() !== null) {
1859             comp.getPropertyInParent().$setDisplayName(sp.cdn);
1860             comp.getPropertyInParent().$setDisplay(sp.cd);
1861           }
1862         }
1863         else {
1864           comp = null;
1865         }
1866         
1867         // Look up SyncOp, decode and Commit
1868         if (syncOps.hasOwnProperty(sp.nm) && 
1869             typeof syncOps[sp.nm] === "function" && 
1870             typeof syncOps[sp.nm].id === "string") {
1871           syncOps[sp.nm].decodeAndCommit(comp, sp);
1872         }
1873       }
1874       catch (err) {
1875         // Log any errors generated by committing sync ops
1876         baja.error(err);
1877       }
1878     }
1879   };
1880   
1881   /**
1882    * Private framework handler for a Component Space.
1883    * <p>
1884    * This is a private internal method for framework developers.
1885    *
1886    * @private
1887    */
1888   baja.BoxComponentSpace.prototype.$fw = function (x, a, b, c) {    
1889     if (x === "commitSyncOps") {
1890       // Process sync ops
1891       commitSyncOps.call(this, /*SyncOps*/a);
1892     }
1893     else if (x === "commitSlotInfo") {
1894       // Commit a singular Slot
1895       commitSlotInfo.call(this, /*Slot Information to Commit*/a);
1896     }
1897     else {
1898       // Else call super framework handler
1899       baja.BoxComponentSpace.$super.prototype.$fw.apply(this, arguments);
1900     }
1901   };  
1902             
1903 }(baja));