1 //
  2 // Copyright 2010, Tridium, Inc. All Rights Reserved.
  3 //
  4 
  5 /**
  6  * BOX System Object Notation.
  7  * <p>
  8  * BSON is BOG notation in a JSON format. JSON is used instead of XML because
  9  * tests show that browsers can parse JSON significantly faster.
 10  *
 11  * @author Gareth Johnson
 12  * @version 1.0.0.0
 13  */
 14 
 15 //JsLint options (see http://www.jslint.com )
 16 /*jslint rhino: true, onevar: false, plusplus: true, white: true, undef: false, nomen: false, eqeqeq: true, 
 17 bitwise: true, regexp: true, newcap: true, immed: true, strict: false, indent: 2, vars: true, continue: true */
 18 
 19 // Globals for JsLint to ignore 
 20 /*global baja, syncVal, encodeVal, BaseBajaObj*/ 
 21   
 22 (function bson(baja) {
 23 
 24   // Use ECMAScript 5 Strict Mode
 25   "use strict";
 26   
 27   // Create local for improved minification
 28   var bajaDef = baja.def,
 29       objectify = baja.objectify,
 30       serverDecodeContext = baja.$serverDecodeContext = { serverDecode: true },
 31       bsonDecodeValue;
 32   
 33   /**
 34    * @namespace BOX System Object Notation.
 35    */
 36   baja.bson = new BaseBajaObj();
 37   
 38   ////////////////////////////////////////////////////////////////
 39   // Decode Actions/Topics
 40   //////////////////////////////////////////////////////////////// 
 41   
 42   /**
 43    * Decode the BSON for an Action Type.
 44    *
 45    * @private
 46    *
 47    * @param bson the BSON to decode.
 48    * @returns type the parameter for the Action Type (or null if none).
 49    */
 50   function decodeActionParamType(bson) {
 51     var paramType = null;    
 52     
 53     // Decode Action Parameter Type
 54     if (typeof bson.apt === "string") {
 55       paramType = baja.lt(bson.apt);
 56     }
 57     
 58     return paramType;
 59   }
 60   
 61   /**
 62    * Decode the BSON for an Action Default.
 63    *
 64    * @private
 65    *
 66    * @param bson the BSON to decode.
 67    * @returns type the parameter for the Action Default (or null if none).
 68    */
 69   function decodeActionParamDefault(bson) {
 70     var paramDef = null;    
 71     
 72     // Decode default Action Parameter Type
 73     if (typeof bson.apd === "object") {
 74       paramDef = bsonDecodeValue(bson.apd, serverDecodeContext);
 75     }
 76     
 77     return paramDef;
 78   }
 79   
 80   /**
 81    * Decode the BSON for an Action Return Type.
 82    *
 83    * @private
 84    *
 85    * @param bson the BSON to decode.
 86    * @returns type the parameter for the Action Return Type (or null if none).
 87    */
 88   function decodeActionReturnType(bson) {
 89     var returnType = null;    
 90     
 91     // Decode default Action Parameter Type
 92     if (typeof bson.art === "string") {
 93       returnType = baja.lt(bson.art);
 94     }
 95     
 96     return returnType;
 97   }
 98         
 99   /**
100    * Decode the BSON for the Topic and return the event type.
101    *
102    * @private
103    *
104    * @param bson the BSON to decode.
105    * @returns {Type} the event type for a Topic.
106    */
107   function decodeTopicEventType(bson) {   
108     var eventType = null;
109     
110     if (typeof bson.tet === "string") {
111       eventType = baja.lt(bson.tet);
112     }
113     
114     return eventType;
115   }
116 
117   ////////////////////////////////////////////////////////////////
118   // BSON Frozen Slots
119   //////////////////////////////////////////////////////////////// 
120   
121   /**
122    * @class Frozen Property Slot.
123    * <p>
124    * Property defines a Slot which is a storage location
125    * for a variable in a Complex.
126    * <p>
127    * A new object should never be directly created with this Constructor. All Slots are 
128    * created internally by BajaScript.
129    *
130    * @name FrozenProperty
131    * @extends baja.Property
132    */
133   var FrozenProperty = function (bson, complex) {
134     FrozenProperty.$super.call(this, bson.n, bson.dn);
135     this.$bson = bson;
136     this.$complex = complex;
137   }.$extend(baja.Property);
138   
139   FrozenProperty.prototype.isFrozen = function () {
140     return true;
141   };
142   
143   /**   
144    * Return the Property value.
145    * <p>
146    * Please note, this method is intended for INTERNAL use by Tridium only. An
147    * external developer should never call this method.
148    *
149    * @private  
150    *
151    * @returns value
152    */
153   FrozenProperty.prototype.$getValue = function () {
154     if (this.$val === undefined) {
155       var val = this.$val = bsonDecodeValue(this.$bson.v, serverDecodeContext);
156       // Set up any parenting if needed      
157       if (val.getType().isComplex() && this.$complex) {
158         val.$parent = this.$complex;
159         val.$propInParent = this;
160       }
161     }
162     return this.$val;
163   };
164   
165   /**
166    * Return true if the value has been lazily decoded.
167    *
168    * @private
169    *
170    * @returns {Boolean}
171    */
172   FrozenProperty.prototype.$isValueDecoded = function () {
173     return this.$val !== undefined;
174   };
175 
176   /**   
177    * Set the Property value.
178    * <p>
179    * Please note, this method is intended for INTERNAL use by Tridium only. An
180    * external developer should never call this method.
181    *
182    * @private  
183    *
184    * @param val value to be set.
185    */
186   FrozenProperty.prototype.$setValue = function (val) {
187     this.$val = val;
188   };   
189     
190   /**   
191    * Return the Flags for the Property.
192    *
193    * @see baja.Flags
194    *
195    * @returns {Number}
196    */
197   FrozenProperty.prototype.getFlags = function () { 
198     if (this.$flags === undefined) {
199       this.$flags = this.$bson.f === undefined ? 0 : baja.Flags.decodeFromString(this.$bson.f);
200     }
201     return this.$flags; 
202   };
203   
204   /**   
205    * Set the Flags for the Property.
206    * <p>
207    * Please note, this method is intended for INTERNAL use by Tridium only. An
208    * external developer should never call this method.
209    *
210    * @private
211    * @see baja.Flags
212    *
213    * @param {Number} flags
214    */
215   FrozenProperty.prototype.$setFlags = function (flags) { 
216     this.$flags = flags; 
217   };
218   
219   /**
220    * Return the Facets for the Property.
221    *
222    * @see baja.Facets
223    *
224    * @returns the Slot Facets
225    */
226   FrozenProperty.prototype.getFacets = function () {
227     if (this.$facets === undefined) {
228       this.$facets = this.$bson.x === undefined ? baja.Facets.DEFAULT : baja.Facets.DEFAULT.decodeFromString(this.$bson.x);
229     }
230     return this.$facets;  
231   };
232   
233   /**   
234    * Set the Facets for the Property.
235    * <p>
236    * Please note, this method is intended for INTERNAL use by Tridium only. An
237    * external developer should never call this method.
238    *
239    * @private
240    * @see baja.Facets
241    *
242    * @param {baja.Facets} facets
243    */
244   FrozenProperty.prototype.$setFacets = function (facets) {
245     this.$facets = facets; 
246   };
247   
248   /**
249    * Return the default flags for the Property.
250    *
251    * @returns {Number}
252    */
253   FrozenProperty.prototype.getDefaultFlags = function () {
254     if (this.$defFlags === undefined) {
255       this.$defFlags = this.$bson.f === undefined ? 0 : baja.Flags.decodeFromString(this.$bson.f);
256     }
257     return this.$defFlags; 
258   };
259   
260   /**
261    * Return the default value for the Property.
262    *
263    * @returns the default value for the Property.
264    */
265   FrozenProperty.prototype.getDefaultValue = function () {   
266     if (this.$defVal === undefined) {
267       this.$defVal = bsonDecodeValue(this.$bson.v, serverDecodeContext);
268     }
269     return this.$defVal;
270   }; 
271   
272   /**
273    * Return the Type for this Property.
274    *
275    * @returns the Type for the Property.
276    */
277   FrozenProperty.prototype.getType = function () {
278     if (this.$val === undefined) {
279       if (this.$initType === undefined) {
280         this.$initType = baja.lt(this.$bson.v.t);
281       }
282       return this.$initType;
283     }
284     else {
285       return this.$getValue().getType();
286     }    
287   };
288     
289   /**
290    * Return the display String for this Property.
291    * <p>
292    * Please note, this method is intended for INTERNAL use by Tridium only. An
293    * external developer should never call this method.
294    *
295    * @private
296    *
297    * @returns {String}
298    */
299   FrozenProperty.prototype.$getDisplay = function () {
300     if (this.$display === undefined) {
301       this.$display = this.$bson.v.d || "";
302     }
303     return this.$display;
304   };
305   
306   /**
307    * Set the display for this Property.
308    * <p>
309    * Please note, this method is intended for INTERNAL use by Tridium only. An
310    * external developer should never call this method.
311    *
312    * @private
313    *
314    * @param {String} display the display String
315    */
316   FrozenProperty.prototype.$setDisplay = function (display) {
317     this.$display = display;
318   };
319   
320   /**
321    * @class Frozen Action Slot.
322    * <p>
323    * Action is a Slot that defines a behavior which can
324    * be invoked on a Component.
325    * <p>
326    * A new object should never be directly created with this Constructor. All Slots are 
327    * created internally by BajaScript
328    *
329    * @name FrozenAction
330    * @extends baja.Action
331    */
332   var FrozenAction = function (bson) {
333     FrozenAction.$super.call(this, bson.n, bson.dn);
334     this.$bson = bson;
335   }.$extend(baja.Action);
336 
337   FrozenAction.prototype.isFrozen = FrozenProperty.prototype.isFrozen;
338   
339   /**   
340    * Return the Flags for the Action.
341    *
342    * @function
343    * @see baja.Flags
344    *
345    * @returns {Number}
346    */
347   FrozenAction.prototype.getFlags = FrozenProperty.prototype.getFlags;
348   
349   /**   
350    * Set the Flags for the Action.
351    * <p>
352    * Please note, this method is intended for INTERNAL use by Tridium only. An
353    * external developer should never call this method.
354    *
355    * @function
356    * @private
357    * @see baja.Flags
358    *
359    * @param {Number} flags
360    */
361   FrozenAction.prototype.$setFlags = FrozenProperty.prototype.$setFlags;
362   
363   /**
364    * Return the Facets for the Action.
365    *
366    * @function
367    * @see baja.Facets
368    *
369    * @returns the Slot Facets
370    */
371   FrozenAction.prototype.getFacets = FrozenProperty.prototype.getFacets;
372   
373   /**   
374    * Set the Facets for the Action.
375    * <p>
376    * Please note, this method is intended for INTERNAL use by Tridium only. An
377    * external developer should never call this method.
378    *
379    * @function
380    * @private
381    * @see baja.Facets
382    *
383    * @param {baja.Facets} facets
384    */
385   FrozenAction.prototype.$setFacets = FrozenProperty.prototype.$setFacets;
386   
387   /**
388    * Return the default flags for the Action.
389    *
390    * @returns {Number}
391    */
392   FrozenAction.prototype.getDefaultFlags = FrozenProperty.prototype.getDefaultFlags;
393   
394   /**
395    * Return the Action's Parameter Type.
396    *
397    * @returns {Type} the Parameter's Type (or null if the Action has no argument).
398    */
399   FrozenAction.prototype.getParamType = function () {
400     if (this.$paramType === undefined) {
401       this.$paramType = decodeActionParamType(this.$bson);
402     }
403     return this.$paramType;
404   };
405   
406   /**
407    * Return the Action's Default Value.
408    *
409    * @returns the parameter default value (or null if the Action has no argument).
410    */
411   FrozenAction.prototype.getParamDefault = function () {
412     if (this.$paramDef === undefined) {
413       this.$paramDef = decodeActionParamDefault(this.$bson);
414     }
415     return this.$paramDef;
416   };
417   
418   /**
419    * Return the return Type for the Action.
420    * 
421    * @returns {Type} the return Type (or null if the Action has no return Type).
422    */
423   FrozenAction.prototype.getReturnType = function () {
424     if (this.$returnType === undefined) {
425       this.$returnType = decodeActionReturnType(this.$bson);
426     }
427     return this.$returnType;
428   };
429   
430   /**
431    * @class Frozen Topic Slot.
432    * <p>
433    * Topic defines a Slot which indicates an event that
434    * is fired on a Component.
435    * <p>
436    * A new object should never be directly created with this Constructor. All Slots are 
437    * created internally by BajaScript.
438    *
439    * @name FrozenTopic
440    * @extends baja.Topic
441    */
442   var FrozenTopic = function (bson) {
443     FrozenTopic.$super.call(this, bson.n, bson.dn);
444     this.$bson = bson;
445   }.$extend(baja.Topic);
446   
447   FrozenTopic.prototype.isFrozen = FrozenProperty.prototype.isFrozen;
448   
449   /**   
450    * Return the Flags for the Topic.
451    *
452    * @function
453    * @see baja.Flags
454    *
455    * @returns {Number}
456    */
457   FrozenTopic.prototype.getFlags = FrozenProperty.prototype.getFlags;
458   
459   /**   
460    * Set the Flags for the Topic.
461    * <p>
462    * Please note, this method is intended for INTERNAL use by Tridium only. An
463    * external developer should never call this method.
464    *
465    * @function
466    * @private
467    * @see baja.Flags
468    *
469    * @param {Number} flags
470    */
471   FrozenTopic.prototype.$setFlags = FrozenProperty.prototype.$setFlags;
472   
473   /**
474    * Return the Facets for the Topic.
475    *
476    * @function
477    * @see baja.Facets
478    *
479    * @returns the Slot Facets
480    */
481   FrozenTopic.prototype.getFacets = FrozenProperty.prototype.getFacets;
482   
483   /**   
484    * Set the Facets for the Topic.
485    * <p>
486    * Please note, this method is intended for INTERNAL use by Tridium only. An
487    * external developer should never call this method.
488    *
489    * @function
490    * @private
491    * @see baja.Facets
492    *
493    * @param {baja.Facets} facets
494    */
495   FrozenTopic.prototype.$setFacets = FrozenProperty.prototype.$setFacets;
496   
497   /**
498    * Return the default flags for the Topic.
499    *
500    * @returns {Number}
501    */
502   FrozenTopic.prototype.getDefaultFlags = FrozenProperty.prototype.getDefaultFlags;
503     
504   /**
505    * Return the event type.
506    *
507    * @returns {Type} the event type (or null if the Topic has not event).
508    */
509   FrozenTopic.prototype.getEventType = function () {
510     if (this.$eventType === undefined) {
511       this.$eventType = decodeTopicEventType(this.$bson);
512     }
513     return this.$eventType;
514   };
515 
516   ////////////////////////////////////////////////////////////////
517   // Auto-generate Slot Methods
518   //////////////////////////////////////////////////////////////// 
519   
520   function generateSlotMethods(complexType, complex, slot) {
521     // Cache auto-generated methods onto Type
522     var autoGen = complexType.$autoGen = complexType.$autoGen || {},
523         slotName = slot.getName(),
524         methods = autoGen.hasOwnProperty(slotName) ? autoGen[slotName] : null;
525     
526     // If the methods already exist then simply copy them over and return
527     if (methods) {
528       var methodName;  
529       for (methodName in methods) {
530         if (methods.hasOwnProperty(methodName)) {
531           complex[methodName] = methods[methodName];
532         }
533       } 
534       return;
535     }
536     
537     autoGen[slotName] = methods = {};
538     
539     // Please note: these auto-generated methods should always respect the fact that a Slot can be overridden by
540     // sub-Types. Therefore, be aware of using too much closure in these auto-generated methods.
541           
542     // Form appropriate name for getters, setters and firers
543     var capSlotName = slot.getName().capitalizeFirstLetter(),
544         i = 0;
545         
546     if (slot.isProperty()) {
547      
548       var origGetterName = "get" + capSlotName,
549           getterName = origGetterName,
550           origGetterDisplayName = origGetterName + "Display",
551           getterDisplayName = origGetterDisplayName,
552           origSetterName = "set" + capSlotName,
553           setterName = origSetterName;
554             
555       // Find some unique names for the getter and setter (providing this isn't the icon Slot which we DO want to override)...
556       i = 0;
557       if (capSlotName !== "Icon") {
558         while (complex[getterName] !== undefined || complex[getterDisplayName] !== undefined || complex[setterName] !== undefined) {
559           getterName = origGetterName + (++i); 
560           getterDisplayName = origGetterDisplayName + i;
561           setterName = origSetterName + i;
562         }
563       }
564                                  
565       // Add Getter
566       complex[getterName] = methods[getterName] = function () {
567         var v = this.get(slotName);
568         
569         // If a number then return its inner boxed value
570         return v.getType().isNumber() ? v.valueOf() : v;
571       };
572       
573       // Add Display String Getter
574       complex[getterDisplayName] = methods[getterDisplayName] = function () {
575         return this.getDisplay(slotName);
576       };
577       
578       // Add Setter
579       complex[setterName] = methods[setterName] = function (obj) {
580         obj = objectify(obj, "value");
581         obj.slot = slotName;
582       
583         // TODO: Need to check incoming value to ensure it's the same Type!!!
584         this.set(obj);	
585       };
586     }
587     
588     var invokeActionName = slotName;
589     if (slot.isAction()) {
590     
591       // Find a unique name for the Action invocation method
592       i = 0;
593       while (complex[invokeActionName] !== undefined) {
594         invokeActionName = slotName + (++i); 
595       }
596       
597       complex[invokeActionName] = methods[invokeActionName] = function (obj) {
598         obj = objectify(obj, "value");
599         obj.slot = slotName;
600                  
601         return this.invoke(obj);
602       };
603     }         
604     
605     if (slot.isTopic()) { 
606 
607       // Find a unique name for the topic invocation method     
608       var origFireTopicName = "fire" + capSlotName,
609           fireTopicName = origFireTopicName;
610       i = 0;
611       while (complex[fireTopicName] !== undefined) {
612         fireTopicName = origFireTopicName + (++i); 
613       }
614           
615       complex[fireTopicName] = methods[fireTopicName] = function (obj) {        
616         obj = objectify(obj, "value");
617         obj.slot = slotName;
618         
619         this.fire(obj);
620       };
621     }
622   }
623     
624   ////////////////////////////////////////////////////////////////
625   // Contracts
626   //////////////////////////////////////////////////////////////// 
627   
628   /**
629    * Return an instance of a frozen Slot.
630    *
631    * @param bson
632    * @param {baja.Complex} [complex]
633    *
634    * @returns {baja.Slot}
635    */
636   function createContractSlot(bson, complex) {
637     var slot,
638         slotType = bson.st;
639     
640     // Create frozen Slot
641     if (slotType === "p") {
642       slot = new FrozenProperty(bson, complex);
643     }
644     else if (slotType === "a") {
645       slot = new FrozenAction(bson);
646     }
647     else if (slotType === "t") {          
648       slot = new FrozenTopic(bson);
649     }
650     else {
651       throw new Error("Invalid BSON: Cannot decode: " + JSON.stringify(bson));
652     }
653     
654     return slot;
655   }
656   
657   /**
658    * Return return a decoded Contract Slot.
659    *
660    * @private
661    *
662    * @param complex the Complex to decode the Slots onto
663    * @param bson the BSON to decode
664    */
665   function decodeContractSlot(complexType, complex, bson) {    
666     var slot = createContractSlot(bson, complex),
667         slotName = bson.n;
668                 
669     // Only auto-generate the Slot methods if Slot doesn't already exist.
670     // This caters for Slots that are overridden by sub-Types.
671     if (!complex.$map.contains(slotName)) { 
672       // Auto-generate the methods and copy them over to the complex
673       generateSlotMethods(complexType, complex, slot);
674     }
675     
676     // Add to Slot Map
677     complex.$map.put(slotName, slot);  
678   }
679     
680   /**
681    * Return a decoded array of Slots from a BSON Contract Definition.
682    *
683    * @private
684    *
685    * @see baja.Slot
686    *
687    * @param type the Type.
688    * @param {baja.Complex} complex the complex instance the Slots are being loaded for.
689    */
690   baja.bson.decodeComplexContract = function (type, complex) {
691     var clxTypes = [],
692         t = type,
693         i,
694         j,
695         bson;
696     
697     // Get a list of all the Super types
698     while (t && t.isComplex()) {
699       clxTypes.push(t);
700       t = t.getSuperType();
701     }
702       
703     // Iterate down through the Super Types and build up the Contract list
704     for (i = clxTypes.length - 1; i >= 0; --i) {   
705       if (!clxTypes[i].hasContract()) {
706         clxTypes[i].loadContract();
707       }
708       
709       bson = clxTypes[i].getContract();
710       
711       if (bson) {
712         for (j = 0; j < bson.length; ++j) {      
713           // Add newly created Slot to array
714           decodeContractSlot(type, complex, bson[j]);      
715         } 
716       }
717     }   
718   };
719   
720   ////////////////////////////////////////////////////////////////
721   // BSON Type Scanning
722   //////////////////////////////////////////////////////////////// 
723 
724   function scanUnknown(bson, typeSpecs) { 
725     if (!bson) {
726       return;
727     }
728   
729     var type,
730         prop, 
731         i;
732      
733     // Ensure we're dealing with a Slot
734     if ((bson.nm === "p" || bson.nm === "a" || bson.nm === "t") && bson.t) {
735       // Check to see if the Type is loaded and has a Contract associated with it
736       if (baja.registry.hasType(bson.t)) {
737       
738         type = baja.lt(bson.t);
739       
740         // Only request type information if we need too
741         if ((type.isComplex() || type.isFrozenEnum()) && !type.hasContract()) {
742           typeSpecs[bson.t] = bson.t; 
743         }
744       }
745       else {
746         typeSpecs[bson.t] = bson.t; 
747       }
748     }
749     
750     // Scan for sub-properties
751     if (bson instanceof Array) {
752       for (i = 0; i < bson.length; ++i) {
753         scanUnknown(bson[i], typeSpecs);
754       }
755     }
756     else if (bson instanceof Object) {
757       for (prop in bson) {
758         if (bson.hasOwnProperty(prop)) {
759           scanUnknown(bson[prop], typeSpecs);
760         }
761       }
762     }
763   }
764   
765   /**
766    * Scan for Types and Contracts that aren't yet loaded into the BajaScript Registry.
767    *
768    * @private
769    *
770    * @param bson  the BSON to scan Types for.
771    * @returns {Array} An array of typeSpec information we need to request for (String).
772    */
773   baja.bson.scanForUnknownTypes = function (bson) {
774  
775     // Store results in an object as we only want type information added once
776     var typeSpecs = {},
777         typeSpecArray = [], // Convert from object map to array
778         prop;
779         
780     // Scan the data structure for Slot Type information
781     scanUnknown(bson, typeSpecs);
782     
783     for (prop in typeSpecs) {
784       if (typeSpecs.hasOwnProperty(prop)) {
785         typeSpecArray.push(prop);
786       }
787     }
788     
789     return typeSpecArray;
790   };
791   
792   ////////////////////////////////////////////////////////////////
793   // BOG BSON Decoding
794   ////////////////////////////////////////////////////////////////
795 
796   /**
797    * Decode and return a Knob.
798    *
799    * @private
800    *
801    * @param bson  the BSON that contains knob information to decode
802    * @returns a decoded value (null if unable to decode).
803    */
804   baja.bson.decodeKnob = function (bson) { 
805     var targetOrd = baja.Ord.make(bson.to);
806     
807     // TODO: Document these methods
808     return {
809       getId: function getId() {
810         return bson.id;
811       },
812       
813       getSourceSlotName: function getSourceSlotName() {
814         return bson.ss;
815       },
816             
817       getTargetOrd: function getTargetOrd() {
818         return targetOrd;
819       },
820       
821       getTargetSlotName: function getTargetSlotName() {
822         return bson.ts;
823       }
824     };
825   };
826 
827   /**
828    * Return a decoded value.
829    *
830    * @private
831    *
832    * @param bson  the BSON to decode.
833    * @param [Object] cx the context used when decoding.
834    * @returns a decoded value (null if unable to decode).
835    */
836   baja.bson.decodeValue = function (bson, cx, parent) {     
837     cx = cx || {};
838     
839     // Please note the parent object is designed only to be used internally!
840                 
841     // TODO: Skip this from LoadOp - needed for loading security permissions at some point!
842     if (!(bson.nm === "p" || bson.nm === "a" || bson.nm === "t")) {
843       return null;
844     }
845     
846     // Decode
847     var slot = null,
848         slotType = bajaDef(bson.nm, "p"),
849         stub = bajaDef(bson.stub, false),
850         flags,
851         i, x;
852     
853     if (parent) {
854       slot = parent.getSlot(bson.n);
855     }
856     
857     // Slot Flags    
858     if (slot !== null) {
859       if (bson.f !== undefined) { 
860         flags = baja.Flags.decodeFromString(bson.f);      
861         if (flags !== slot.getFlags()) {
862           parent.setFlags({
863             "slot": slot, 
864             "flags": flags, 
865             "cx": cx
866           });
867         }
868       }
869       
870       if (!slot.isProperty()) {
871         return null;
872       }
873     }
874     else {
875       if (slotType !== "p") {
876         throw new Error("Error decoding Slot from BSON: Missing frozen Slot: " + slotType);
877       }
878     }
879     
880     // Create object used for decoding
881     var obj;
882     if (bson.t === undefined) {
883       // TODO: Should be getDefaultValue()?
884       obj = slot.$getValue();
885     }
886     else {
887       // Get an instance of the Type
888       obj = baja.$(bson.t);
889     }
890     
891     // Decode if a Simple
892     if (obj.getType().isSimple() && bson.v !== undefined) {
893       obj = obj.decodeFromString(bson.v);
894     }
895     
896     // Decode BSON specifically for baja:Action and baja:Topic
897     if (obj.getType().isAction()) {      
898       obj.$paramType = decodeActionParamType(bson);
899       obj.$paramDef = decodeActionParamDefault(bson);
900       obj.$returnType = decodeActionReturnType(bson);
901     }
902     else if (obj.getType().isTopic()) {
903       obj.$eventType = decodeTopicEventType(bson);
904     }
905     
906     // Decode Component
907     if (obj.getType().isComponent()) {    
908       // Decode handle
909       if (bson.h !== undefined) {
910         obj.$handle = bson.h;
911       }
912       
913       // Decode knobs
914       if (bson.nk) {
915         for (x = 0; x < bson.nk.length; ++x) {
916           obj.$fw("installKnob", baja.bson.decodeKnob(bson.nk[x]), cx);
917         }
918       }
919       
920       // Decode permissions
921       if (bson.l && typeof bson.l.p === "string") {
922         obj.$fw("setPermissions", bson.l.p);
923       }
924       
925       // TODO: Handle Component Stub decoding here
926       if (!stub) {
927         obj.$bPropsLoaded = true;
928       }
929     }
930               
931     var facets;       
932     if (parent) {
933       try {
934         cx.displayName = bajaDef(bson.dn, "");
935         cx.display = bajaDef(bson.d, "");
936       
937         if (slot !== null) {
938           parent.set({
939             "slot": slot, 
940             "value": obj,
941             "cx": cx
942           });
943         }
944         else if (parent.getType().isComponent()) {
945           facets = baja.Facets.DEFAULT.decodeFromString(bajaDef(bson.x, ""));  
946           flags = baja.Flags.decodeFromString(bajaDef(bson.f, "0"));         
947           parent.add({
948             "slot": bson.n, 
949             "value": obj, 
950             "flags": flags, 
951             "facets": facets, 
952             "cx": cx
953           });
954         }
955       }
956       finally {   
957         cx.displayName = undefined;
958         cx.display = undefined;
959       }
960     }
961 
962     // Decode kids
963     if (bson.s) {
964       for (i = 0; i < bson.s.length; ++i) {
965         bsonDecodeValue(bson.s[i], cx, obj);
966       }
967     }
968 
969     return obj;    
970   };
971 
972   bsonDecodeValue = baja.bson.decodeValue;
973     
974   ////////////////////////////////////////////////////////////////
975   // BSON Encoding
976   ////////////////////////////////////////////////////////////////  
977   
978   function encodeSlot(parObj, par, slot) {
979     if (slot === null) {
980       return;
981     }
982             
983     // Encode Slot Flags (if they differ from the default    
984     var value = null, // Property Value
985         skipv = false;    // Skip value
986     
987     if (slot.isProperty()) {
988       value = par.get(slot);    
989       if (slot.isFrozen()) {
990         if (value.equivalent(slot.getDefaultValue())) {
991           skipv = true;
992         }
993       }
994     }
995     else {
996       skipv = true;
997     }
998     
999     var flags = par.getFlags(slot);
1000         
1001     // Skip frozen Slots that have default flags and value
1002     if (flags === slot.getDefaultFlags() && skipv) {
1003       return;
1004     }
1005     
1006     // Encode Slot Type
1007     var o = {};
1008     if (slot.isProperty()) {
1009       o.nm = "p";
1010     }
1011     else if (slot.isAction()) {
1012       o.nm = "a";
1013     }
1014     else if (slot.isTopic()) {
1015       o.nm = "t";
1016     }
1017     
1018     // Slot name
1019     o.n = slot.getName();
1020     
1021     // Slot Flags if necessary
1022     if (((!slot.isFrozen() && flags !== 0) || (flags !== slot.getDefaultFlags())) && par.getType().isComponent()) {
1023       o.flags = flags;
1024     }
1025     
1026     // Slot facets if necessary
1027     var fc = slot.getFacets();
1028     if (!slot.isFrozen() && fc.getKeys().length > 0) {
1029       o.x = fc.encodeToString();
1030     }
1031     
1032     if (value !== null && value.getType().isComponent()) {
1033       // Encode handle
1034       if (value.isMounted()) {
1035         o.h = value.getHandle();
1036       }
1037       
1038       // TODO: Categories and stub?
1039     }
1040     // TODO: Need to re-evalulate this method by going through BogEncoder.encodeSlot again
1041     
1042     if (!skipv && slot.isProperty()) {
1043       o.t = value.getType().getTypeSpec();
1044       encodeVal(o, value);
1045     }
1046     
1047     // Now we've encoded the Slot, add it to the Slots array
1048     if (!parObj.s) {
1049       parObj.s = [];
1050     }
1051     
1052     parObj.s.push(o);
1053   }
1054   
1055   function encodeVal(obj, val) {
1056   
1057     var cursor;
1058     
1059     if (val.getType().isSimple()) {
1060       // Encode Simple
1061       obj.v = val.encodeToString();
1062     }
1063     else {
1064       // Encode Complex
1065       cursor = val.getSlots();
1066      
1067       // Encode all of the Slots on the Complex
1068       while (cursor.next()) {
1069         encodeSlot(obj, val, cursor.get());
1070       }
1071     }
1072   }  
1073    
1074   function encode(name, val) {
1075     var o = { nm: "p" };
1076     
1077     // Encode name
1078     if (name !== null) {
1079       o.n = name;
1080     }
1081     
1082     if (val.getType().isComponent()) {
1083     
1084       // Encode handle
1085       if (val.isMounted()) {
1086         o.h = val.getHandle();
1087       }
1088       
1089       // TODO: Encode categories
1090       
1091       // TODO: Encode whether this Component is fully loaded or not???
1092     }
1093     
1094     o.t = val.getType().getTypeSpec();
1095     encodeVal(o, val);
1096     return o;
1097   } 
1098   
1099   /**
1100    * Return an encoded BSON value.
1101    *
1102    * @private
1103    *
1104    * @param val  the value to encode to BSON.
1105    * @returns encoded BSON value.
1106    */
1107   baja.bson.encodeValue = function (val) {
1108     return encode(null, val);
1109   };  
1110   
1111 }(baja));