add ControllerSiteDeployment to xoslib
[plstackapi.git] / planetstack / core / xoslib / static / js / xoslib / xos-backbone.js
1 if (! window.XOSLIB_LOADED ) {
2     window.XOSLIB_LOADED=true;
3
4     SLIVER_API = "/plstackapi/slivers/";
5     SLICE_API = "/plstackapi/slices/";
6     SLICEROLE_API = "/plstackapi/slice_roles/";
7     NODE_API = "/plstackapi/nodes/";
8     SITE_API = "/plstackapi/sites/";
9     SITEDEPLOYMENT_API = "/plstackapi/sitedeployments/";
10     USER_API = "/plstackapi/users/";
11     USERDEPLOYMENT_API = "/plstackapi/user_deployments/";
12     DEPLOYMENT_API = "/plstackapi/deployments/";
13     IMAGE_API = "/plstackapi/images/";
14     IMAGEDEPLOYMENTS_API = "/plstackapi/imagedeployments/";
15     NETWORKTEMPLATE_API = "/plstackapi/networktemplates/";
16     NETWORK_API = "/plstackapi/networks/";
17     NETWORKSLIVER_API = "/plstackapi/networkslivers/";
18     SERVICE_API = "/plstackapi/services/";
19     SLICEPRIVILEGE_API = "/plstackapi/slice_privileges/";
20     NETWORKDEPLOYMENT_API = "/plstackapi/networkdeployments/";
21     FLAVOR_API = "/plstackapi/flavors/";
22     CONTROLLER_API = "/plstackapi/controllers/";
23     CONTROLLERSITEDEPLOYMENT_API = "/plstackapi/controllersitedeploymentses";
24
25     /* changed as a side effect of the big rename
26     SLICEDEPLOYMENT_API = "/plstackapi/slice_deployments/";
27     USERDEPLOYMENT_API = "/plstackapi/user_deployments/";
28     */
29
30     SLICEDEPLOYMENT_API = "/plstackapi/slicedeployments/";
31     USERDEPLOYMENT_API = "/plstackapi/userdeployments/";
32
33     SLICEPLUS_API = "/xoslib/slicesplus/";
34
35     XOSModel = Backbone.Model.extend({
36         /* from backbone-tastypie.js */
37         //idAttribute: 'resource_uri',
38
39         /* from backbone-tastypie.js */
40         url: function() {
41                     var url = this.attributes.resource_uri;
42
43                     if (!url) {
44                         if (this.id) {
45                             url = this.urlRoot + this.id;
46                         } else {
47                             // this happens when creating a new model.
48                             url = this.urlRoot;
49                         }
50                     }
51
52                     if (!url) {
53                         // XXX I'm not sure this does anything useful
54                         url = ( _.isFunction( this.collection.url ) ? this.collection.url() : this.collection.url );
55                         url = url || this.urlRoot;
56                     }
57
58                     // remove any existing query parameters
59                     url && ( url.indexOf("?") > -1 ) && ( url = url.split("?")[0] );
60
61                     url && ( url += ( url.length > 0 && url.charAt( url.length - 1 ) === '/' ) ? '' : '/' );
62
63                     url && ( url += "?no_hyperlinks=1" );
64
65                     return url;
66             },
67
68             listMethods: function() {
69                 var res = [];\r
70                 for(var m in this) {\r
71                     if(typeof this[m] == "function") {\r
72                         res.push(m)\r
73                     }\r
74                 }\r
75                 return res;\r
76             },
77
78             save: function(attributes, options) {
79                 if (this.preSave) {
80                     this.preSave();
81                 }
82                 return Backbone.Model.prototype.save.call(this, attributes, options);
83             },
84
85             getChoices: function(fieldName, excludeChosen) {
86                 choices=[];
87                 if (fieldName in this.m2mFields) {
88                     for (index in xos[this.m2mFields[fieldName]].models) {
89                         candidate = xos[this.m2mFields[fieldName]].models[index];
90                         if (excludeChosen && idInArray(candidate.id, this.attributes[fieldName])) {
91                             continue;
92                         }
93                         choices.push(candidate.id);
94                     }
95                 }
96                 return choices;
97             },
98
99             /* If a 'validate' method is supplied, then it will be called
100                automatically on save. Unfortunately, save calls neither the
101                'error' nor the 'success' callback if the validator fails.
102
103                For now, we're calling our validator 'xosValidate' so this
104                autoamtic validation doesn't occur.
105             */
106
107             xosValidate: function(attrs, options) {
108                 errors = {};
109                 foundErrors = false;
110                 _.each(this.validators, function(validatorList, fieldName) {
111                     _.each(validatorList, function(validator) {
112                         if (fieldName in attrs) {
113                             validatorResult = validateField(validator, attrs[fieldName], this)
114                             if (validatorResult != true) {
115                                 errors[fieldName] = validatorResult;
116                                 foundErrors = true;
117                             }
118                         }
119                     });
120                 });
121                 if (foundErrors) {
122                     return errors;
123                 }
124                 // backbone.js semantics -- on successful validate, return nothing
125             },
126
127             /* uncommenting this would make validate() call xosValidate()
128             validate: function(attrs, options) {
129                 r = this.xosValidate(attrs, options);
130                 console.log("validate");
131                 console.log(r);
132                 return r;
133             }, */
134     });
135
136     XOSCollection = Backbone.Collection.extend({
137         objects: function() {
138                     return this.models.map(function(element) { return element.attributes; });
139                  },
140
141         initialize: function(){
142           this.isLoaded = false;
143           this.failedLoad = false;
144           this.startedLoad = false;
145           this.sortVar = 'name';\r
146           this.sortOrder = 'asc';\r
147           this.on( "sort", this.sorted );\r
148         },\r
149 \r
150         relatedCollections: [],\r
151         foreignCollections: [],\r
152 \r
153         sorted: function() {\r
154             //console.log("sorted " + this.modelName);\r
155         },\r
156 \r
157         simpleComparator: function( model ){\r
158           parts=this.sortVar.split(".");\r
159           result = model.get(parts[0]);\r
160           for (index=1; index<parts.length; ++index) {\r
161               result=result[parts[index]];\r
162           }\r
163           return result;\r
164         },\r
165 \r
166         comparator: function (left, right) {\r
167             var l = this.simpleComparator(left);\r
168             var r = this.simpleComparator(right);\r
169 \r
170             if (l === void 0) return -1;\r
171             if (r === void 0) return 1;\r
172 \r
173             if (this.sortOrder=="desc") {\r
174                 return l < r ? 1 : l > r ? -1 : 0;\r
175             } else {\r
176                 return l < r ? -1 : l > r ? 1 : 0;\r
177             }\r
178         },\r
179 \r
180         fetchSuccess: function(collection, response, options) {\r
181             //console.log("fetch succeeded " + collection.modelName);\r
182             this.failedLoad = false;\r
183             this.fetching = false;\r
184             if (!this.isLoaded) {\r
185                 this.isLoaded = true;\r
186                 Backbone.trigger("xoslib:collectionLoadChange", this);\r
187             }\r
188             this.trigger("fetchStateChange");\r
189             if (options["orig_success"]) {\r
190                 options["orig_success"](collection, response, options);\r
191             }\r
192         },\r
193 \r
194         fetchFailure: function(collection, response, options) {\r
195             //console.log("fetch failed " + collection.modelName);\r
196             this.fetching = false;\r
197             if ((!this.isLoaded) && (!this.failedLoad)) {\r
198                 this.failedLoad=true;\r
199                 Backbone.trigger("xoslib:collectionLoadChange", this);\r
200             }\r
201             this.trigger("fetchStateChange");\r
202             if (options["orig_failure"]) {\r
203                 options["orig_failure"](collection, response, options);\r
204             }\r
205         },\r
206 \r
207         fetch: function(options) {\r
208             var self=this;\r
209             this.fetching=true;\r
210             //console.log("fetch " + this.modelName);\r
211             if (!this.startedLoad) {\r
212                 this.startedLoad=true;\r
213                 Backbone.trigger("xoslib:collectionLoadChange", this);\r
214             }\r
215             this.trigger("fetchStateChange");\r
216             if (options == undefined) {\r
217                 options = {};\r
218             }\r
219             options["orig_success"] = options["success"];\r
220             options["orig_failure"] = options["failure"];\r
221             options["success"] = function(collection, response, options) { self.fetchSuccess.call(self, collection, response, options); };\r
222             options["failure"] = this.fetchFailure;\r
223             Backbone.Collection.prototype.fetch.call(this, options);\r
224         },\r
225 \r
226         startPolling: function() {\r
227             if (!this._polling) {\r
228                 var collection=this;
229                 setInterval(function() { collection.fetch(); }, 10000);
230                 this._polling=true;
231                 this.fetch();
232             }
233         },
234
235         refresh: function(refreshRelated) {
236             if (!this.fetching) {
237                 this.fetch();
238             }
239             if (refreshRelated) {
240                 for (related in this.relatedCollections) {
241                     related = xos[related];
242                     if (!related.fetching) {
243                         related.fetch();
244                     }
245                 }
246             }
247         },
248
249         maybeFetch: function(options){
250                 // Helper function to fetch only if this collection has not been fetched before.
251             if(this._fetched){
252                     // If this has already been fetched, call the success, if it exists
253                 options.success && options.success();
254                 console.log("alreadyFetched");
255                 return;
256             }
257
258                 // when the original success function completes mark this collection as fetched
259             var self = this,
260             successWrapper = function(success){
261                 return function(){
262                     self._fetched = true;
263                     success && success.apply(this, arguments);
264                 };
265             };
266             options.success = successWrapper(options.success);
267             console.log("call fetch");
268             this.fetch(options);
269         },
270
271         getOrFetch: function(id, options){
272                 // Helper function to use this collection as a cache for models on the server
273             var model = this.get(id);
274
275             if(model){
276                 options.success && options.success(model);
277                 return;
278             }
279
280             model = new this.model({
281                 resource_uri: id
282             });
283
284             model.fetch(options);
285         },
286
287         /* filterBy: note that this yields a new collection. If you pass that
288               collection to a CompositeView, then the CompositeView won't get
289               any events that trigger on the original collection.
290
291               Using this function is probably wrong, and I wrote
292               FilteredCompositeView() to replace it.
293         */
294
295         filterBy: function(fieldName, value) {
296              filtered = this.filter(function(obj) {
297                  return obj.get(fieldName) == value;
298                  });
299              return new this.constructor(filtered);
300         },
301
302         /* from backbone-tastypie.js */
303         url: function( models ) {
304                     var url = this.urlRoot || ( models && models.length && models[0].urlRoot );
305                     url && ( url += ( url.length > 0 && url.charAt( url.length - 1 ) === '/' ) ? '' : '/' );
306
307                     // Build a url to retrieve a set of models. This assume the last part of each model's idAttribute
308                     // (set to 'resource_uri') contains the model's id.
309                     if ( models && models.length ) {
310                             var ids = _.map( models, function( model ) {
311                                             var parts = _.compact( model.id.split('/') );
312                                             return parts[ parts.length - 1 ];
313                                     });
314                             url += 'set/' + ids.join(';') + '/';
315                     }
316
317                     url && ( url += "?no_hyperlinks=1" );
318
319                     return url;
320             },
321
322         listMethods: function() {
323                 var res = [];\r
324                 for(var m in this) {\r
325                     if(typeof this[m] == "function") {\r
326                         res.push(m)\r
327                     }\r
328                 }\r
329                 return res;\r
330             },
331     });
332
333     function define_model(lib, attrs) {
334         modelName = attrs.modelName;
335         modelClassName = modelName;
336         collectionClassName = modelName + "Collection";
337
338         if (!attrs.addFields) {
339             attrs.addFields = attrs.detailFields;
340         }
341
342         attrs.inputType = attrs.inputType || {};
343         attrs.foreignFields = attrs.foreignFields || {};
344         attrs.m2mFields = attrs.m2mFields || {};
345         attrs.readOnlyFields = attrs.readOnlyFields || [];
346         attrs.detailLinkFields = attrs.detailLinkFields || ["id","name"];
347
348         if (!attrs.collectionName) {
349             attrs.collectionName = modelName + "s";
350         }
351         collectionName = attrs.collectionName;
352
353         modelAttrs = {}
354         collectionAttrs = {}
355
356         for (key in attrs) {
357             value = attrs[key];
358             if ($.inArray(key, ["urlRoot", "modelName", "collectionName", "listFields", "addFields", "detailFields", "detailLinkFields", "foreignFields", "inputType", "relatedCollections", "foreignCollections"])>=0) {
359                 modelAttrs[key] = value;
360                 collectionAttrs[key] = value;
361             }
362             if ($.inArray(key, ["validate", "preSave", "readOnlyFields"])) {
363                 modelAttrs[key] = value;
364             }
365         }
366
367         if ((typeof xosdefaults !== "undefined") && xosdefaults[modelName]) {
368             modelAttrs["defaults"] = xosdefaults[modelName];
369         }
370
371         if ((typeof xosvalidators !== "undefined") && xosvalidators[modelName]) {
372             modelAttrs["validators"] = xosvalidators[modelName];
373         }
374
375         lib[modelName] = XOSModel.extend(modelAttrs);
376
377         collectionAttrs["model"] = lib[modelName];
378
379         lib[collectionClassName] = XOSCollection.extend(collectionAttrs);
380         lib[collectionName] = new lib[collectionClassName]();
381
382         lib.allCollectionNames.push(collectionName);
383         lib.allCollections.push(lib[collectionName]);
384     };
385
386     function xoslib() {
387         this.allCollectionNames = [];
388         this.allCollections = [];
389
390         /* Give an id, the name of a collection, and the name of a field for models
391            within that collection, lookup the id and return the value of the field.
392         */
393
394         this.idToName = function(id, collectionName, fieldName) {
395             linkedObject = xos[collectionName].get(id);
396             if (linkedObject == undefined) {
397                 return "#" + id;
398             } else {
399                 return linkedObject.attributes[fieldName];
400             }
401         };
402
403         /* defining the models
404
405            modelName          - name of the model.
406
407            relatedCollections - collections which should be drawn as an inline
408                                 list when the detail view is displayed.
409                                 Format: <collection>:<collectionFieldName> where
410                                 <collectionFieldName> is the name of the field
411                                 in the collection that points back to the
412                                 collection in the detail view.
413
414            foreignCollections - collections which are used in idToName() calls
415                                 when presenting the data to the user. Used to
416                                 create a listento event. Somewhat
417                                 redundant with foreignFields.
418
419            foreignFields -      <localFieldName>:<collection>. Used to
420                                 automatically map ids into humanReadableNames
421                                 when presenting data to the user.
422
423            m2mfields -          <localFieldName>:<colleciton>. Used to
424                                 populate choices in picker lists. Simalar to
425                                 foreignFields.
426
427            listFields -         fields to display in lists
428
429            detailFields -       fields to display in detail views
430
431            addFields -          fields to display in popup add windows
432
433            inputType -          by default, "detailFields" will be displayed
434                                 as text input controls. This will let you display
435                                 a checkbox or a picker instead.
436         */
437
438         define_model(this, {urlRoot: SLIVER_API,
439                             relatedCollections: {"networkSlivers": "sliver"},
440                             foreignCollections: ["slices", "deployments", "images", "nodes", "users", "flavors"],
441                             foreignFields: {"creator": "users", "image": "images", "node": "nodes", "deploymentNetwork": "deployments", "slice": "slices", "flavor": "flavors"},
442                             modelName: "sliver",
443                             listFields: ["backend_status", "id", "name", "instance_id", "instance_name", "slice", "deploymentNetwork", "image", "node", "flavor"],
444                             addFields: ["slice", "deploymentNetwork", "flavor", "image", "node"],
445                             detailFields: ["backend_status", "name", "instance_id", "instance_name", "slice", "deploymentNetwork", "flavor", "image", "node", "creator"],
446                             preSave: function() { if (!this.attributes.name && this.attributes.slice) { this.attributes.name = xos.idToName(this.attributes.slice, "slices", "name"); } },
447                             });
448
449         define_model(this, {urlRoot: SLICE_API,
450                            relatedCollections: {"slivers": "slice", "slicePrivileges": "slice", "networks": "owner"},
451                            foreignCollections: ["services", "sites"],
452                            foreignFields: {"service": "services", "site": "sites"},
453                            listFields: ["backend_status", "id", "name", "enabled", "description", "slice_url", "site", "max_slivers", "service"],
454                            detailFields: ["backend_status", "name", "site", "enabled", "description", "slice_url", "max_slivers"],
455                            inputType: {"enabled": "checkbox"},
456                            modelName: "slice",
457                            xosValidate: function(attrs, options) {
458                                errors = XOSModel.prototype.xosValidate(this, attrs, options);
459                                // validate that slice.name starts with site.login_base
460                                site = attrs.site || this.site;
461                                if ((site!=undefined) && (attrs.name!=undefined)) {
462                                    site = xos.sites.get(site);
463                                    if (attrs.name.indexOf(site.attributes.login_base+"_") != 0) {
464                                         errors = errors || {};
465                                         errors["name"] = "must start with " + site.attributes.login_base + "_";
466                                    }
467                                }
468                                return errors;
469                              },
470                            });
471
472         define_model(this, {urlRoot: SLICEPRIVILEGE_API,
473                             foreignCollections: ["slices", "users", "sliceRoles"],
474                             modelName: "slicePrivilege",
475                             foreignFields: {"user": "users", "slice": "slices", "role": "sliceRoles"},
476                             listFields: ["backend_status", "id", "user", "slice", "role"],
477                             detailFields: ["backend_status", "user", "slice", "role"],
478                             });
479
480         define_model(this, {urlRoot: SLICEROLE_API,
481                             modelName: "sliceRole",
482                             listFields: ["backend_status", "id", "role"],
483                             detailFields: ["backend_status", "role"],
484                             });
485
486         define_model(this, {urlRoot: NODE_API,
487                             foreignCollections: ["sites", "deployments"],
488                             modelName: "node",
489                             foreignFields: {"site": "sites", "deployment": "deployments"},
490                             listFields: ["backend_status", "id", "name", "site", "deployment"],
491                             detailFields: ["backend_status", "name", "site", "deployment"],
492                             });
493
494         define_model(this, {urlRoot: SITE_API,
495                             relatedCollections: {"users": "site", "slices": "site", "nodes": "site", "siteDeployments": "site"},
496                             modelName: "site",
497                             listFields: ["backend_status", "id", "name", "site_url", "enabled", "login_base", "is_public", "abbreviated_name"],
498                             detailFields: ["backend_status", "name", "abbreviated_name", "url", "enabled", "is_public", "login_base"],
499                             inputType: {"enabled": "checkbox", "is_public": "checkbox"},
500                             });
501
502         define_model(this, {urlRoot: SITEDEPLOYMENT_API,
503                             foreignCollections: ["sites", "deployments", "controllers"],
504                             foreignFields: {"site": "sites", "deployment": "deployments", "controller": "controllers"},
505                             modelName: "siteDeployment",
506                             listFields: ["backend_status", "id", "site", "deployment", "controller", "availability_zone"],
507                             detailFields: ["backend_status", "site", "deployment", "controller", "availability_zone"],
508                             inputType: {"enabled": "checkbox", "is_public": "checkbox"},
509                             });
510
511         define_model(this, {urlRoot: USER_API,
512                             relatedCollections: {"slicePrivileges": "user", "slices": "owner"},
513                             foreignCollections: ["sites"],
514                             modelName: "user",
515                             foreignFields: {"site": "sites"},
516                             listFields: ["backend_status", "id", "username", "firstname", "lastname", "phone", "user_url", "site"],
517                             detailFields: ["backend_status", "username", "firstname", "lastname", "phone", "user_url", "site"],
518                             });
519
520         define_model(this, { urlRoot: DEPLOYMENT_API,
521                              relatedCollections: {"nodes": "deployment", "slivers": "deploymentNetwork"},
522                              m2mFields: {"flavors": "flavors", "sites": "sites", "images": "images"},
523                              modelName: "deployment",
524                              listFields: ["backend_status", "id", "name", "backend_type", "admin_tenant"],
525                              detailFields: ["backend_status", "name", "backend_type", "admin_tenant", "flavors", "sites", "images"],
526                              inputType: {"flavors": "picker", "sites": "picker", "images": "picker"},
527                              });
528
529         define_model(this, {urlRoot: IMAGE_API,
530                             model: this.image,
531                             modelName: "image",
532                             listFields: ["backend_status", "id", "name", "disk_format", "container_format", "path"],
533                             detailFields: ["backend_status", "name", "disk_format", "admin_tenant"],
534                             });
535
536         define_model(this, {urlRoot: NETWORKTEMPLATE_API,
537                             modelName: "networkTemplate",
538                             listFields: ["backend_status", "id", "name", "visibility", "translation", "sharedNetworkName", "sharedNetworkId"],
539                             detailFields: ["backend_status", "name", "description", "visibility", "translation", "sharedNetworkName", "sharedNetworkId"],
540                             });
541
542         define_model(this, {urlRoot: NETWORK_API,
543                             relatedCollections: {"networkSlivers": "network"},
544                             foreignCollections: ["slices", "networkTemplates"],
545                             modelName: "network",
546                             foreignFields: {"template": "networkTemplates", "owner": "slices"},
547                             listFields: ["backend_status", "id", "name", "template", "ports", "labels", "owner"],
548                             detailFields: ["backend_status", "name", "template", "ports", "labels", "owner"],
549                             });
550
551         define_model(this, {urlRoot: NETWORKSLIVER_API,
552                             modelName: "networkSliver",
553                             foreignFields: {"network": "networks", "sliver": "slivers"},
554                             listFields: ["backend_status", "id", "network", "sliver", "ip", "port_id"],
555                             detailFields: ["backend_status", "network", "sliver", "ip", "port_id"],
556                             });
557
558         define_model(this, {urlRoot: SERVICE_API,
559                             modelName: "service",
560                             listFields: ["backend_status", "id", "name", "enabled", "versionNumber", "published"],
561                             detailFields: ["backend_status", "name", "description", "versionNumber"],
562                             });
563
564         define_model(this, {urlRoot: FLAVOR_API,
565                             modelName: "flavor",
566                             m2mFields: {"deployments": "deployments"},
567                             listFields: ["backend_status", "id", "name", "flavor", "order", "default"],
568                             detailFields: ["backend_status", "name", "description", "flavor", "order", "default", "deployments"],
569                             inputType: {"default": "checkbox", "deployments": "picker"},
570                             });
571
572         define_model(this, {urlRoot: CONTROLLER_API,
573                             modelName: "controller",
574                             listFields: ["backend_status", "id", "name", "version", "backend_type"],
575                             detailFields: ["backend_status", "name", "version", "backend_type", "auth_url", "admin_user", "admin_password", "admin_tenant"],
576                             });
577
578         define_model(this, {urlRoot: CONTROLLERSITEDEPLOYMENT_API,
579                             modelName: "controllerSiteDeployment",
580                             foreignCollections: ["site_deployments", "controllers"],
581                             foreignFields: {"site_deployment": "siteDeployments", "controller": "controllers"},
582                             listFields: ["backend_status", "id", "site_deployment", "controller", "tenant_id"],
583                             detailFields: ["backend_status", "site_deployment", "controller", "tenant_id"],
584                             });
585
586         /* DELETED in site-controller branch
587
588         define_model(this, {urlRoot: NETWORKDEPLOYMENT_API,
589                             modelName: "networkDeployment",
590                             foreignFields: {"network": "networks", "deployment": "deployments"},
591                             listFields: ["backend_status", "id", "network", "deployment", "net_id"],
592                             detailFields: ["backend_status", "network", "deployment", "net_id"],
593                             });
594
595         define_model(this, {urlRoot: SLICEDEPLOYMENT_API,
596                            foreignCollections: ["slices", "deployments"],
597                            modelName: "sliceDeployment",
598                            foreignFields: {"slice": "slices", "deployment": "deployments"},
599                            listFields: ["backend_status", "id", "slice", "deployment", "tenant_id"],
600                            detailFields: ["backend_status", "slice", "deployment", "tenant_id"],
601                            });
602
603         define_model(this, {urlRoot: USERDEPLOYMENT_API,
604                             foreignCollections: ["users","deployments"],
605                             modelName: "userDeployment",
606                             foreignFields: {"deployment": "deployments", "user": "users"},
607                             listFields: ["backend_status", "id", "user", "deployment", "kuser_id"],
608                             detailFields: ["backend_status", "user", "deployment", "kuser_id"],
609                             });
610
611         END stuff deleted in site-controller branch */
612
613         /* not deleted, but obsolete since it has degenerated to a ManyToMany with no other fields
614
615         define_model(this, {urlRoot: IMAGEDEPLOYMENTS_API,
616                             modelName: "imageDeployment",
617                             foreignCollections: ["images", "deployments"],
618                             listFields: ["backend_status", "id", "image", "deployment", "glance_image_id"],
619                             detailFields: ["backend_status", "image", "deployment", "glance_image_id"],
620                             });
621
622         */
623
624         // enhanced REST
625         // XXX this really needs to somehow be combined with Slice, to avoid duplication
626         define_model(this, {urlRoot: SLICEPLUS_API,
627                             relatedCollections: {'slivers': "slice"},
628                             modelName: "slicePlus",
629                             collectionName: "slicesPlus"});
630
631         this.listObjects = function() { return this.allCollectionNames; };
632
633         this.getCollectionStatus = function() {
634             stats = {isLoaded: 0, failedLoad: 0, startedLoad: 0};
635             for (index in this.allCollections) {
636                 collection = this.allCollections[index];
637                 if (collection.isLoaded) {
638                     stats["isLoaded"] = stats["isLoaded"] + 1;
639                 }
640                 if (collection.failedLoad) {
641                     stats["failedLoad"] = stats["failedLoad"] + 1;
642                 }
643                 if (collection.startedLoad) {
644                     stats["startedLoad"] = stats["startedLoad"] + 1;
645                 }
646             }
647             stats["completedLoad"] = stats["failedLoad"] + stats["isLoaded"];
648             return stats;
649         };
650     };
651
652     xos = new xoslib();
653
654     function getCookie(name) {
655         var cookieValue = null;\r
656         if (document.cookie && document.cookie != '') {\r
657             var cookies = document.cookie.split(';');\r
658             for (var i = 0; i < cookies.length; i++) {\r
659                 var cookie = jQuery.trim(cookies[i]);\r
660                 // Does this cookie string begin with the name we want?\r
661                 if (cookie.substring(0, name.length + 1) == (name + '=')) {\r
662                     cookieValue = decodeURIComponent(cookie.substring(name.length + 1));\r
663                     break;\r
664                 }\r
665             }\r
666         }\r
667         return cookieValue;\r
668     }
669
670     (function() {
671       var _sync = Backbone.sync;\r
672       Backbone.sync = function(method, model, options){\r
673         options.beforeSend = function(xhr){\r
674           var token = getCookie("csrftoken");\r
675           xhr.setRequestHeader('X-CSRFToken', token);\r
676         };\r
677         return _sync(method, model, options);\r
678       };\r
679     })();
680 }