1 //
  2 // Copyright 2010, Tridium, Inc. All Rights Reserved.
  3 //
  4 
  5 /**
  6  * Core Object Resolution Descriptor 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, 
 14 eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false, 
 15 indent: 2, vars: true, continue: true */
 16 
 17 // Globals for JsLint to ignore 
 18 /*global baja, BaseBajaObj, encodeURI, decodeURI, decodeURIComponent, encodeURIComponent*/ 
 19   
 20 (function ord(baja, BaseBajaObj) {
 21 
 22   // Use ECMAScript 5 Strict Mode
 23   "use strict";
 24   
 25   // Create local for improved minification
 26   var strictArg = baja.strictArg,
 27       bajaDef = baja.def,
 28       objectify = baja.objectify,
 29       Callback = baja.comm.Callback;
 30     
 31   ////////////////////////////////////////////////////////////////
 32   // Local Host and Component Space
 33   //////////////////////////////////////////////////////////////// 
 34   
 35   /**
 36    * @namespace Local Host
 37    */
 38   baja.nav.localhost = baja.nav.$addChildNode(new baja.LocalHost());
 39   
 40   /**
 41    * @namespace local Station Component Space
 42    */
 43   baja.nav.localhost.station = baja.nav.localhost.$addChildNode(new baja.BoxComponentSpace("station", "station:", baja.nav.localhost));
 44   
 45   /**
 46    * Shortcut to local Station Component Space
 47    *
 48    * @see baja.nav.localhost.station
 49    */
 50   baja.station = baja.nav.localhost.station;
 51   
 52   ////////////////////////////////////////////////////////////////
 53   // ORD
 54   //////////////////////////////////////////////////////////////// 
 55   
 56   /**
 57    * @class ORD Query.
 58    * <p>
 59    * The base class for all OrdQuery Objects.
 60    *
 61    * @name OrdQuery
 62    * @private
 63    * @inner
 64    */
 65   var OrdQuery = function (obj) { 
 66     obj = obj || {};  
 67     this.$scheme = obj.scheme;
 68     this.$schemeName = obj.schemeName;
 69     this.$body = obj.body;
 70     this.$isHost = obj.isHost || false;
 71     this.$isSession = obj.isSession || false;
 72     
 73     // Override any functions
 74     var p;
 75     for (p in obj) {
 76       if (obj.hasOwnProperty(p) && typeof obj[p] === "function") {
 77         this[p] = obj[p];
 78       }
 79     }
 80   }.$extend(BaseBajaObj);
 81   
 82   /**
 83    * Return the ORD Scheme.
 84    *
 85    * @returns {baja.OrdScheme}
 86    */
 87   OrdQuery.prototype.getScheme = function () {
 88     return this.$scheme;
 89   };
 90   
 91   /**
 92    * Return the ORD Scheme name.
 93    *
 94    * @returns {String}
 95    */
 96   OrdQuery.prototype.getSchemeName = function () {
 97     return this.$schemeName;
 98   };
 99   
100   /**
101    * Return the body for the query.
102    *
103    * @returns {String}
104    */
105   OrdQuery.prototype.getBody = function () {
106     return this.$body;
107   };
108   
109   /**
110    * Return a String representation of the query.
111    *
112    * @returns {String}
113    */
114   OrdQuery.prototype.toString = function () {
115     return this.getSchemeName() + ":" + this.getBody();
116   };
117     
118   /**
119    * Return true if the Query is a Host.
120    *
121    * @returns {Boolean}
122    */
123   OrdQuery.prototype.isHost = function () {
124     return this.$isHost;
125   };
126   
127   /**
128    * Return true if the Query is a Session.
129    *
130    * @returns {Boolean}
131    */
132   OrdQuery.prototype.isSession = function () {
133     return this.$isSession;
134   };
135   
136   /**
137    * Normalize the query and return true if modified.
138    *
139    * @private
140    *
141    * @param {OrdQueryList} list
142    * @param {Number} index
143    *
144    * @returns {Boolean}
145    */
146   OrdQuery.prototype.normalize = function (list, index) {
147     return false;
148   };
149   
150   /**
151    * @class Cursor for an ORD Query List.
152    * 
153    * @name OrdQueryListCursor
154    * @extends baja.SyncCursor
155    * @inner
156    * @public
157    */
158   var OrdQueryListCursor = function (list) {
159     OrdQueryListCursor.$super.apply(this, arguments);
160     this.$list = list;
161     this.$index = -1;
162   }.$extend(baja.SyncCursor);
163   
164   /**
165    * Return true if there's another query in the Cursor.
166    *
167    * @returns {Boolean}
168    */
169   OrdQueryListCursor.prototype.hasNext = function () {
170     return this.$index + 1 < this.$list.$queries.length;
171   };
172 
173   /**
174    * Advance the Cursor to the next query.
175    *
176    * @returns {Boolean} returns true if there's another query in the Cursor.
177    */
178   OrdQueryListCursor.prototype.next = function () {
179     if (!this.hasNext()) {
180       return false;
181     }
182     else {
183       this.$index++;
184       return true;
185     }
186   };
187   
188   /**
189    * Return the current query from the Cursor.
190    *
191    * @returns the ORD Query.
192    */
193   OrdQueryListCursor.prototype.get = function () {
194     if (this.$index === -1) {
195       throw new Error("Illegal cursor index");
196     }
197     return this.$list.$queries[this.$index];
198   };
199   
200   /**
201    * Iterate through the Cursor and call 'each' on every item.
202    * 
203    * @param {Function} func function called on every iteration with the 'value' being used as an argument.
204    */
205   OrdQueryListCursor.prototype.each = function (func) {
206     var result;
207     while (this.next()) {
208       result = func(this.get(), this.$index);
209       if (result) {
210         return result;
211       }
212     }
213   };
214   
215   /**
216    * Return the current index for the Cursor.
217    *
218    * @returns {Number}
219    */
220   OrdQueryListCursor.prototype.getIndex = function () {
221     return this.$index;
222   };
223   
224   /**
225    * Return the current ORD String at the current index.
226    *
227    * @returns {String}
228    */
229   OrdQueryListCursor.prototype.getOrd = function () {
230     return this.$list.toString(this.$index + 1);
231   };
232   
233   /**
234    * Resolve the next ORD Query.
235    *
236    * @param {OrdTarget} target
237    * @param {Object} options
238    */
239   OrdQueryListCursor.prototype.resolveNext = function (target, options) {
240     try {
241       // Resolve the next part of the ORD scheme
242       if (this.next()) {
243         var query = this.get();
244         query.getScheme().resolve(target, query, this, options);
245       }
246       // If we've finished iterating through the ORDs then
247       // call the original ok callback
248       else {
249         options.callback.ok(target);
250       }
251     }
252     catch (err) {
253       options.callback.fail(err);
254     }
255   };
256   
257   /**
258    * @class ORD Query List.
259    * <p>
260    * Used to hold a list of OrdQueries.
261    *
262    * @name baja.OrdQueryList
263    * @extends BaseBajaObj
264    *
265    * @see OrdQuery
266    *
267    * @param {Array} [queries] an array of ORD queries.
268    */   
269   baja.OrdQueryList = function (queries) {  
270     this.$queries = queries || [];
271   }.$extend(BaseBajaObj);
272   
273   /**
274    * Add an ORD Query to the List
275    *
276    * @param query 
277    */
278   baja.OrdQueryList.prototype.add = function (query) {
279     this.$queries.push(query); 
280     this.$hasUnknown = undefined; // reset the cached hasUnknown result
281   };
282   
283   /**
284    * Is the list empty?
285    *
286    * @returns {Boolean}
287    */
288   baja.OrdQueryList.prototype.isEmpty = function () {
289     return this.$queries.length === 0;
290   };
291   
292   /**
293    * Does the list contain an unknown ORD scheme?
294    *
295    * @private
296    *
297    * @returns {Boolean}
298    */
299   baja.OrdQueryList.prototype.hasUnknown = function () {
300     if (this.$hasUnknown !== undefined) {
301       return this.$hasUnknown;
302     }
303   
304     // Search for an unknown ORD scheme and cache the result
305     var i, unknown = false;
306     for (i = 0; i < this.$queries.length; ++i) {
307       if (this.$queries[i].getScheme() instanceof baja.UnknownScheme) {
308         unknown = true;
309       }
310     }
311     this.$hasUnknown = unknown;
312     return unknown;
313   };
314   
315   /**
316    * Returns a Cursor for use with the ORD Query List.
317    *
318    * @returns {OrdQueryListCursor} the Cursor for the ORD Query List.
319    */
320   baja.OrdQueryList.prototype.getCursor = function () {
321     return new OrdQueryListCursor(this);
322   };
323   
324   function normalizeOrdQueryList(list) {
325     var i;
326     for (i = 0; i < list.$queries.length; ++i) {
327       if (list.$queries[i].normalize(list, i)) {
328         return true;
329       }
330     }
331     return false;
332   }
333   
334   /**
335    * Normalize the ORD Query List.
336    * 
337    * @private
338    *
339    * @returns {baja.OrdQueryList} returns itself.
340    */
341   baja.OrdQueryList.prototype.normalize = function () {
342     // Don't try to normalize if there's unknown client ORD Schemes
343     if (!this.hasUnknown()) {
344       for (;;) {
345         if (!normalizeOrdQueryList(this)) {
346           break;
347         }
348       }
349     }
350     return this;
351   };
352   
353   /**
354    * Return String representation of the ORD Query List.
355    *
356    * @returns {String}
357    */
358   baja.OrdQueryList.prototype.toString = function (length) {
359     length = bajaDef(length, this.$queries.length);
360     var a = [], i;
361     for (i = 0; i < length; ++i) {
362       a.push(this.$queries[i].toString());
363     }
364     return a.join("|");
365   };
366   
367   /**
368    * Return the query object at the specified index.
369    *
370    * @param {Number|String} index or scheme name.
371    * @returns query (or null if can't be found).
372    */
373   baja.OrdQueryList.prototype.get = function (index) {
374     var to = typeof index,
375         queries = this.$queries,
376         i,
377         q;
378         
379     if (to === "number") {
380       // Get via index
381       q = queries[index];
382     }
383     else if (to === "string") {
384       // Search via scheme name
385       for (i = 0; i < queries.length; ++i) {
386         if (queries[i].getSchemeName() === index) {
387           q = queries[i];
388           break;
389         }
390       }
391     }
392     
393     return q || null;
394   };
395     
396   /**
397    * Set an ORD query object at the given index.
398    *
399    * @param {Number} index 
400    * @param query
401    */
402   baja.OrdQueryList.prototype.set = function (index, query) {
403     if (index < 0 || index > this.$queries.length) {
404       throw new Error("Invalid index (" + index + ")");
405     }
406     this.$queries[index] = query;
407     this.$hasUnknown = undefined; // reset the cached hasUnknown result
408   };
409   
410   /**
411    * Remove the entry at the specified index and return it.
412    *
413    * @param {Number} index 
414    * @returns query
415    */
416   baja.OrdQueryList.prototype.remove = function (index) {
417     var query = null;
418     if (index >= 0 && index < this.$queries.length) {
419       query = this.$queries.splice(index, 1)[0];
420       this.$hasUnknown = undefined; // reset the cached hasUnknown result    
421     }
422     return query;
423   };
424   
425   /**
426    * Return the size of the query list.
427    * 
428    * @returns {Number} size of the list.
429    */
430   baja.OrdQueryList.prototype.size = function () {
431     return this.$queries.length;
432   };
433         
434   /**
435    * @class ORD Target.
436    * <p>
437    * This constructor shouldn't be invoked directly.
438    *  
439    * @name OrdTarget
440    * @extends BaseBajaObj
441    * @inner
442    * @public
443    *
444    * @param {OrdTarget} [base]  the base ORD Target
445    */
446   var OrdTarget = function (base) {    
447     this.base = base || null;  
448     this.object = null;
449     
450     if (base && typeof base === "object") {
451       base.next = this;
452     }    
453   }.$extend(BaseBajaObj);
454   
455   /**
456    * Return the Component for the ORD Target.
457    *
458    * @returns {baja.Component}
459    */
460   OrdTarget.prototype.getComponent = function () {
461     if (baja.hasType(this.container) && this.container.getType().isComponent()) {
462       return this.container;
463     }
464     else if (baja.hasType(this.object)) {
465       if (this.object.getType().isComponent()) {
466         return this.object;
467       }
468       else if (this.object.getType().isComplex()) {
469         var o = this.object.getParent();
470         while (o !== null) {
471           if (o.getType().isComponent()) {
472             break;
473           }
474           o = o.getParent();
475         }
476         if (o !== null) {
477           return o;
478         }
479       }
480     }
481     if (this.base && typeof this.base === "object") {
482       return this.base.getComponent();
483     }
484     return null;
485   };  
486   
487   /**
488    * Return the object associated with this OrdTarget.
489    * <p>
490    * This method will attempt to access any resolved value from its
491    * Property. If the resolved value wasn't resolved from a Property then 
492    * the original resolved object is returned.
493    *
494    * @returns {baja.Object}
495    */
496   OrdTarget.prototype.getObject = function () {
497     if (this.container) {
498       var parent = this.container,
499           slot,
500           i;
501       
502       // Walk up the Property Path
503       if (this.propertyPath) {
504         if (this.propertyPath.length > 0) {
505           var val = parent;
506           for (i = 0; i < this.propertyPath.length; ++i) {
507             val = val.get(this.propertyPath[i]);
508           } 
509           return val;          
510         }
511       }
512       // If no PropertyPath then access from the Slot
513       else if (this.slot) {
514         return parent.get(this.slot);
515       }
516     }
517     // By default, just return the object originally resolved
518     return this.object;
519   };  
520     
521   /**
522    * @class Object Resolution Descriptor.
523    * <p>
524    * An ORD is how we can access Objects in the Server from BajaScript. It's 
525    * similar to a URI but is much more powerful and extensible. For more
526    * information, please see the Niagara developer documentation on ORDs and how
527    * they're used.
528    * <pre>
529    *   // Resolve an ORD
530    *   baja.Ord.make("station:|slot:/Folder/NumericWritable").get({
531    *     ok: function () {
532    *       baja.outln(this.getOutDisplay());
533    *     },
534    *     lease: true
535    *   });
536    * </pre>
537    * <p>
538    * If more than one ORD needs to be resolved then use a {@link baja.BatchResolve}.
539    * <p>
540    * This Constructor shouldn't be invoked directly. Please use the 'make' methods to create
541    * an instance of an ORD.
542    *
543    * @see baja.Ord.make
544    * @see baja.BatchResolve
545    *
546    * @name baja.Ord
547    * @extends baja.Simple
548    */   
549   baja.Ord = function (ord) {
550     baja.Ord.$super.apply(this, arguments);
551     this.$ord = strictArg(ord, String);    
552   }.$extend(baja.Simple);
553   
554   /**
555    * Default ORD instance.
556    */
557   baja.Ord.DEFAULT = new baja.Ord("");
558     
559   /**
560    * Make an ORD.
561    * <p>
562    * The argument can be a String, baja.Ord or an Object.
563    * If an Object is passed in then if there's a base and child Property, this
564    * will be used to construct the ORD (by calling 'toString' on each). Otherwise 'toString' will be called
565    * on the Object for the ORD.
566    * <pre>
567    *   // Resolve an ORD
568    *   baja.Ord.make("station:|slot:/Folder/NumericWritable").get({
569    *     ok: function () {
570    *       baja.outln(this.getOutDisplay());
571    *     },
572    *     lease: true
573    *   });
574    * </pre>
575    *
576    * @param {String|Object} ord
577    * @returns {baja.Ord}
578    */
579   baja.Ord.make = function (ord) {
580     if (arguments.length === 0) {
581       return baja.Ord.DEFAULT;
582     }
583     
584     // Handle child and base
585     if (typeof ord === "object" && ord.base && ord.child) {
586       ord = ord.base.toString() + "|" + ord.child.toString();
587     }
588     else {
589       ord = ord.toString(); 
590     }  
591         
592     // Handle URL decoding
593     if (ord.match(/^\/ord/)) {
594       // Remove '/ord?' or '/ord/'
595       ord = ord.substring(5, ord.length);
596       
597       // Replace this with the pipe character
598       ord = decodeURI(ord);    
599     }
600     
601     if (ord === "" || ord === "null") {
602       return baja.Ord.DEFAULT;
603     }
604     
605     return new baja.Ord(ord);
606   };
607   
608   /**
609    * Make an ORD.
610    *
611    * @see baja.Ord.make
612    *
613    * @param {String} ord
614    * @returns {baja.Ord}
615    */
616   baja.Ord.prototype.make = function (ord) {
617     return baja.Ord.make(ord);
618   };
619   
620   /**
621    * Decode an ORD from a String.
622    *
623    * @param {String} str  the ORD String.
624    * @returns {baja.Ord} the decoded ORD.
625    */
626   baja.Ord.prototype.decodeFromString = function (str) {
627     return baja.Ord.make(str);
628   };
629   
630   /**
631    * Encode an ORD to a String.
632    *
633    * @returns {String} the ORD encoded to a String.
634    */
635   baja.Ord.prototype.encodeToString = function () {
636     return this.$ord;
637   };
638   
639   // Register Type
640   baja.Ord.registerType("baja:Ord");
641   
642   /**
643    * Return an String representation of the object.
644    *
645    * @returns {String} a String representation of an ORD.
646    */
647   baja.Ord.prototype.toString = function () {
648     return this.$ord;
649   };
650   
651   /**
652    * Return the inner value of this Object.
653    *
654    * @returns {String} a String representation of an ORD.
655    */
656   baja.Ord.prototype.valueOf = function () {
657     return this.toString();
658   };
659       
660   /**
661    * Parse an ORD to a number of ORD Query objects.
662    *
663    * @returns {baja.OrdQueryList} a list of ORDs to resolve.
664    */
665   baja.Ord.prototype.parse = function () {
666     // TODO: Validate all characters are valid
667     var list = new baja.OrdQueryList();
668     if (this.$ord.length === 0) {
669       return list; 
670     }
671     
672     var os = this.$ord.split("|"), // ORDs
673         i,
674         ind,
675         schemeName,
676         scheme,
677         body;
678 
679     for (i = 0; i < os.length; ++i) {      
680       ind = os[i].indexOf(":");
681       if (ind === -1) {
682         throw new Error("Unable to resolve ORD: " + os[i]);
683       }
684         
685       schemeName = os[i].substring(0, ind);
686       body = os[i].substring(ind + 1, os[i].length);      
687       scheme = baja.OrdScheme.lookup(schemeName);
688             
689       // Create the ORD scheme      
690       list.add(scheme.parse(schemeName, body));
691     }
692     return list;
693   };
694      
695   /**
696    * Resolve an ORD.
697    * <p>
698    * Resolving an ORD consists of parsing and processing it to get a result. The result is an ORD Target.
699    * <p>
700    * Any network calls that result from processing an ORD are always asynchronous.
701    * <p>
702    * The resolve method requires an ok function callback or an Object Literal that contains the method's arguments...
703    * <pre>
704    *   baja.Ord.make("station:|slot:/").resolve(function ok(target) {
705    *     // process the ORD Target
706    *   });
707    *
708    *   //...or use an Object Literal to specify multiple arguments...
709    *   
710    *   baja.Ord.make("station:|slot:/").resolve({
711    *     ok: function (target) {
712    *       // process the ORD target
713    *     },
714    *     fail: function (err) {
715    *       // process the failure
716    *     },
717    *     lease: true // ensure any resolved Components are leased before calling 'ok'
718    *   });
719    * </pre> 
720    * <p>
721    * Please note that unlike other methods that require network calls, no batch object can be specified!   
722    *
723    * @see OrdTarget
724    * @see baja.Ord#get
725    * @see baja.RelTime
726    *
727    * @param {Object} [obj] the Object Literal that contains the method's arguments.
728    * @param {Function} [obj.ok] the ok function called once the ORD has been successfully resolved. The ORD Target is passed to this function when invoked.
729    * @param {Function} [obj.fail] the fail function called if the ORD fails to resolve. An error cause is passed to this function when invoked.
730    * @param [obj.base] the base Object to resolve the ORD against.
731    * @param {Boolean} [obj.lease] if defined and true, any Components are temporarily subscribed.
732    * @param {Number|baja.RelTime} [obj.leaseTime] the amount of time in milliseconds to lease for (lease argument must be true). As well as a Number, this can also 
733    *                              a baja.RelTime. 
734    * @param {baja.Subscriber} [obj.subscriber] if defined the Component is subscribed using this Subscriber.
735    * @param {Object} [obj.cursor] if defined, this specifies parameters for iterating through a Cursor (providing the ORD resolves to a Collection or Table).
736    *                              For more information, please see {@link baja.coll.Collection#cursor}.
737    */    
738   baja.Ord.prototype.resolve = function (obj) {    
739     obj = objectify(obj, "ok");
740     
741     var base = bajaDef(obj.base, baja.nav.localhost),
742         cb = obj.cb === undefined ? new Callback(obj.ok, obj.fail) : obj.cb,
743         subscriber = bajaDef(obj.subscriber, null),
744         lease = bajaDef(obj.lease, false), 
745         leaseTime = obj.leaseTime, // If undefined, lease will use lease default
746         full = bajaDef(obj.full, false),
747         cursor = obj.cursor;
748 
749     // Ensure 'this' in callback is the target's Component. If it's not a Component then 
750     // fallback to the resolved Object.
751     cb.addOk(function (ok, fail, target) {
752       var resolvedObj = target.getComponent();
753       if (!resolvedObj) {
754         resolvedObj = target.object;
755       }
756       if (resolvedObj !== null) {
757         ok.call(resolvedObj, target);
758       }
759       else {
760         ok(target);
761       }
762     });
763      
764     if (subscriber !== null) {
765        // If we need to subscribe using a Subscriber once the Component is resolved...
766       cb.addOk(function (ok, fail, target) {
767         function newOk() {
768           ok(target);
769         }
770         var comp = target.getComponent();
771         if (comp !== null && comp.isMounted()) {
772           subscriber.subscribe({
773             "comps": [comp], 
774             "ok": newOk, 
775             "fail": fail,
776             "importAsync": true
777           });
778         }
779         else {
780           newOk();
781         }
782       });
783     }     
784     
785     if (lease) {
786       // If we need to lease once the Component is resolved...
787       cb.addOk(function (ok, fail, target) {
788         function newOk() {
789           ok(target);
790         }
791         var comp = target.getComponent();
792         if (comp !== null && comp.isMounted()) {
793           comp.lease({
794             "ok": newOk, 
795             "fail": fail,
796             "time": leaseTime,
797             "importAsync": true
798           });
799         }
800         else {
801           newOk();
802         }
803       });
804     }
805     
806     try {            
807       // Check the user isnt trying to batch an ORD as this isn't supported
808       if (obj.batch) {
809         throw new Error("Cannot batch ORD resolution");
810       }
811     
812       var ordQueries = this.parse(); 
813       if (ordQueries.isEmpty()) {
814         throw new Error("Cannot resolve null ORD: " + this.toString());
815       }
816       
817       var target = new OrdTarget();
818       target.object = base;
819       
820       var options = {
821         "full": full,
822         "callback": cb,
823         "queries": ordQueries,
824         "ord": this,
825         "cursor": cursor
826       };
827       
828       // Normalize
829       ordQueries.normalize();
830             
831       // If there are ORD Schemes that aren't implemented in BajaScript then we 
832       // simply make a network call and resolve the ORD Server side
833       if (ordQueries.hasUnknown()) {
834         var newTarget = new OrdTarget(target);
835                
836         cb.addOk(function (ok, fail, resp) {      
837           var newOk = function () {
838             // Decode the result
839             var t = newTarget.object = baja.bson.decodeValue(resp.o, baja.$serverDecodeContext);
840             
841             // Finished iterating so just make the callback
842             ok(newTarget); 
843             
844             // If we've got a collection result cached then call 'cursor' on the Collection or Table
845             if (resp.c &&
846                 baja.hasType(t) &&
847                 t.getType().is("baja:ICollection") && 
848                 typeof t.cursor === "function") {
849               cursor.$data = resp.c;
850               t.cursor(cursor);              
851             }           
852           };
853           
854           // Scan for any unknown types we don't have here and make another
855           // network request if necessary 
856           var unknownTypes = baja.bson.scanForUnknownTypes(resp);
857           
858           if (unknownTypes.length > 0) {
859             var importBatch = new baja.comm.Batch();
860             
861             baja.importTypes({
862               "typeSpecs": unknownTypes, 
863               "ok": newOk, 
864               "fail": fail,
865               "batch": importBatch
866             });
867             
868             // Commit synchronously if this has been resolved from a BatchResolve. 
869             // It's best to do this as there's other ORDs also resolving in tandem.
870             if (obj.fromBatchResolve) {
871               importBatch.commitSync();
872             }
873             else {
874               importBatch.commit();
875             }
876           }
877           else {
878             newOk();
879           }       
880         });
881         
882         // If Cursor information is defined, ensure we set some defaults
883         if (options.cursor) {
884           options.cursor.limit = options.cursor.limit || 10;
885           options.cursor.offset = options.cursor.offset || 0;
886         }
887         
888         // Make the network call to resolve the complete ORD Server side       
889         baja.comm.resolve(this, base, cb, options); 
890       }
891       else {
892                                 
893         // Resolve the ORD. Each ORD scheme must call 'resolveNext' on the cursor to process the next 
894         // part of the ORD. This design has been chosen because some ORD schemes may need to make network calls.
895         // If the network call is asynchronous in nature then they'll be a delay before the ORD can process further    
896         ordQueries.getCursor().resolveNext(target, options);
897       }
898     }
899     catch (err) {
900       cb.fail(err);
901     }
902   };
903      
904   /**
905    * Resolve the ORD and get the resolved Object from the ORD Target.
906    * <p>
907    * This method calls {@link baja.Ord#resolve} and calls 'get' on the ORD Target to
908    * pass the object onto the ok function callback.
909    * <p>
910    * An Object Literal is used to to specify the method's arguments. For more information
911    * on how to use this method please see {@link baja.Ord#resolve}.
912    * <pre>
913    *   baja.Ord.make("service:baja:UserService|slot:jack").get({
914    *     ok: function (user) {
915    *       // Do something with the user...
916    *     },
917    *     lease: true
918    *   });
919    * </pre>
920    *
921    * @see OrdTarget#resolve
922    *
923    * @param {Object} [object]
924    * @returns the value resolved from the ORD.
925    */   
926   baja.Ord.prototype.get = function (obj) {
927     obj = objectify(obj, "ok"); 
928     
929     obj.cb = new Callback(obj.ok, obj.fail);
930     obj.cb.addOk(function (ok, fail, target) {
931       ok.call(this, target.object);
932     }); 
933   
934     this.resolve(obj);
935   };
936   
937   /**
938    * Return a normalized version of the ORD.
939    *
940    * @returns {baja.Ord}
941    */   
942   baja.Ord.prototype.normalize = function () {
943     return baja.Ord.make(this.parse().normalize());
944   };
945     
946   /**
947    * Relativize is used to extract the relative portion
948    * of this ord within an session:
949    * <ol>
950    * <li>
951    * First the ord is normalized.</li>
952    * <li>
953    * Starting from the left to right, if any queries are
954    * found which return true for isSession(), then remove
955    * everything from that query to the left.</li>
956    * </ol>
957    *
958    * @returns {baja.Ord}
959    */
960   baja.Ord.prototype.relativizeToSession = function () {
961     var list = this.parse().normalize(),
962         q,
963         newList = new baja.OrdQueryList(),
964         i;
965         
966     for (i = 0; i < list.size(); ++i) {
967       q = list.get(i);
968       
969       if (!q.isSession() && !q.isHost()) {
970         newList.add(q);
971       }
972     } 
973     return baja.Ord.make(newList);
974   };
975   
976   /**
977    * Return the ORD as URI that can be used in a browser.
978    *
979    * @returns {String}
980    */   
981   baja.Ord.prototype.toUri = function () {
982     var uri = encodeURI(this.relativizeToSession().toString());
983     
984     // Hack for HTTP scheme
985     return uri.match(/^http/i) ? uri : ("/ord?" + uri);
986   };
987           
988   /**
989    * @class ORD Scheme.
990    * <p>
991    * An ORD is made up of a series of ORD Queries separated by the '|' character.
992    * Each ORD Query has an ORD Scheme name (i.e. 'slot') and a body (i.e. '/Drivers/ModbusNetwork').
993    *
994    * @name baja.OrdScheme
995    * @extends baja.Singleton
996    */     
997   baja.OrdScheme = function () {
998     baja.OrdScheme.$super.apply(this, arguments);
999   }.$extend(baja.Singleton).registerType("baja:OrdScheme"); 
1000   
1001   /**
1002    * All extended ORD Schemes must implement this method so an ORD can be resolved!
1003    *
1004    * @param {ORDTarget} target  the current ORD Target
1005    * @param {Object} query  the ORD Query used in resolving the ORD
1006    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD
1007    * @param {Object} options  options used for resolving an ORD
1008    */
1009   baja.OrdScheme.prototype.resolve = function (target, query, cursor, options) {
1010     throw new Error("ORD Scheme must implement resolve: " + this.getType());
1011   };
1012       
1013   /**
1014    * Return the ORD Scheme for the given scheme name.
1015    *
1016    * @returns {baja.OrdScheme}
1017    */
1018   baja.OrdScheme.lookup = function (schemeName) {
1019     var type = baja.registry.getOrdScheme(schemeName);
1020     return type && type.hasConstructor() ? type.getInstance() : baja.UnknownScheme.DEFAULT;
1021   };
1022   
1023   /**
1024    * Return an ORD Query for the scheme.
1025    *
1026    * @returns {OrdQuery}
1027    */
1028   baja.OrdScheme.prototype.parse = function (schemeName, body) {
1029     return new OrdQuery({
1030       scheme: this,
1031       schemeName: schemeName,
1032       body: body
1033     });
1034   };
1035   
1036   ////////////////////////////////////////////////////////////////
1037   // Unknown ORD Scheme
1038   //////////////////////////////////////////////////////////////// 
1039   
1040   /**
1041    * @class Unknown ORD Scheme. 
1042    * <p>
1043    * Some ORD Schemes are represented in BajaScript and some are not. When an ORD is resolved, 
1044    * the BajaScript Registry is used to see if we locally have an ORD scheme representation.
1045    * If all of the ORD Schemes in an ORD do have a local representation (i.e. they have JS Constructors),
1046    * the ORD is resolved locally. If any unknown ORD schemes are found then the entire ORD is resolved 
1047    * on the Server and the corresponding results are serialized and sent back down to the client.
1048    *
1049    * @name baja.UnknownScheme
1050    * @extends baja.OrdScheme
1051    * @private
1052    */    
1053   baja.UnknownScheme = function () {
1054     baja.UnknownScheme.$super.apply(this, arguments);
1055   }.$extend(baja.OrdScheme);
1056   
1057   /**
1058    * Default Unknown ORD Scheme instance.
1059    * @private
1060    */
1061   baja.UnknownScheme.DEFAULT = new baja.UnknownScheme();
1062   
1063   /**
1064    * Called when an ORD is resolved.
1065    *
1066    * @private
1067    *
1068    * @see baja.OrdScheme#resolve
1069    *
1070    * @param {ORDTarget} target  the current ORD Target.
1071    * @param {Object} query  the ORD Query used in resolving the ORD.
1072    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD.
1073    * @param {Object} options  options used for resolving an ORD.
1074    */
1075   baja.UnknownScheme.prototype.resolve = function (target, query, cursor, options) {
1076     
1077     // Fail since this should always be resolved Server side
1078     var newTarget = new OrdTarget(target);
1079     options.callback.fail("Unknown BajaScript ORD Scheme: " + query.getSchemeName());
1080   };
1081   
1082   ////////////////////////////////////////////////////////////////
1083   // Http ORD Scheme
1084   //////////////////////////////////////////////////////////////// 
1085   
1086   /**
1087    * @class HTTP ORD Scheme. 
1088    *
1089    * @name baja.HttpScheme
1090    * @extends baja.OrdScheme
1091    * @private
1092    */    
1093   baja.HttpScheme = function () {
1094     baja.HttpScheme.$super.apply(this, arguments);
1095   }.$extend(baja.OrdScheme).registerType("net:HttpScheme");
1096   
1097   /**
1098    * Default HTTP ORD Scheme instance.
1099    * @private
1100    */
1101   baja.HttpScheme.DEFAULT = new baja.HttpScheme();
1102   
1103   /**
1104    * Called when an ORD is resolved.
1105    *
1106    * @private
1107    *
1108    * @see baja.OrdScheme#resolve
1109    *
1110    * @param {ORDTarget} target  the current ORD Target.
1111    * @param {Object} query  the ORD Query used in resolving the ORD.
1112    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD.
1113    * @param {Object} options  options used for resolving an ORD.
1114    */
1115   baja.HttpScheme.prototype.resolve = function (target, query, cursor, options) {
1116     cursor.resolveNext(new OrdTarget(target), options);
1117   };
1118   
1119   function trimToStart(list, index) {
1120     if (index > 0) {
1121       var size = list.size(), i;
1122       for (i = 0; i < index; ++i) {
1123         list.remove(0);
1124       }
1125       return true;
1126     }
1127     return false;
1128   }
1129   
1130   /**
1131    * Return an ORD Query for the scheme.
1132    *
1133    * @returns {OrdQuery}
1134    */
1135   baja.HttpScheme.prototype.parse = function (schemeName, body) {
1136     return new OrdQuery({
1137       scheme: this,
1138       schemeName: schemeName,
1139       body: body,
1140       normalize: trimToStart
1141     });
1142   };
1143     
1144   ////////////////////////////////////////////////////////////////
1145   // Host Schemes
1146   //////////////////////////////////////////////////////////////// 
1147     
1148   /**
1149    * @class Local Host ORD Scheme.
1150    * <p>
1151    * This scheme resolved to the local host. The local host represents the Station BajaScript is directly connected too.
1152    *
1153    * @name baja.LocalScheme
1154    * @extends baja.OrdScheme
1155    * @private
1156    */    
1157   baja.LocalScheme = function () {
1158     baja.LocalScheme.$super.apply(this, arguments);
1159   }.$extend(baja.OrdScheme);
1160   
1161   /**
1162    * Default Local ORD Scheme instance.
1163    * @private
1164    */
1165   baja.LocalScheme.DEFAULT = new baja.LocalScheme();
1166   
1167   // Register Type
1168   baja.LocalScheme.registerType("baja:LocalScheme"); 
1169 
1170   /**
1171    * Called when an ORD is resolved.
1172    *
1173    * @private
1174    *
1175    * @see baja.OrdScheme#resolve
1176    *
1177    * @param {ORDTarget} target  the current ORD Target.
1178    * @param {Object} query  the ORD Query used in resolving the ORD.
1179    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD.
1180    * @param {Object} options  options used for resolving an ORD.
1181    */
1182   baja.LocalScheme.prototype.resolve = function (target, query, cursor, options) {
1183     var newTarget = new OrdTarget(target);
1184     newTarget.object = baja.nav.localhost;
1185     cursor.resolveNext(newTarget, options);
1186   };
1187   
1188   /**
1189    * Return an ORD Query for the scheme.
1190    *
1191    * @returns {OrdQuery}
1192    */
1193   baja.LocalScheme.prototype.parse = function (schemeName, body) {
1194     return new OrdQuery({
1195       scheme: this,
1196       schemeName: schemeName,
1197       body: body,
1198       isHost: true,
1199       isSession: true,
1200       normalize: trimToStart
1201     });
1202   };
1203   
1204   ////////////////////////////////////////////////////////////////
1205   // Session Schemes
1206   //////////////////////////////////////////////////////////////// 
1207     
1208   /**
1209    * @class Fox ORD Scheme.
1210    *
1211    * @name baja.FoxScheme
1212    * @extends baja.OrdScheme
1213    * @private
1214    */    
1215   baja.FoxScheme = function () {
1216     baja.FoxScheme.$super.apply(this, arguments);
1217   }.$extend(baja.OrdScheme);
1218   
1219   /**
1220    * Default Fox ORD Scheme instance.
1221    * @private
1222    */
1223   baja.FoxScheme.DEFAULT = new baja.FoxScheme();
1224   
1225   // Register Type
1226   baja.FoxScheme.registerType("fox:FoxScheme"); 
1227 
1228   /**
1229    * Called when an ORD is resolved.
1230    *
1231    * @private
1232    *
1233    * @see baja.OrdScheme#resolve
1234    *
1235    * @param {ORDTarget} target  the current ORD Target.
1236    * @param {Object} query  the ORD Query used in resolving the ORD.
1237    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD.
1238    * @param {Object} options  options used for resolving an ORD.
1239    */
1240   baja.FoxScheme.prototype.resolve = function (target, query, cursor, options) {
1241     var newTarget = new OrdTarget(target);
1242     newTarget.object = target.object;
1243     cursor.resolveNext(newTarget, options);
1244   };
1245   
1246   /**
1247    * Return an ORD Query for the scheme.
1248    *
1249    * @returns {OrdQuery}
1250    */
1251   baja.FoxScheme.prototype.parse = function (schemeName, body) {
1252     return new OrdQuery({
1253       scheme: this,
1254       schemeName: schemeName,
1255       body: body,
1256       isSession: true,
1257       normalize: function (list, index) {
1258         // Shift to host
1259         var i, q, modified = false;
1260         for (i = index - 1; i >= 0; --i) {
1261           q = list.get(i);
1262           if (!q.isHost()) {
1263             list.remove(i);
1264             modified = true;
1265           }
1266         }
1267         return modified;
1268       }
1269     });
1270   };
1271       
1272   ////////////////////////////////////////////////////////////////
1273   // Space Schemes
1274   //////////////////////////////////////////////////////////////// 
1275   
1276   /**
1277    * @class Station ORD Scheme.
1278    * <p>
1279    * This scheme resolves to a Station.
1280    *
1281    * @name baja.StationScheme
1282    * @extends baja.OrdScheme
1283    * @private
1284    */    
1285   baja.StationScheme = function () {
1286     baja.StationScheme.$super.apply(this, arguments);
1287   }.$extend(baja.OrdScheme); // TODO: Need to extend from SpaceScheme eventually
1288   
1289   /**
1290    * Default Station ORD Scheme instance.
1291    * @private
1292    */
1293   baja.StationScheme.DEFAULT = new baja.StationScheme();
1294   
1295   // Register Type
1296   baja.StationScheme.registerType("baja:StationScheme"); 
1297   
1298   /**
1299    * Called when an ORD is resolved.
1300    *
1301    * @private
1302    *
1303    * @see baja.OrdScheme#resolve
1304    *
1305    * @param {ORDTarget} target  the current ORD Target.
1306    * @param {Object} query  the ORD Query used in resolving the ORD.
1307    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD.
1308    * @param {Object} options  options used for resolving an ORD.
1309    */
1310   baja.StationScheme.prototype.resolve = function (target, query, cursor, options) {
1311     
1312     var newTarget = new OrdTarget(target);
1313     newTarget.object = target.object;
1314     
1315     if (!newTarget.object.getType().is("baja:ComponentSpace")) {
1316       newTarget.object = baja.nav.localhost.station;
1317     }
1318     
1319     cursor.resolveNext(newTarget, options);
1320   };
1321   
1322   /**
1323    * Return an ORD Query for the scheme.
1324    *
1325    * @returns {OrdQuery}
1326    */
1327   baja.StationScheme.prototype.parse = function (schemeName, body) {
1328     return new OrdQuery({
1329       scheme: this,
1330       schemeName: schemeName,
1331       body: body,
1332       normalize: function (list, index) {
1333         // Shift to session
1334         var i, q, modified = false;
1335         for (i = index - 1; i >= 0; --i) {
1336           q = list.get(i);
1337           if (!q.isHost() && !q.isSession()) {
1338             list.remove(i);
1339             modified = true;
1340           }
1341         }
1342         return modified;
1343       } 
1344     });
1345   };
1346       
1347   ////////////////////////////////////////////////////////////////
1348   // SlotPath Resolution
1349   //////////////////////////////////////////////////////////////// 
1350   
1351   /**
1352    * @class Slot ORD Scheme.
1353    * <p>
1354    * This scheme resolves a SlotPath to a Niagara Object.
1355    *
1356    * @see baja.SlotPath
1357    *
1358    * @name baja.SlotScheme
1359    * @extends baja.OrdScheme
1360    */    
1361   baja.SlotScheme = function () {
1362     baja.SlotScheme.$super.apply(this, arguments);
1363   }.$extend(baja.OrdScheme); // TODO: Need to extend from SpaceScheme eventually
1364   
1365   /**
1366    * Default Slot ORD Scheme instance.
1367    * @private
1368    */
1369   baja.SlotScheme.DEFAULT = new baja.SlotScheme();
1370   
1371   // Register Type
1372   baja.SlotScheme.registerType("baja:SlotScheme"); 
1373 
1374   // Empty fail function. It doesn't matter is the loads fail as the resolve without the
1375   // network calls will result in the fail we want
1376   var emptyFail = function (err) {};
1377   
1378   function slotSchemeResolve(scheme, target, query, cursor, options, netcall) {
1379     var newTarget = new OrdTarget(target),
1380         object = target.object;
1381     
1382     newTarget.slotPath = query;
1383     
1384     var slotPath = newTarget.slotPath, // SlotPath
1385         space = null,     // Space
1386         container = null; // base container
1387         
1388     netcall = bajaDef(netcall, true);
1389     
1390     // TODO: May need to make more robust if other types of Slot Paths are added
1391     var isVirtual = slotPath.constructor !== baja.SlotPath;
1392     
1393     // TODO: Property Container support
1394     
1395     if (object.getType().is("baja:VirtualGateway") && isVirtual) {
1396       space = object.getVirtualSpace();
1397       container = space.getRootComponent();
1398     }
1399     else if (object.getType().is("baja:ComponentSpace")) {
1400       space = object;
1401       container = object.getRootComponent();
1402     }    
1403     else if (object.getType().isComponent()) {
1404       space = object.getComponentSpace();
1405       if (slotPath.isAbsolute()) {
1406         container = space.getRootComponent();
1407       }
1408       else {
1409         container = object; 
1410       }
1411     }
1412     else {
1413       throw new Error("Slot Scheme Unsupported ORD base: " + object.getType());
1414     }
1415     
1416     // Hack for Virtual Space
1417     if (isVirtual && slotPath.getBody() === "") {
1418       newTarget.object = space;
1419       cursor.resolveNext(newTarget, options);
1420       return;
1421     }
1422     
1423     // Avoid a network call if the Component Space doesn't support callbacks
1424     if (netcall) {
1425       if (space === null) {
1426         netcall = false;
1427       }
1428       else {
1429         netcall = space.hasCallbacks();
1430       }
1431     }
1432     
1433     var value = container,
1434         nameAtDepth,   
1435         slot = null, 
1436         propertyPath = null, 
1437         depthRequestIndex = -1,
1438         backupDepth = slotPath.getBackupDepth(),
1439         k, i, j;
1440     
1441     // first walk up using backup 
1442     for (k = 0; k < backupDepth; ++k) {
1443       container = container.getType().isComponent() ? container.getParent() : null;
1444       value = container;
1445       
1446       if (value === null) { 
1447         throw new Error("Cannot walk backup depth: " + object);
1448       }
1449     }
1450                   
1451     // Walk the SlotPath
1452     for (i = 0; i < slotPath.depth(); ++i) {
1453       nameAtDepth = slotPath.nameAt(i);    
1454       
1455       if (isVirtual) {
1456         nameAtDepth = baja.SlotPath.escape(nameAtDepth);
1457       }
1458             
1459       if (value.getType().isComplex()) {          
1460         slot = value.getSlot(nameAtDepth);
1461       }
1462       else {
1463         throw new Error("Unable to resolve ORD: " + slotPath);
1464       }
1465                  
1466       if (slot === null) {
1467         if (netcall) {    
1468           depthRequestIndex = i;         
1469           break;
1470         }
1471       
1472         throw new Error("Unresolved ORD - unable to resolve SlotPath");
1473       }
1474       
1475       if (!slot.isProperty()) {
1476         if (i !== slotPath.depth() - 1) { // Actions and Topics must be at the final depth
1477           throw new Error("Unresolved ORD - Actions/Topics must be at final depth: " + slotPath); 
1478         }
1479         
1480         newTarget.container = container;
1481         newTarget.slot = slot;
1482         
1483         // Resolve the next part of the ORD and feed this target into it
1484         cursor.resolveNext(newTarget, options);
1485         return;
1486       }
1487       
1488       value = value.get(slot);
1489       
1490       // If we have a Component without a Handle then it's probably a value from a frozen Property
1491       // that's completely unloaded. In this case, we need to perform a load for this Component
1492       if (value.getType().isComponent() && value.$handle === null && space !== null) {
1493         if (netcall) {    
1494           depthRequestIndex = i;         
1495           break;
1496         }
1497       
1498         throw new Error("Unresolved ORD - unable to load handle for Component");
1499       }
1500       
1501       if (propertyPath === null && value.getType().is("baja:IPropertyContainer")) {
1502         container = value;
1503       }
1504       else {
1505         if (propertyPath === null) {
1506           propertyPath = [];
1507         }
1508         propertyPath.push(slot);
1509       }
1510     }
1511     
1512     // Make a network call to resolve the SlotPath
1513     var slotOrd,
1514         batch;
1515         
1516     if (netcall && depthRequestIndex > -1) {
1517       batch = new baja.comm.Batch();
1518        
1519       // Load ops on Slots that don't exist
1520       slotOrd = "slot:";
1521       if (slotPath.isAbsolute()) {
1522         slotOrd += "/";
1523       }
1524       
1525       var slotPathInfo = [];
1526       for (j = 0; j < slotPath.depth(); ++j) { 
1527         if (j >= depthRequestIndex) {
1528         
1529           slotPathInfo.push({
1530             o: slotOrd,
1531             sn: isVirtual ? baja.SlotPath.escape(slotPath.nameAt(j)) : slotPath.nameAt(j)
1532           });
1533         }
1534         
1535         if (j > 0) {
1536           slotOrd += "/";
1537         }        
1538         slotOrd += isVirtual ? baja.SlotPath.escape(slotPath.nameAt(j)) : slotPath.nameAt(j);
1539       }
1540                  
1541       var newOk = function () {
1542         // Now the network calls have all been committed, resolve this
1543         // Slot path without making any network calls
1544         scheme.resolve(target, query, cursor, options, /*network call*/false);
1545       };
1546       
1547       var newFail = function (err) {
1548         options.callback.fail(err);
1549       };
1550       
1551       // Attempt to load the missing Slot Path information
1552       space.getCallbacks().loadSlotPath(slotPathInfo,
1553                                         container,      
1554                                         new Callback(newOk, newFail, batch));
1555       
1556       batch.commit();
1557       return;
1558     }
1559     else if (slot === null && slotPath.depth() > 0) {
1560       throw new Error("Unable to resolve ORD: " + slotPath);
1561     }
1562     
1563     if (propertyPath === null) {
1564       newTarget.object = container;
1565     }
1566     else {
1567       // If there was a Property Path then use the first Property in the Property Path as the Slot
1568       slot = propertyPath[0];    
1569       newTarget.container = container; 
1570       newTarget.object = value;
1571       newTarget.slot = slot; 
1572       newTarget.propertyPath = propertyPath;
1573     }
1574     
1575     // Resolve the next part of the ORD and feed this target into it
1576     cursor.resolveNext(newTarget, options);
1577   }
1578   
1579   function slotSchemeResolveFull(scheme, target, query, cursor, options, netcall) {
1580     
1581     var newTarget = new OrdTarget(target),
1582         object = target.object;
1583     
1584     newTarget.slotPath = query;
1585     
1586     var slotPath = newTarget.slotPath, // SlotPath
1587         space = null,     // Space
1588         container = null, // base container
1589         isVirtual = slotPath.constructor !== baja.SlotPath;
1590         
1591     netcall = bajaDef(netcall, true);
1592         
1593     // TODO: Property Container support
1594     
1595     if (object.getType().is("baja:VirtualGateway") && isVirtual) {
1596       space = object.getVirtualSpace();
1597       container = space.getRootComponent();
1598     }
1599     else if (object.getType().is("baja:ComponentSpace")) {
1600       space = object;
1601       container = object.getRootComponent();
1602     }    
1603     else if (object.getType().isComponent()) {
1604       space = object.getComponentSpace();
1605       if (slotPath.isAbsolute()) {
1606         container = space.getRootComponent();
1607       }
1608       else {
1609         container = object; 
1610       }
1611     }
1612     else {
1613       throw new Error("Slot Scheme Unsupported ORD base: " + object.getType());
1614     }
1615     
1616     // Hack for Virtual Space
1617     if (isVirtual && slotPath.getBody() === "") {
1618       newTarget.object = space;
1619       cursor.resolveNext(newTarget, options);
1620       return;
1621     }
1622     
1623     // Avoid a network call if the Component Space doesn't support callbacks
1624     if (netcall) {
1625       if (space === null) {
1626         netcall = false;
1627       }
1628       else {
1629         netcall = space.hasCallbacks();
1630       }
1631     }
1632     
1633     var value = container, 
1634         nameAtDepth,    
1635         slot = null, 
1636         propertyPath = null,
1637         batch = new baja.comm.Batch(),
1638         depthRequestIndex = 0,
1639         backupDepth = slotPath.getBackupDepth(),
1640         k, i, j;
1641     
1642     // first walk up using backup 
1643     for (k = 0; k < backupDepth; ++k) {
1644       container = container.getType().isComponent() ? container.getParent() : null;
1645       value = container;
1646       
1647       if (value === null) { 
1648         throw new Error("Cannot walk backup depth: " + object);
1649       }
1650     }
1651             
1652     // Attempt to load slots on the container if needed
1653     if (container.getType().isComplex()) {
1654       container.loadSlots({
1655         "ok": baja.ok, 
1656         "fail": emptyFail, 
1657         "batch": batch
1658       });
1659     }
1660         
1661     if (batch.isEmpty()) {
1662       // Walk the SlotPath
1663       for (i = 0; i < slotPath.depth(); ++i) {
1664         nameAtDepth = slotPath.nameAt(i); 
1665 
1666         if (isVirtual) {
1667           nameAtDepth = baja.SlotPath.escape(nameAtDepth);
1668         }        
1669               
1670         if (value.getType().isComplex()) {
1671           value.loadSlots({
1672             "ok": baja.ok, 
1673             "fail": emptyFail, 
1674             "batch": batch
1675           });
1676           
1677           slot = value.getSlot(nameAtDepth);
1678         }
1679         else {
1680           throw new Error("Unable to resolve ORD: " + slotPath);
1681         }
1682              
1683         if (netcall && !batch.isEmpty()) {    
1684           depthRequestIndex = i;         
1685           break;
1686         }
1687         
1688         if (slot === null) {
1689           throw new Error("Unresolved ORD - unable to resolve SlotPath");
1690         }
1691         
1692         if (!slot.isProperty()) {
1693           if (i !== slotPath.depth() - 1) { // Actions and Topics must be at the final depth
1694             throw new Error("Unresolved ORD - Actions/Topics must be at final depth: " + slotPath); 
1695           }
1696           
1697           newTarget.container = container;
1698           newTarget.slot = slot;
1699           
1700           // Resolve the next part of the ORD and feed this target into it
1701           cursor.resolveNext(newTarget, options);
1702           return;
1703         }
1704         
1705         value = value.get(slot);
1706         
1707         // If we have a Component without a Handle then it's probably a value from a frozen Property
1708         // that's completely unloaded. In this case, we need to perform a load for this Component
1709         if (value.getType().isComponent() && value.$handle === null && space !== null) {
1710           if (netcall) {    
1711             depthRequestIndex = i;         
1712             break;
1713           }
1714       
1715           throw new Error("Unresolved ORD - unable to load handle for Component");
1716         }
1717         
1718         if (propertyPath === null && value.getType().is("baja:IPropertyContainer")) {
1719           container = value;
1720         }
1721         else {
1722           if (propertyPath === null) {
1723             propertyPath = [];
1724           }
1725           propertyPath.push(slot);
1726         }
1727       }
1728     }
1729 
1730     // Make a network call to resolve the SlotPath
1731     var slotOrd;
1732     if (!batch.isEmpty() && netcall) {
1733         
1734       // Load ops on Slots that don't exist
1735       slotOrd = "slot:";
1736       if (slotPath.isAbsolute()) {
1737         slotOrd += "/";
1738       }   
1739       for (j = 0; j < slotPath.depth(); ++j) {        
1740         if (j > 0) {
1741           slotOrd += "/";
1742         }        
1743         slotOrd += isVirtual ? baja.SlotPath.escape(slotPath.nameAt(j)) : slotPath.nameAt(j);
1744         
1745         if (j >= depthRequestIndex) {
1746           space.getCallbacks().loadSlots(slotOrd, 0, new Callback(baja.ok, emptyFail, batch));
1747         }
1748       }
1749       
1750       var newOk = function () {
1751         // Now the network calls have all been committed, resolve this
1752         // Slot path without making any network calls
1753         scheme.resolve(target, query, cursor, options, false);
1754       };
1755       
1756       var newFail = function (err) {
1757         options.callback.fail(err);
1758       };
1759             
1760       // Finally perform a poll so all of the sync ops can be processed from all the subsequent load ops
1761       baja.comm.poll(new Callback(newOk, newFail, batch));
1762       
1763       batch.commit();
1764       return;
1765     }
1766     else if (slot === null && slotPath.depth() > 0) {
1767       throw new Error("Unable to resolve ORD: " + slotPath);
1768     }
1769     
1770     if (propertyPath === null) {
1771       newTarget.object = container;
1772     }
1773     else {
1774       // If there was a Property Path then use the first Property in the Property Path as the Slot
1775       slot = propertyPath[0];    
1776       newTarget.container = container; 
1777       newTarget.object = value;
1778       newTarget.slot = slot; 
1779       newTarget.propertyPath = propertyPath;
1780     }
1781     
1782     // Resolve the next part of the ORD and feed this target into it
1783     cursor.resolveNext(newTarget, options);
1784   } 
1785      
1786   /**
1787    * Called when an ORD is resolved.
1788    *
1789    * @private
1790    *
1791    * @see baja.OrdScheme#resolve
1792    *
1793    * @param {ORDTarget} target  the current ORD Target.
1794    * @param {Object} query  the ORD Query used in resolving the ORD.
1795    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD.
1796    * @param {Object} options  options used for resolving an ORD.
1797    * @param {Boolean} [netcall] optional boolean to specify whether a network call should be attempted (used internally).
1798    */
1799   baja.SlotScheme.prototype.resolve = function (target, query, cursor, options, netcall) {
1800     if (options.full) {
1801       slotSchemeResolveFull(this, target, query, cursor, options, netcall);
1802     }
1803     else {
1804       slotSchemeResolve(this, target, query, cursor, options, netcall);
1805     }
1806   };
1807     
1808   /**
1809    * Return an ORD Query for the scheme.
1810    *
1811    * @returns {Object}
1812    */
1813   baja.SlotScheme.prototype.parse = function (schemeName, body) {
1814     return new baja.SlotPath(body);
1815   };
1816 
1817   function parseSlotPathBackup(slotPath) {
1818     var body = slotPath.getBody(),
1819         len = body.length,
1820         c0, c1, c2, i;
1821     
1822     for (i = 0; i < len; i += 3) {
1823       c0 = body.charAt(i);
1824       c1 = (i + 1 < len) ? body.charAt(i + 1) : -1;
1825       c2 = (i + 2 < len) ? body.charAt(i + 2) : "/";
1826       
1827       if (c0 !== ".") {
1828         return i;
1829       }
1830       
1831       if (c1 !== "." || c2 !== "/") {
1832         // Since we know c0 is a period ('.'), we can check to see
1833         // if that is a valid path name.  For SlotPath's, it
1834         // should always return false, so the SyntaxException
1835         // will be thrown.  But for subclasses (such as VirtualPath),
1836         // this may be a legal path name, so we don't want to throw
1837         // the Syntax Exception.
1838         if (slotPath.isValidPathName(c0)) {
1839           return i;
1840         }
1841           
1842         throw new Error("Expecting ../ backup");
1843       }
1844       
1845       slotPath.$backupDepth++;
1846     }
1847     
1848     return len; 
1849   }
1850   
1851   function parseSlotPathNames(slotPath, start) {    
1852     var body = slotPath.getBody(),
1853         len = body.length,
1854         n = 0, // Array index
1855         c, // Character
1856         nm,  // Name
1857         i;
1858     
1859     if (start >= len) {
1860       return;
1861     }
1862     
1863     if (body.charAt(len - 1) === "/") {
1864       throw new Error("Invalid Slot Path - Trailing Slash");
1865     }
1866     
1867     for (i = start; i < len; ++i) {
1868       c = body.charAt(i);
1869       
1870       if (c === "/") {
1871         if (i === start) {
1872           throw new Error("Invalid Slot Path - Double Slashes");
1873         }
1874         nm = body.substring(start, i);
1875         
1876         if (!slotPath.isValidPathName(nm)) {
1877           throw new Error("Invalid name in path");
1878         }
1879         
1880         slotPath.$names.push(nm);
1881         start = i + 1;
1882       }
1883     }
1884   
1885     nm = body.substring(start, len);
1886     
1887     if (!slotPath.isValidPathName(nm)) {
1888       throw new Error("Invalid name in path");
1889     }
1890   
1891     slotPath.$names.push(nm);
1892   }
1893      
1894   function parseSlotPath(slotPath) {
1895     slotPath.$names = [];
1896     slotPath.$abs = false;
1897     
1898     if (slotPath.getBody().length === 0) {
1899       return;
1900     }
1901     
1902     var s = 0, // Start
1903         c = slotPath.getBody().charAt(0);
1904         
1905     if (c === "/") {
1906       slotPath.$abs = true;
1907       s = 1;
1908     }
1909     else if (c === ".") {
1910       s = parseSlotPathBackup(slotPath);
1911     }
1912         
1913     parseSlotPathNames(slotPath, s);
1914   }
1915         
1916   /**
1917    * @class SlotPath.
1918    * <p>
1919    * SlotPath is used for resolving BValues using slot names.
1920    *
1921    * @name baja.SlotPath
1922    * @extends OrdQuery
1923    *
1924    * @param {String} body  the body of the ORD scheme
1925    */  
1926   baja.SlotPath = function (body) {
1927     baja.SlotPath.$super.call(this, {
1928       scheme: baja.SlotScheme.DEFAULT,
1929       schemeName: "slot",
1930       body: strictArg(body, String)
1931     });
1932     this.$abs = false;
1933     this.$names = [];
1934     this.$backupDepth = 0;
1935     parseSlotPath(this);
1936   }.$extend(OrdQuery);
1937   
1938   /**
1939    * Make a Slot Path.
1940    *
1941    * @private
1942    *
1943    * @param {Object} body  the body.
1944    * @returns {baja.SlotPath} the new Slot Path.
1945    */
1946   baja.SlotPath.prototype.makeSlotPath = function (body) {
1947     return new baja.SlotPath(body);
1948   };
1949       
1950   /**
1951    * Return the SlotPath depth.
1952    *
1953    * @returns the SlotPath depth.
1954    */ 
1955   baja.SlotPath.prototype.depth = function () {
1956     return this.$names.length;
1957   };
1958   
1959   /**
1960    * Return the SlotPath backup depth.
1961    *
1962    * @returns the SlotPath depth.
1963    */ 
1964   baja.SlotPath.prototype.getBackupDepth = function () {
1965     return this.$backupDepth;
1966   };
1967   
1968   /**
1969    * Return the name at the given depth.
1970    *
1971    * @param {Number} depth  the specified depth for the name.
1972    *
1973    * @returns {String} the name at the specified depth.
1974    */ 
1975   baja.SlotPath.prototype.nameAt = function (depth) {
1976     baja.strictArg(depth, Number);
1977     return this.$names[depth];
1978   };
1979   
1980   /**
1981    * Return true if the SlotPath is absolute.
1982    *
1983    * @returns {Boolean} true if the SlotPath is absolute.
1984    */ 
1985   baja.SlotPath.prototype.isAbsolute = function () {
1986     return this.$abs;
1987   };
1988             
1989   /**
1990    * Return whether the specified path name is valid.
1991    *
1992    * @param {String} pathName the path name to validate.
1993    *
1994    * @returns {Boolean} true if the slot name is valid.
1995    */ 
1996   baja.SlotPath.prototype.isValidPathName = function (pathName) {
1997     return baja.SlotPath.isValidName(pathName);
1998   };
1999   
2000   /**
2001    * Return whether the slot name is valid
2002    *
2003    * @param {String} nm the name to be validated.
2004    *
2005    * @returns {Boolean} true if the slot name is valid.
2006    */ 
2007   baja.SlotPath.isValidName = function (nm) {
2008     return (/^([a-zA-Z$]([a-zA-Z0-9_]|(\$([0-9a-fA-F]{2}))|(\$u([0-9a-fA-F]{4})))*)$/).test(nm); 
2009   };
2010   
2011   /**
2012    * Verify whether the slot name is valid.
2013    *
2014    * @param {String} nm the name to be validated.
2015    *
2016    * @throws error if the slot name isn't valid.
2017    */ 
2018   baja.SlotPath.verifyValidName = function (nm) {
2019     if (!baja.SlotPath.isValidName(nm)) {
2020       throw new Error("Illegal name for Slot: " + nm);
2021     }
2022   };
2023   
2024   // Converts a character
2025   function convertSlotPathChar(c) {
2026     var code = c.charCodeAt(0),
2027         hex = code.toString(16),
2028         buf = "$";
2029     
2030     if (code < 0x10) {
2031       buf += "0" + hex;
2032     }
2033     else if (code < 0x100) {
2034       buf += hex;
2035     }
2036     else if (code < 0x1000) {
2037       buf += "u0" + hex;
2038     }
2039     else {
2040       buf += "u" + hex;
2041     }
2042     return buf;
2043   }
2044   
2045   /**
2046    * Escape the string so it becomes a valid name for a slot.
2047    *
2048    * @see baja.SlotPath.unescape
2049    *
2050    * @param {String} str the string to be escaped.
2051    *
2052    * @returns {String} the escaped String.
2053    */ 
2054   baja.SlotPath.escape = function (str) {    
2055     if (str.length === 0) {
2056       return str;
2057     }
2058         
2059     // Convert first character
2060     var res = str.charAt(0).replace(/[^a-zA-Z]/, function (c) {
2061       return convertSlotPathChar(c);
2062     });
2063     
2064     if (str.length > 1) {   
2065       // Convert everything after first character
2066       res += str.substring(1, str.length).replace(/[^a-zA-Z0-9_]/g, function (c) {
2067         return convertSlotPathChar(c);
2068       });
2069     }
2070     
2071     return res;
2072   };
2073   
2074   /**
2075    * Unescape the string so all escaped characters become readable.
2076    *
2077    * @see baja.SlotPath.escape
2078    *
2079    * @param {String} str the string to be unescaped.
2080    *
2081    * @returns {String} the unescaped String.
2082    */ 
2083   baja.SlotPath.unescape = function (str) {
2084     if (str.length === 0) {
2085       return str;
2086     }
2087     
2088     // Convert from $xx    
2089     str = str.replace(/\$[0-9a-fA-F]{2}/g, function (s) {
2090       return String.fromCharCode(parseInt(s.substring(1, s.length), 16));
2091     });
2092     
2093     // Convert from $uxxxx
2094     str = str.replace(/\$u[0-9a-fA-F]{4}/g, function (s) {
2095       return String.fromCharCode(parseInt(s.substring(2, s.length), 16));
2096     });
2097     
2098     return str;
2099   };
2100   
2101   /**
2102    * Merge this path with the specified path.
2103    *
2104    * @param {baja.SlotPath} a
2105    * @returns {String} the body of the SlotPath.
2106    */ 
2107   baja.SlotPath.prototype.merge = function (a) {
2108     // if absolute then return a
2109     if (a.isAbsolute()) {
2110       return a.getBody();
2111     }
2112 
2113     // otherwise we have no backup or a backup 
2114     // contained within my path
2115     var s = "";
2116     if (this.isAbsolute()) { 
2117       s += "/";
2118     }
2119 
2120     // if the backup is past me
2121     if (a.getBackupDepth() > 0 && a.getBackupDepth() > this.depth()) {
2122       // can't handle backup past absolute root
2123       if (this.isAbsolute()) {
2124         throw new Error("Invalid merge " + this + " + " + a); 
2125       }        
2126       
2127       var backups = a.getBackupDepth() - this.depth() + this.getBackupDepth(), 
2128           i;
2129       for (i = 0; i < backups; ++i) {
2130         s += "../";
2131       }
2132     }
2133     
2134     // add my path minus backup
2135     var needSlash = false,
2136         x;
2137     for (x = 0; x < this.depth() - a.getBackupDepth(); ++x)
2138     {
2139       if (needSlash) {
2140         s += "/";
2141       }
2142       else {
2143         needSlash = true;
2144       }
2145       s += this.nameAt(x);
2146     }
2147       
2148     // now add relative path
2149     var j;
2150     for (j = 0; j < a.depth(); ++j) {
2151       if (needSlash) {
2152         s += '/'; 
2153       }
2154       else {
2155         needSlash = true;
2156       }
2157       s += a.nameAt(j);
2158     }
2159       
2160     return s;
2161   };
2162   
2163   /**
2164    * Normalize the ORD Query list.
2165    *
2166    * @private
2167    *
2168    * @param {baja.OrdQueryList} list  the ORD Query List.
2169    * @param {Number} index  the ORD Query List index.
2170    * @param {Boolean} return true if the list was modified.
2171    */
2172   baja.SlotPath.prototype.normalize = function (list, index) {
2173     var current = list.get(index),
2174         next = list.get(index + 1),
2175         modified = false;
2176     
2177     // Merge two Slot paths together
2178     if (next && next.getSchemeName() === current.getSchemeName()) {
2179     
2180       // Merge the slot paths together
2181       var currentSlotPath = this.makeSlotPath(current.getBody()),
2182           newSlotPath = this.makeSlotPath(currentSlotPath.merge(this.makeSlotPath(next.getBody())));
2183       
2184       // Update the OrdQueryList
2185       list.set(index, newSlotPath);
2186       
2187       // Remove the next item from the list
2188       list.remove(index + 1);
2189       
2190       modified = true;
2191     }
2192     
2193     return modified;
2194   };
2195       
2196   ////////////////////////////////////////////////////////////////
2197   // Handle Scheme
2198   ////////////////////////////////////////////////////////////////   
2199   
2200   /**
2201    * @class Handle ORD Scheme.
2202    * <p>
2203    * This scheme resolves a SlotPath to a handle. Each Component in a ComponentSpace has a 
2204    * unique handle. This is a great way to keep track of a Component regardless of whether
2205    * its SlotPath changes.
2206    *
2207    * @name baja.HandleScheme
2208    * @extends baja.OrdScheme
2209    * @private
2210    */  
2211   baja.HandleScheme = function () {
2212     baja.HandleScheme.$super.apply(this, arguments);
2213   }.$extend(baja.OrdScheme);
2214   
2215   /**
2216    * Default Handle ORD Scheme instance
2217    * @private
2218    */
2219   baja.HandleScheme.DEFAULT = new baja.HandleScheme();
2220   
2221   // Register Type
2222   baja.HandleScheme.registerType("baja:HandleScheme"); 
2223      
2224   /**
2225    * Called when an ORD is resolved.
2226    *
2227    * @private
2228    *
2229    * @see baja.OrdScheme#resolve
2230    *
2231    * @param {ORDTarget} target  the current ORD Target.
2232    * @param {Object} query  the ORD Query used in resolving the ORD.
2233    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD.
2234    * @param {Object} options  options used for resolving an ORD.
2235    */ 
2236   baja.HandleScheme.prototype.resolve = function (target, query, cursor, options) {
2237     
2238     var object = target.object,
2239         handle = query.getBody(),
2240         space;
2241         
2242     if (!baja.hasType(object)) {
2243       throw new Error("Not based via ComponentSpace. Invalid Object");
2244     }
2245     else if (object.getType().isComponent()) {
2246       space = object.getComponentSpace();
2247     }
2248     else if (object.getType().is("baja:ComponentSpace")) {
2249       space = object;
2250     }
2251     
2252     // Pick up whether the Space is null
2253     if (!baja.hasType(space)) {
2254       throw new Error("Not based via ComponentSpace");
2255     }
2256     
2257     var ok = function (comp) {
2258       var newTarget = new OrdTarget(target);
2259       newTarget.object = comp;
2260       cursor.resolveNext(newTarget, options);
2261     };
2262     
2263     var fail = function (err) {
2264       options.callback.fail(err);
2265     };
2266     
2267     // Resolve the handle in the Space (may make network calls if necessary)
2268     space.resolveByHandle({
2269       "handle": handle, 
2270       "ok": ok, 
2271       "fail": fail
2272     });
2273   };
2274   
2275   ////////////////////////////////////////////////////////////////
2276   // Service Scheme
2277   //////////////////////////////////////////////////////////////// 
2278   
2279   /**
2280    * @class Service ORD Scheme.
2281    * <p>
2282    * This scheme is used to resolve a Server running in a Station.
2283    *
2284    * @name baja.ServiceScheme
2285    * @extends baja.OrdScheme
2286    * @private
2287    */    
2288   baja.ServiceScheme = function () {
2289     baja.ServiceScheme.$super.apply(this, arguments);
2290   }.$extend(baja.OrdScheme);
2291   
2292   /**
2293    * Default Service Scheme instance.
2294    * @private
2295    */
2296   baja.ServiceScheme.DEFAULT = new baja.ServiceScheme();
2297   
2298   // Register Type
2299   baja.ServiceScheme.registerType("baja:ServiceScheme"); 
2300   
2301   /**
2302    * Called when an ORD is resolved.
2303    *
2304    * @private
2305    *
2306    * @see baja.OrdScheme#resolve
2307    *
2308    * @param {ORDTarget} target  the current ORD Target.
2309    * @param {Object} query  the ORD Query used in resolving the ORD.
2310    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD.
2311    * @param {Object} options  options used for resolving an ORD.
2312    */ 
2313   baja.ServiceScheme.prototype.resolve = function (target, query, cursor, options) {
2314     
2315     var object = target.object,
2316         typeSpec = query.getBody(),
2317         space;
2318     
2319     if (!baja.hasType(object)) {
2320       throw new Error("Not based via ComponentSpace. Invalid Object");
2321     }
2322     else if (object.getType().isComponent()) {
2323       space = object.getComponentSpace();
2324     }
2325     else if (object.getType().is("baja:ComponentSpace")) {
2326       space = object;
2327     }
2328     else if (object.getType().is("baja:ISession") && object.station) {
2329       space = object.station;
2330     }
2331     
2332     // Pick up whether the Space is null
2333     if (!baja.hasType(space)) {
2334       throw new Error("Not based via ComponentSpace");
2335     }
2336     
2337     var ok = function (comp) {
2338       var newTarget = new OrdTarget(target);
2339       newTarget.object = comp;
2340       cursor.resolveNext(newTarget, options);
2341     };
2342     
2343     var fail = function (err) {
2344       options.callback.fail(err);
2345     };
2346     
2347     if (space.hasCallbacks()) {
2348       space.getCallbacks().getService(typeSpec, new Callback(ok, fail));
2349     }
2350     else {
2351       throw new Error("Unable to resolve Service: " + typeSpec);
2352     }
2353   };
2354   
2355   ////////////////////////////////////////////////////////////////
2356   // View Scheme
2357   //////////////////////////////////////////////////////////////// 
2358   
2359   /**
2360    * @class View ORD Scheme.
2361    * <p>
2362    * This scheme is used to process View query related information.
2363    *
2364    * @name baja.ViewScheme
2365    * @extends baja.OrdScheme
2366    * @private
2367    */    
2368   baja.ViewScheme = function () {
2369     baja.ViewScheme.$super.apply(this, arguments);
2370   }.$extend(baja.OrdScheme);
2371   
2372   /**
2373    * Default View Scheme instance.
2374    * @private
2375    */
2376   baja.ViewScheme.DEFAULT = new baja.ViewScheme();
2377   
2378   // Register Type
2379   baja.ViewScheme.registerType("baja:ViewScheme"); 
2380   
2381   /**
2382    * Called when an ORD is resolved.
2383    *
2384    * @private
2385    *
2386    * @see baja.OrdScheme#resolve
2387    *
2388    * @param {ORDTarget} target  the current ORD Target.
2389    * @param {Object} query  the ORD Query used in resolving the ORD.
2390    * @param cursor  the ORD Query List cursor used for helping to asynchronously resolve the ORD.
2391    * @param {Object} options  options used for resolving an ORD.
2392    */ 
2393   baja.ViewScheme.prototype.resolve = function (target, query, cursor, options) { 
2394     // Note down the view query information onto the ORD target so it can be accessed
2395     target.view = {
2396       id: query.getViewId(),
2397       params: query.getParameters()
2398     };
2399     
2400     cursor.resolveNext(target, options);
2401   };
2402   
2403   /**
2404    * Return an ORD Query for the scheme.
2405    *
2406    * @returns {OrdQuery}
2407    */
2408   baja.ViewScheme.prototype.parse = function (schemeName, body) {
2409     return new baja.ViewQuery(body);
2410   };
2411   
2412   function parseViewQuery(body) {
2413     // Parse the view query (viewId?params)
2414     var res;
2415     if (body) {
2416       res = /^([^?]*)(?:[\?](.*))?$/.exec(body);
2417     }
2418     
2419     if (!res || (res && res.length < 2)) {
2420       throw new Error("Invalid view query: " + body);
2421     }
2422     
2423     // Note down the view query information onto the ORD target so it can be accessed
2424     var view = {
2425       id: res[1] || "",
2426       params: {}
2427     };
2428     
2429     // If there are some view parameters then parse them
2430     if (res[2]) {
2431       var regex = /([^=]+)\=([^;]+)\;?/g,
2432           params = regex.exec(res[2]);
2433           
2434       while (params) { 
2435         view.params[decodeURIComponent(params[1])] = decodeURIComponent(params[2]);
2436         params = regex.exec(res[2]);
2437       }
2438     }
2439     
2440     return view;
2441   }
2442   
2443   /**
2444    * @class ViewQuery
2445    * <p>
2446    * ViewQuery defines user agent information.
2447    *
2448    * @name baja.ViewQuery
2449    * @extends OrdQuery
2450    *
2451    * @param {String|Object} body the view query body or an Object Literal for 
2452    *                             the view id and parameters.
2453    * @param {String} [body.id] view id.
2454    * @param {Object} [body.params] view parameters (key value pairs in an Object Literal).
2455    */  
2456   baja.ViewQuery = function (body) {
2457     // If an Object is passed in then attempt to get id and params from object
2458     if (body && typeof body === "object") {
2459       var params = body.params || {};
2460     
2461       this.$view = {
2462         id: body.id || "",
2463         params: params
2464       };
2465     
2466       // Build up a new view query body String
2467       var i = 0;
2468       body = this.$view.id;
2469           
2470       baja.iterate(params, function (prop, propName) {
2471         if (i === 0) {
2472           body += "?";
2473         }
2474         else if (i > 0) {
2475           body += ";";
2476         }
2477         body += encodeURIComponent(propName) + "=" + encodeURIComponent(prop);
2478         ++i;
2479       });
2480     }
2481     else {
2482       this.$view = parseViewQuery(body);
2483     }
2484   
2485     baja.ViewQuery.$super.call(this, {
2486       scheme: baja.ViewScheme.DEFAULT,
2487       schemeName: "view",
2488       body: strictArg(body, String)
2489     });
2490   }.$extend(OrdQuery);
2491   
2492   /**
2493    * Normalize the query and return true if modified.
2494    *
2495    * @private
2496    *
2497    * @param {OrdQueryList} list
2498    * @param {Number} index
2499    *
2500    * @returns {Boolean}
2501    */
2502   baja.ViewQuery.prototype.normalize = function (list, index) {
2503     var modified = false;     
2504     if (list.get(index + 1) && 
2505         list.get(index + 1).getSchemeName().equals("view")) {
2506       // If the next scheme is the same as this one then
2507       list.remove(index + 1);
2508       modified = true;
2509     }      
2510     else if (index < list.size() - 1) {
2511       // Ensure view query is always the last in the list
2512       list.remove(index);
2513       modified = true;
2514     }
2515     return modified;
2516   };
2517   
2518   /**
2519    * Return the view id (this could be view type spec or the name of a Px view).
2520    *
2521    * @returns {String}
2522    */
2523   baja.ViewQuery.prototype.getViewId = function () {
2524     return this.$view.id;
2525   };
2526     
2527   /**
2528    * Return the view parameters as an Object Literal.
2529    * <p>
2530    * Please note, this returns a defensive copy of the parameters.
2531    *
2532    * @returns {Object} the parameters.
2533    */
2534   baja.ViewQuery.prototype.getParameters = function () {
2535     var o = {};
2536     baja.iterate(this.$view.params, function (prop, propName) {
2537       o[propName] = prop;
2538     });
2539     return o;
2540   };
2541   
2542   ////////////////////////////////////////////////////////////////
2543   // Batch Resolve
2544   //////////////////////////////////////////////////////////////// 
2545   
2546   // Enclose BatchResolve in its own anonymous function as it uses quite a lot of inner private functions
2547   (function batchRes() {
2548   
2549     /**
2550      * @class BatchResolve is used to resolve a list of ORDs together.
2551      * <p>
2552      * This method should always be used if multiple ORDs need to be resolved at the same time.
2553      *
2554      * @name baja.BatchResolve
2555      * @extends BaseBajaObj
2556      *
2557      * @param {Array} ords an array of ORDs to resolve. The array can be Strings or baja.Ord.
2558      */      
2559     baja.BatchResolve = function (ords) {
2560       baja.BatchResolve.$super.apply(this, arguments);
2561       strictArg(ords, Array);        
2562       // Ensure ORDs are normalized
2563       var items = [], i;
2564       for (i = 0; i < ords.length; ++i) {
2565         items.push({
2566           ord: baja.Ord.make(ords[i].toString()).normalize(),
2567           target: null
2568         });
2569       }
2570       this.$items = items;
2571       this.$groups = [];
2572       this.$ok = baja.ok;
2573       this.$fail = baja.fail;
2574       this.$resolved = false;
2575     }.$extend(BaseBajaObj);
2576     
2577     /**
2578      * Return the number of items in the Batch.
2579      *
2580      * @returns {Number}
2581      */  
2582     baja.BatchResolve.prototype.size = function () {
2583       return this.$items.length;
2584     };
2585      
2586     /**
2587      * Return the ORD at the specified index or null if the index is invalid.
2588      *
2589      * @param {Number} index
2590      * @returns {baja.Ord}
2591      */      
2592     baja.BatchResolve.prototype.getOrd = function (index) {
2593       strictArg(index, Number);
2594       var t = this.$items[index];
2595       return t ? t.ord : null;
2596     };
2597     
2598     /**
2599      * Return true if the ORD at the specified index has successfully resolved.
2600      *
2601      * @param {Number} index
2602      * @returns {Boolean}
2603      */  
2604     baja.BatchResolve.prototype.isResolved = function (index) {
2605       strictArg(index, Number);
2606       var t = this.$items[index];
2607       return t && t.target ? true : false;
2608     };
2609     
2610     /**
2611      * Return the error for a particular ORD that failed to resolve or null for no error.
2612      *
2613      * @param {Number} index
2614      * @returns {Error} the error or null if no error.
2615      */
2616     baja.BatchResolve.prototype.getFail = function (index) {
2617       strictArg(index, Number);
2618       var t = this.$items[index];
2619       return t && t.fail ? t.fail : null;
2620     };
2621     
2622     /**
2623      * Return the ORD Target at the specified index.
2624      * <p>
2625      * If the ORD failed to resolve, an error will be thrown.
2626      *
2627      * @param {Number} index
2628      * @returns {OrdTarget} the ORD Target
2629      * @throws {Error} thrown if ORD failed to resolve
2630      */
2631     baja.BatchResolve.prototype.getTarget = function (index) {
2632       strictArg(index, Number);
2633       var t = this.$items[index];
2634       if (t && t.target) {
2635         return t.target;
2636       }
2637       else {
2638         throw t && t.fail ? t.fail : new Error("Unresolved ORD");
2639       }
2640     };
2641     
2642     /**
2643      * Return an array of resolved ORD Targets.
2644      * <p>
2645      * If any of the ORDs failed to resolve, an error will be thrown.
2646      *
2647      * @returns {Array} an array of ORD Targets
2648      * @throws {Error} thrown if any ORDs failed to resolve
2649      */
2650     baja.BatchResolve.prototype.getTargets = function () {
2651       var targets = [], i;
2652       for (i = 0; i < this.$items.length; ++i) {
2653         targets.push(this.getTarget(i));
2654       }
2655       return targets;
2656     };
2657     
2658     /**
2659      * Return an array of resolved objects.
2660      * <p>
2661      * If any of the ORDs failed to resolve, an error will be thrown.
2662      *
2663      * @returns {Array} an array of objects
2664      * @throws {Error} thrown if any ORDs failed to resolve
2665      */
2666     baja.BatchResolve.prototype.getTargetObjects = function () {
2667       var objects = [], i;
2668       for (i = 0; i < this.$items.length; ++i) {
2669         objects.push(this.get(i));
2670       }
2671       return objects;
2672     };
2673     
2674     /**
2675      * Return the resolved object at the specified index.
2676      * <p>
2677      * If the ORD failed to resolve, an error will be thrown.
2678      *
2679      * @param {Number} index
2680      * @returns the resolved object
2681      * @throws {Error} thrown if the ORD failed to resolve
2682      */
2683     baja.BatchResolve.prototype.get = function (index) {
2684       return this.getTarget(index).object;
2685     };
2686     
2687     /**
2688      * For each resolved target, call the specified function.
2689      * <p>
2690      * If any ORDs failed to resolve, an error will be thrown.
2691      * <p>
2692      * When the function is called, the 'this' will be the resolved Component's target.
2693      * The target's object will be passed as a parameter into the function.
2694      *
2695      * @param {Function} func
2696      * @throws {Error} thrown if any of the ORDs failed to resolve
2697      */
2698     baja.BatchResolve.prototype.each = function (func) {
2699       strictArg(func, Function);
2700       var target,
2701           result, 
2702           i,
2703           obj;
2704       
2705       for (i = 0; i < this.$items.length; ++i) {
2706         target = this.getTarget(i);
2707         try {
2708           obj = target.getComponent();
2709           if (!obj) {
2710             obj = target.object;
2711           }
2712           
2713           result = func.call(obj, target.object, i);
2714           if (result) {
2715             return result;
2716           }
2717         }
2718         catch (err) {
2719           baja.error(err);
2720         }
2721       }
2722     };
2723     
2724     function endResolve(batchResolve, obj) {    
2725       // Called at the very end once everything has resolved
2726       // If there are any unresolved ORDs then fail the callback
2727       var failedErr, i;
2728       for (i = 0; i < batchResolve.$items.length; ++i) {
2729         if (!batchResolve.$items[i].target) {
2730           failedErr = batchResolve.$items[i].fail || new Error("Unresolved ORD");
2731           break;            
2732         }          
2733       }
2734       
2735       if (failedErr) {
2736         batchResolve.$fail.call(batchResolve, failedErr);
2737       }
2738       else {
2739         batchResolve.$ok.call(batchResolve);
2740       }
2741     }
2742     
2743     function subscribeTargets(batchResolve, index, batch, obj) {      
2744       // If there's no lease and no subscriber then automatically commit
2745       if (!obj.subscriber && !obj.lease) {
2746         index = batchResolve.$groups.length;
2747       }
2748           
2749       if (index >= batchResolve.$groups.length) {
2750         // If we've resolved everything we can do then end the resolution
2751         batch.addCallback(function () {
2752           endResolve(batchResolve, obj);
2753         });
2754 
2755         batch.commit();  
2756         return;
2757       }
2758 
2759       var group = batchResolve.$groups[index],
2760           comps = [],
2761           c, i;
2762       
2763       // Try to find some components we can subscribe too
2764       for (i = 0; i < group.items.length; ++i) {
2765         if (group.items[i].target) {
2766           c = group.items[i].target.getComponent();
2767           
2768           // Only try to subscribe to components that are mounted and support subscription
2769           if (c && c.isMounted() && c.getComponentSpace().hasCallbacks()) {
2770             comps.push(c);
2771           }
2772         }
2773       }
2774       
2775       // If we can subscribe then batch up a subscription network call      
2776       if (comps.length > 0) {
2777         if (obj.subscriber) {
2778           // Subscribe using Subscriber
2779           obj.subscriber.subscribe({
2780             comps: comps,
2781             batch: batch
2782           });
2783         }
2784         
2785         // If leasing then lease the Components for a pre-defined period of time
2786         if (obj.lease) {
2787           baja.Component.lease({
2788             comps: comps,
2789             time: obj.leaseTime,
2790             batch: batch
2791           });
2792         }
2793       }
2794       
2795       // Try batch the next group
2796       subscribeTargets(batchResolve, ++index, batch, obj);
2797     }
2798     
2799     function resolveOrds(batchResolve, index, obj) {      
2800       // If we've resolved everything then try to subscribing to the components (if at all possible)
2801       if (index >= batchResolve.$items.length) {
2802         subscribeTargets(batchResolve, 0, new baja.comm.Batch(), obj);
2803         return;
2804       }
2805       
2806       // Resolve each ORD to its target (unless it's an unknown ORD whereby we've 
2807       // already tried to resolve it earlier)
2808       var item = batchResolve.$items[index];
2809       if (!item.unknown) {
2810         item.ord.resolve({
2811           ok: function (target) {
2812             item.target = target;
2813             resolveOrds(batchResolve, ++index, obj);
2814           },
2815           fail: function (err) {
2816             item.fail = err;
2817             resolveOrds(batchResolve, ++index, obj);
2818           },
2819           base: obj.base
2820         });
2821       }
2822       else {
2823         resolveOrds(batchResolve, ++index, obj);
2824       }
2825     }
2826     
2827     function resolveSlotPaths(batchResolve, index, batch, obj) { 
2828           
2829       if (index >= batchResolve.$groups.length) {
2830         batch.addCallback(function () {
2831           // If we've resolved the SlotPaths from each group then we can finally attempt 
2832           // to resolve each ORD. With a bit of luck, this will result in minimal network calls
2833           resolveOrds(batchResolve, 0, obj);
2834         });
2835         
2836         batch.commit();
2837         return;
2838       }
2839     
2840       var group = batchResolve.$groups[index], path, comp, isVirtual, nameAtDepth, arg, fullPath, slotOrd, slot, i, x, j,
2841           slotPathInfo = [],
2842           slotPathInfoMap = {},
2843           depthRequestIndex = -1,
2844           subscribeOrds = obj.subscriber ? [] : null;          
2845 
2846       for (i = 0; i < group.items.length; ++i) {
2847         comp = group.space.getRootComponent(); 
2848         path = group.items[i].slot;
2849         
2850         // Skip if no valid SlotPath is available
2851         if (!path) {
2852           continue;
2853         }
2854         
2855         // Record ORD for possible subscription
2856         if (subscribeOrds) {
2857           subscribeOrds.push(path.toString());
2858         }
2859         
2860         isVirtual = path instanceof baja.VirtualPath;
2861         
2862         // Find out what exists and doesn't exist
2863         for (x = 0; x < path.depth(); ++x) {
2864           nameAtDepth = path.nameAt(x);
2865         
2866           if (isVirtual) {
2867             nameAtDepth = baja.SlotPath.escape(nameAtDepth);
2868           }
2869           
2870           slot = comp.getSlot(nameAtDepth);
2871           
2872           // If there's no slot present then we need to try and make a network call for it.
2873           if (slot === null) {
2874             depthRequestIndex = x;
2875             break;
2876           }
2877           
2878           // If the Slot isn't a Property then bail
2879           if (!slot.isProperty()) {
2880             break;
2881           }
2882           // If the Property isn't a Component then bail since we're only interested
2883           // in really loading up to a Component
2884           if (!slot.getType().isComponent()) {
2885             break;
2886           }
2887           comp = comp.get(slot);
2888         }
2889               
2890         // If we've got Slots to request then do so
2891         if (depthRequestIndex > -1) {
2892            
2893           // Load ops on Slots that don't exist
2894           slotOrd = "slot:/";
2895           
2896           for (j = 0; j < path.depth(); ++j) {
2897             // If we've gone past the depth we need to request then build up the network
2898             // calls we need to make            
2899             if (j >= depthRequestIndex) {
2900               arg = {
2901                 o: slotOrd,
2902                 sn: isVirtual ? baja.SlotPath.escape(path.nameAt(j)) : path.nameAt(j)
2903               };
2904               
2905               fullPath = arg.o + "/" + arg.sn;
2906               
2907               // Only request the Slot Path if it already isn't going to be requested
2908               if (!slotPathInfoMap.hasOwnProperty(fullPath)) {
2909                 slotPathInfoMap[fullPath] = arg;
2910                 slotPathInfo.push(arg);
2911               }
2912             }
2913             
2914             if (j > 0) {
2915               slotOrd += "/";
2916             }        
2917             slotOrd += isVirtual ? baja.SlotPath.escape(path.nameAt(j)) : path.nameAt(j);
2918           }
2919         }
2920       }
2921       
2922       // Make network request if there are slot paths to load for this Space
2923       if (slotPathInfo.length > 0) {
2924         group.space.getCallbacks().loadSlotPath(slotPathInfo, 
2925                                                 group.space, 
2926                                                 new Callback(baja.ok, baja.fail, batch), 
2927                                                 /*importAsync*/false);
2928         
2929         // Attempt to roll the network subscription call into
2930         // the Slot Path resolution to avoid another network call...
2931         if (subscribeOrds) {
2932           // Subscribe using Subscriber for the given Space
2933           
2934           // TODO: subscribing in this way is not ideal. Here we're subscribing Components before they're 
2935           // fully loaded into the Proxy Component Space to avoid another network call. This assumes that
2936           // each of these Components will fully load into the Component Space without any problems. This will
2937           // do for now since it's critical to customer's perceptions that BajaScript loads values quickly.
2938           // If any errors do occur (i.e. the values haven't loaded properly), they are flagged up using baja.error.
2939           obj.subscriber.$ordSubscribe({
2940             ords: subscribeOrds,
2941             space: group.space,
2942             batch: batch
2943           });
2944         }
2945       }
2946       
2947       // Resolve next group
2948       resolveSlotPaths(batchResolve, ++index, batch, obj);
2949     }
2950         
2951     function resolveHandlesToSlotPaths(batchResolve, index, batch, obj) {
2952       // If there are any handle ORDs then resolve them to their SlotPaths
2953       if (index >= batchResolve.$items.length) {
2954         batch.addCallback(function () {
2955           // If we resolved all of the handles to SlotPaths then resolve the SlotPaths
2956           // Piggy back the unknown ORD resolve off the Slot Path resolve
2957           resolveSlotPaths(batchResolve, 0, batchResolve.$unknownBatch || new baja.comm.Batch(), obj);
2958         });
2959         
2960         // Commit the batch for getting all of the handles as SlotPaths
2961         batch.commit();
2962         return;
2963       }
2964       
2965       var item = batchResolve.$items[index];
2966       
2967       // If we're got a handle to resolve then attempt make a network call to resolve the 
2968       if (item.h && item.space && item.space.hasCallbacks() && item.space.findByHandle(item.h) === null) {
2969         var cb = new Callback(function ok(slotPath) {
2970           item.slot = slotPath;
2971         },
2972         function fail(err) {
2973           item.fail = err;
2974         },
2975         batch);
2976         
2977         // Batch up this network request
2978         item.space.getCallbacks().handleToPath(item.h, cb);
2979       }
2980 
2981       // Resolve next handle
2982       resolveHandlesToSlotPaths(batchResolve, ++index, batch, obj);
2983     }
2984     
2985     function groupComponentSpaceItems(batchResolve, obj) {
2986       // Group Items together by Component Space
2987       var added, 
2988           item, 
2989           group, 
2990           i, 
2991           x;
2992       
2993       for (i = 0; i < batchResolve.$items.length; ++i) {  
2994         item = batchResolve.$items[i];
2995         added = false;   
2996 
2997         // Skip grouping for Spaces that don't have callbacks
2998         if (!item.space) {
2999           continue;
3000         }        
3001         if (!item.space.hasCallbacks()) {
3002           continue;
3003         }
3004       
3005         for (x = 0; x < batchResolve.$groups.length; ++x) {
3006           group = batchResolve.$groups[x];
3007          
3008           if (group.space.getNavOrd().toString().equals(item.spaceOrd.toString())) {
3009             group.items.push(item);
3010             added = true;
3011             break;      
3012           }        
3013         }
3014         
3015         // If the item isn't added then create a new group for this item
3016         if (!added) {
3017           batchResolve.$groups.push({
3018             space: item.space,
3019             items: [item]
3020           });     
3021         }      
3022       }
3023       
3024       // Resolve all of the Handles to SlotPaths
3025       resolveHandlesToSlotPaths(batchResolve, 0, new baja.comm.Batch(), obj);
3026     }
3027       
3028     function resolveComponentSpaces(batchResolve, index, obj) {
3029     
3030       // If we've resolved each space then group all of the items together by Component Space
3031       if (index >= batchResolve.$items.length) {
3032         groupComponentSpaceItems(batchResolve, obj);
3033         return;
3034       }
3035       
3036       if (!batchResolve.$items[index].spaceOrd) {
3037         // Skip to next item if the Component Space ORD can't be found
3038         resolveComponentSpaces(batchResolve, ++index, obj);
3039       }
3040       else {    
3041         // Recusively resolve each ORD to its Space        
3042         batchResolve.$items[index].spaceOrd.get({
3043           ok: function (value) {
3044             var s;
3045             if (value.getType().is("baja:ComponentSpace")) {
3046               s = batchResolve.$items[index].space = value;
3047               batchResolve.$items[index].spaceOrd = s.getNavOrd();
3048             }
3049             else if (this.getType().is("baja:VirtualGateway")) {
3050               // Note: this may result in some network calls to mount the Virtual Component Space
3051               s = batchResolve.$items[index].space = this.getVirtualSpace();
3052               batchResolve.$items[index].spaceOrd = s.getNavOrd();
3053             }
3054             else if (this.getType().is("baja:Component")) {
3055               s = batchResolve.$items[index].space = this.getComponentSpace();
3056               batchResolve.$items[index].spaceOrd = s.getNavOrd();
3057             }     
3058           
3059             resolveComponentSpaces(batchResolve, ++index, obj);
3060           },
3061           fail: function (err) {
3062             baja.error(err);
3063             
3064             resolveComponentSpaces(batchResolve, ++index, obj);
3065           },
3066           base: obj.base
3067         });  
3068       }
3069     }
3070     
3071     function makeAbsSlotPath(query, obj) {
3072       // Create an absolute SlotPath using the base if necessary
3073       var isVirtual = query.getSchemeName() === "virtual",
3074           path = isVirtual ? new baja.VirtualPath(query.getBody()) : new baja.SlotPath(query.getBody());
3075       
3076       // If the path is already absolute then use it
3077       if (path.isAbsolute()) {
3078         return path;
3079       }
3080       
3081       // Attempt to merge the ORD with the base to get our Absolute SlotPath
3082       if (obj.base.getType().isComponent() && !isVirtual) {
3083         var basePath = obj.base.getSlotPath();
3084         if (basePath !== null) {
3085           var newBody = basePath.merge(path);
3086           return isVirtual ? new baja.VirtualPath(newBody) : new baja.SlotPath(newBody);
3087         }
3088       }
3089       
3090       return null;
3091     }
3092                     
3093     function resolveUnknown(item, obj, batch) {
3094       // Make the network call to resolve the complete ORD Server side       
3095       var cb = new Callback(function ok(target) {
3096         item.target = target;
3097       },
3098       function fail(err) {
3099         item.fail = err;
3100       },
3101       batch); 
3102       
3103       // Batch up the unknown ORD resolution...
3104       item.ord.resolve({
3105         cb: cb,
3106         base: obj.base,
3107         fromBatchResolve: true
3108       });
3109     }
3110         
3111     function processOrds(batchResolve, obj) {
3112       // Find all of the Component Space ORDs
3113       var list,
3114           item, 
3115           cursor, 
3116           foundIndex, 
3117           q, 
3118           i;
3119            
3120       for (i = 0; i < batchResolve.$items.length; ++i) {
3121         item = batchResolve.$items[i];
3122         list = item.list = item.ord.parse();
3123         
3124         // If processed as unknown then skip since this ORD will be completely resolved Server Side
3125         if (list.hasUnknown()) {
3126           item.unknown = true;   
3127           
3128           // Create and cache the unknown batch object
3129           if (!batchResolve.$unknownBatch) {
3130             batchResolve.$unknownBatch = new baja.comm.Batch();
3131           }
3132           
3133           resolveUnknown(item, obj, batchResolve.$unknownBatch);
3134           continue;
3135         }
3136         
3137         cursor = list.getCursor();
3138         foundIndex = -1;
3139                 
3140         // Work out the ORD just before the virtual, slot or handle scheme
3141         while (cursor.next()) {
3142           q = cursor.get();
3143           
3144           if (q.getSchemeName() === "virtual") { 
3145             foundIndex = cursor.getIndex(); 
3146             item.slot = makeAbsSlotPath(q, obj); 
3147             break;
3148           }
3149           else if (q.getSchemeName() === "h" && foundIndex === -1) { 
3150             foundIndex = cursor.getIndex(); 
3151             item.h = q.getBody();
3152           }
3153           else if (q.getSchemeName() === "slot" && foundIndex === -1) {
3154             foundIndex = cursor.getIndex(); 
3155             item.slot = makeAbsSlotPath(q, obj); 
3156           }
3157         }
3158         
3159         // Note down the ORD to the Space
3160         if (foundIndex !== -1) {
3161           item.spaceOrd = baja.Ord.make(list.toString(foundIndex));
3162           
3163           // If there's no ORD then just try using the base to resolve the CS.
3164           if (item.spaceOrd === baja.Ord.DEFAULT) {
3165             item.spaceOrd = obj.base.getNavOrd();
3166           }
3167         }
3168       }
3169      
3170       // Resolve any Space ORDs found
3171       resolveComponentSpaces(batchResolve, 0, obj);
3172     }
3173     
3174     /**
3175      * Batch resolve an array of ORDs.
3176      * <p>
3177      * A Batch Resolve should be used whenever more than one ORD needs to resolved.
3178      * <p>
3179      * Any network calls that result from processing an ORD are always asynchronous.
3180      * <p>
3181      * This method can only be called once per BatchResolve instance.
3182      * <p>
3183      * An Object Literal is used to supply the method's arguments. For example...
3184      * <pre>
3185      *   var r = baja.BatchResolve(["station:|slot:/Ramp", "station:|slot:/SineWave"]);
3186      *   var sub = new baja.Subscriber(); // Also batch subscribe all resolved Components
3187      *   
3188      *   r.resolve({
3189      *     ok: function () {
3190      *       // Get resolved objects
3191      *       var objs = this.getTargetObjects();
3192      *     },
3193      *     fail: function (err) {
3194      *       // Called if any of the ORDs fail to resolve
3195      *     },
3196      *     subscriber: sub
3197      *   });
3198      *
3199      *   // Or use the each method (will only be called if all ORDs resolve). Each will
3200      *   // be called for each target.
3201      *   r.resolve({
3202      *     each: function () {
3203      *       baja.outln("Resolved: " + this.toPathString());
3204      *     },
3205      *     fail: function (err) {
3206      *       // Called if any of the ORDs fail to resolve
3207      *     },
3208      *     subscriber: sub
3209      *   });
3210      * </pre>
3211      *
3212      * @see baja.Ord
3213      *
3214      * @param {Object} [obj] the Object Literal that contains the method's arguments.
3215      * @param {Function} [obj.ok] the ok function called once all of the ORDs have been successfully resolved.
3216      *                            When the function is called, 'this' is set to the BatchResolve object.
3217      * @param {Function} [obj.fail] the fail function called if any of the ORDs fail to resolve. 
3218      *                              The first error found is pass as an argument to this function.
3219      * @param [obj.base] the base Object to resolve the ORDs against. 
3220      * @param {baja.Subscriber} [obj.subscriber] if defined, any mounted Components are subscribed using this Subscriber.
3221      * @param {Boolean} [obj.lease] if defined, any resolved and mounted components are leased.
3222      * @param {Number|baja.RelTime} [obj.leaseTime] the lease time used for leasing Components.
3223      */
3224     baja.BatchResolve.prototype.resolve = function (obj) {
3225       obj = objectify(obj);
3226       
3227       var ok = obj.ok || baja.ok;
3228       
3229       // If an each function was passed in then call if everything resolves ok.
3230       this.$ok = function () {
3231         var result;
3232         if (typeof obj.each === "function") {
3233           try {
3234             result = this.each(obj.each);
3235           }
3236           catch (err) {
3237             baja.error();
3238           }
3239         }
3240         ok.call(this, result);
3241       };
3242       
3243       this.$fail = obj.fail || baja.fail;
3244       
3245       // Can only resolve once
3246       if (this.$resolved) {
3247         this.$fail.call(this, "Cannot call resolve more than once");
3248         return;
3249       }
3250       this.$resolved = true;
3251       
3252       // Initialize
3253       obj.base = bajaDef(obj.base, baja.nav.localhost);
3254 
3255       // Check the user isn't trying to batch an ORD as this isn't supported
3256       if (obj.batch) {
3257         this.$fail.call(this, "Cannot batch ORD resolution");
3258         return;
3259       }
3260         
3261       // Start resolution 
3262       if (this.$items.length > 0) {      
3263         processOrds(this, obj);
3264       }
3265       else {
3266         this.$ok.call(this);
3267       }
3268     };
3269  
3270   }()); // batchRes
3271          
3272 }(baja, BaseBajaObj));