1 | define(["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/connect", "dojox/rpc/Rest", |
---|
2 | "dojox/rpc/JsonRest", "dojox/json/schema", "dojox/data/ServiceStore"], |
---|
3 | function(lang, declare, connect, rpcRest, rpcJsonRest, jsonSchema, ServiceStore) { |
---|
4 | |
---|
5 | /*===== |
---|
6 | var ServiceStore = dojox.data.ServiceStore; |
---|
7 | =====*/ |
---|
8 | |
---|
9 | var JsonRestStore = declare("dojox.data.JsonRestStore", ServiceStore, |
---|
10 | { |
---|
11 | constructor: function(options){ |
---|
12 | //summary: |
---|
13 | // JsonRestStore is a Dojo Data store interface to JSON HTTP/REST web |
---|
14 | // storage services that support read and write through GET, PUT, POST, and DELETE. |
---|
15 | // options: |
---|
16 | // Keyword arguments |
---|
17 | // |
---|
18 | // The *schema* parameter |
---|
19 | // This is a schema object for this store. This should be JSON Schema format. |
---|
20 | // |
---|
21 | // The *service* parameter |
---|
22 | // This is the service object that is used to retrieve lazy data and save results |
---|
23 | // The function should be directly callable with a single parameter of an object id to be loaded |
---|
24 | // The function should also have the following methods: |
---|
25 | // put(id,value) - puts the value at the given id |
---|
26 | // post(id,value) - posts (appends) the value at the given id |
---|
27 | // delete(id) - deletes the value corresponding to the given id |
---|
28 | // Note that it is critical that the service parses responses as JSON. |
---|
29 | // If you are using dojox.rpc.Service, the easiest way to make sure this |
---|
30 | // happens is to make the responses have a content type of |
---|
31 | // application/json. If you are creating your own service, make sure you |
---|
32 | // use handleAs: "json" with your XHR requests. |
---|
33 | // |
---|
34 | // The *target* parameter |
---|
35 | // This is the target URL for this Service store. This may be used in place |
---|
36 | // of a service parameter to connect directly to RESTful URL without |
---|
37 | // using a dojox.rpc.Service object. |
---|
38 | // |
---|
39 | // The *idAttribute* parameter |
---|
40 | // Defaults to 'id'. The name of the attribute that holds an objects id. |
---|
41 | // This can be a preexisting id provided by the server. |
---|
42 | // If an ID isn't already provided when an object |
---|
43 | // is fetched or added to the store, the autoIdentity system |
---|
44 | // will generate an id for it and add it to the index. |
---|
45 | // |
---|
46 | // The *syncMode* parameter |
---|
47 | // Setting this to true will set the store to using synchronous calls by default. |
---|
48 | // Sync calls return their data immediately from the calling function, so |
---|
49 | // callbacks are unnecessary |
---|
50 | // |
---|
51 | // description: |
---|
52 | // The JsonRestStore will cause all saved modifications to be sent to the server using Rest commands (PUT, POST, or DELETE). |
---|
53 | // When using a Rest store on a public network, it is important to implement proper security measures to |
---|
54 | // control access to resources. |
---|
55 | // On the server side implementing a REST interface means providing GET, PUT, POST, and DELETE handlers. |
---|
56 | // GET - Retrieve an object or array/result set, this can be by id (like /table/1) or with a |
---|
57 | // query (like /table/?name=foo). |
---|
58 | // PUT - This should modify a object, the URL will correspond to the id (like /table/1), and the body will |
---|
59 | // provide the modified object |
---|
60 | // POST - This should create a new object. The URL will correspond to the target store (like /table/) |
---|
61 | // and the body should be the properties of the new object. The server's response should include a |
---|
62 | // Location header that indicates the id of the newly created object. This id will be used for subsequent |
---|
63 | // PUT and DELETE requests. JsonRestStore also includes a Content-Location header that indicates |
---|
64 | // the temporary randomly generated id used by client, and this location is used for subsequent |
---|
65 | // PUT/DELETEs if no Location header is provided by the server or if a modification is sent prior |
---|
66 | // to receiving a response from the server. |
---|
67 | // DELETE - This should delete an object by id. |
---|
68 | // These articles include more detailed information on using the JsonRestStore: |
---|
69 | // http://www.sitepen.com/blog/2008/06/13/restful-json-dojo-data/ |
---|
70 | // http://blog.medryx.org/2008/07/24/jsonreststore-overview/ |
---|
71 | // |
---|
72 | // example: |
---|
73 | // A JsonRestStore takes a REST service or a URL and uses it the remote communication for a |
---|
74 | // read/write dojo.data implementation. A JsonRestStore can be created with a simple URL like: |
---|
75 | // | new JsonRestStore({target:"/MyData/"}); |
---|
76 | // example: |
---|
77 | // To use a JsonRestStore with a service, you should create a |
---|
78 | // service with a REST transport. This can be configured with an SMD: |
---|
79 | // | { |
---|
80 | // | services: { |
---|
81 | // | jsonRestStore: { |
---|
82 | // | transport: "REST", |
---|
83 | // | envelope: "URL", |
---|
84 | // | target: "store.php", |
---|
85 | // | contentType:"application/json", |
---|
86 | // | parameters: [ |
---|
87 | // | {name: "location", type: "string", optional: true} |
---|
88 | // | ] |
---|
89 | // | } |
---|
90 | // | } |
---|
91 | // | } |
---|
92 | // The SMD can then be used to create service, and the service can be passed to a JsonRestStore. For example: |
---|
93 | // | var myServices = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc.tests.resources", "test.smd")); |
---|
94 | // | var jsonStore = new dojox.data.JsonRestStore({service:myServices.jsonRestStore}); |
---|
95 | // example: |
---|
96 | // The JsonRestStore also supports lazy loading. References can be made to objects that have not been loaded. |
---|
97 | // For example if a service returned: |
---|
98 | // | {"name":"Example","lazyLoadedObject":{"$ref":"obj2"}} |
---|
99 | // And this object has accessed using the dojo.data API: |
---|
100 | // | var obj = jsonStore.getValue(myObject,"lazyLoadedObject"); |
---|
101 | // The object would automatically be requested from the server (with an object id of "obj2"). |
---|
102 | // |
---|
103 | |
---|
104 | connect.connect(rpcRest._index,"onUpdate",this,function(obj,attrName,oldValue,newValue){ |
---|
105 | var prefix = this.service.servicePath; |
---|
106 | if(!obj.__id){ |
---|
107 | console.log("no id on updated object ", obj); |
---|
108 | }else if(obj.__id.substring(0,prefix.length) == prefix){ |
---|
109 | this.onSet(obj,attrName,oldValue,newValue); |
---|
110 | } |
---|
111 | }); |
---|
112 | this.idAttribute = this.idAttribute || 'id';// no options about it, we have to have identity |
---|
113 | |
---|
114 | if(typeof options.target == 'string'){ |
---|
115 | options.target = options.target.match(/\/$/) || this.allowNoTrailingSlash ? options.target : (options.target + '/'); |
---|
116 | if(!this.service){ |
---|
117 | this.service = rpcJsonRest.services[options.target] || |
---|
118 | rpcRest(options.target, true); |
---|
119 | // create a default Rest service |
---|
120 | } |
---|
121 | } |
---|
122 | |
---|
123 | rpcJsonRest.registerService(this.service, options.target, this.schema); |
---|
124 | this.schema = this.service._schema = this.schema || this.service._schema || {}; |
---|
125 | // wrap the service with so it goes through JsonRest manager |
---|
126 | this.service._store = this; |
---|
127 | this.service.idAsRef = this.idAsRef; |
---|
128 | this.schema._idAttr = this.idAttribute; |
---|
129 | var constructor = rpcJsonRest.getConstructor(this.service); |
---|
130 | var self = this; |
---|
131 | this._constructor = function(data){ |
---|
132 | constructor.call(this, data); |
---|
133 | self.onNew(this); |
---|
134 | } |
---|
135 | this._constructor.prototype = constructor.prototype; |
---|
136 | this._index = rpcRest._index; |
---|
137 | }, |
---|
138 | |
---|
139 | // summary: |
---|
140 | // Will load any schemas referenced content-type header or in Link headers |
---|
141 | loadReferencedSchema: true, |
---|
142 | // summary: |
---|
143 | // Treat objects in queries as partially loaded objects |
---|
144 | idAsRef: false, |
---|
145 | referenceIntegrity: true, |
---|
146 | target:"", |
---|
147 | // summary: |
---|
148 | // Allow no trailing slash on target paths. This is generally discouraged since |
---|
149 | // it creates prevents simple scalar values from being used a relative URLs. |
---|
150 | // Disabled by default. |
---|
151 | allowNoTrailingSlash: false, |
---|
152 | //Write API Support |
---|
153 | newItem: function(data, parentInfo){ |
---|
154 | // summary: |
---|
155 | // adds a new item to the store at the specified point. |
---|
156 | // Takes two parameters, data, and options. |
---|
157 | // |
---|
158 | // data: /* object */ |
---|
159 | // The data to be added in as an item. |
---|
160 | data = new this._constructor(data); |
---|
161 | if(parentInfo){ |
---|
162 | // get the previous value or any empty array |
---|
163 | var values = this.getValue(parentInfo.parent,parentInfo.attribute,[]); |
---|
164 | // set the new value |
---|
165 | values = values.concat([data]); |
---|
166 | data.__parent = values; |
---|
167 | this.setValue(parentInfo.parent, parentInfo.attribute, values); |
---|
168 | } |
---|
169 | return data; |
---|
170 | }, |
---|
171 | deleteItem: function(item){ |
---|
172 | // summary: |
---|
173 | // deletes item and any references to that item from the store. |
---|
174 | // |
---|
175 | // item: |
---|
176 | // item to delete |
---|
177 | // |
---|
178 | |
---|
179 | // If the desire is to delete only one reference, unsetAttribute or |
---|
180 | // setValue is the way to go. |
---|
181 | var checked = []; |
---|
182 | var store = dataExtCfg._getStoreForItem(item) || this; |
---|
183 | if(this.referenceIntegrity){ |
---|
184 | // cleanup all references |
---|
185 | rpcJsonRest._saveNotNeeded = true; |
---|
186 | var index = rpcRest._index; |
---|
187 | var fixReferences = function(parent){ |
---|
188 | var toSplice; |
---|
189 | // keep track of the checked ones |
---|
190 | checked.push(parent); |
---|
191 | // mark it checked so we don't run into circular loops when encountering cycles |
---|
192 | parent.__checked = 1; |
---|
193 | for(var i in parent){ |
---|
194 | if(i.substring(0,2) != "__"){ |
---|
195 | var value = parent[i]; |
---|
196 | if(value == item){ |
---|
197 | if(parent != index){ // make sure we are just operating on real objects |
---|
198 | if(parent instanceof Array){ |
---|
199 | // mark it as needing to be spliced, don't do it now or it will mess up the index into the array |
---|
200 | (toSplice = toSplice || []).push(i); |
---|
201 | }else{ |
---|
202 | // property, just delete it. |
---|
203 | (dataExtCfg._getStoreForItem(parent) || store).unsetAttribute(parent, i); |
---|
204 | } |
---|
205 | } |
---|
206 | }else{ |
---|
207 | if((typeof value == 'object') && value){ |
---|
208 | if(!value.__checked){ |
---|
209 | // recursively search |
---|
210 | fixReferences(value); |
---|
211 | } |
---|
212 | if(typeof value.__checked == 'object' && parent != index){ |
---|
213 | // if it is a modified array, we will replace it |
---|
214 | (dataExtCfg._getStoreForItem(parent) || store).setValue(parent, i, value.__checked); |
---|
215 | } |
---|
216 | } |
---|
217 | } |
---|
218 | } |
---|
219 | } |
---|
220 | if(toSplice){ |
---|
221 | // we need to splice the deleted item out of these arrays |
---|
222 | i = toSplice.length; |
---|
223 | parent = parent.__checked = parent.concat(); // indicates that the array is modified |
---|
224 | while(i--){ |
---|
225 | parent.splice(toSplice[i], 1); |
---|
226 | } |
---|
227 | return parent; |
---|
228 | } |
---|
229 | return null; |
---|
230 | }; |
---|
231 | // start with the index |
---|
232 | fixReferences(index); |
---|
233 | rpcJsonRest._saveNotNeeded = false; |
---|
234 | var i = 0; |
---|
235 | while(checked[i]){ |
---|
236 | // remove the checked marker |
---|
237 | delete checked[i++].__checked; |
---|
238 | } |
---|
239 | } |
---|
240 | rpcJsonRest.deleteObject(item); |
---|
241 | |
---|
242 | store.onDelete(item); |
---|
243 | }, |
---|
244 | changing: function(item,_deleting){ |
---|
245 | // summary: |
---|
246 | // adds an item to the list of dirty items. This item |
---|
247 | // contains a reference to the item itself as well as a |
---|
248 | // cloned and trimmed version of old item for use with |
---|
249 | // revert. |
---|
250 | rpcJsonRest.changing(item,_deleting); |
---|
251 | }, |
---|
252 | cancelChanging : function(object){ |
---|
253 | // summary: |
---|
254 | // Removes an object from the list of dirty objects |
---|
255 | // This will prevent that object from being saved to the server on the next save |
---|
256 | // object: |
---|
257 | // The item to cancel changes on |
---|
258 | if(!object.__id){ |
---|
259 | return; |
---|
260 | } |
---|
261 | dirtyObjects = dirty=rpcJsonRest.getDirtyObjects(); |
---|
262 | for(var i=0; i<dirtyObjects.length; i++){ |
---|
263 | var dirty = dirtyObjects[i]; |
---|
264 | if(object==dirty.object){ |
---|
265 | dirtyObjects.splice(i, 1); |
---|
266 | return; |
---|
267 | } |
---|
268 | } |
---|
269 | |
---|
270 | }, |
---|
271 | |
---|
272 | setValue: function(item, attribute, value){ |
---|
273 | // summary: |
---|
274 | // sets 'attribute' on 'item' to 'value' |
---|
275 | |
---|
276 | var old = item[attribute]; |
---|
277 | var store = item.__id ? dataExtCfg._getStoreForItem(item) : this; |
---|
278 | if(jsonSchema && store.schema && store.schema.properties){ |
---|
279 | // if we have a schema and schema validator available we will validate the property change |
---|
280 | jsonSchema.mustBeValid(jsonSchema.checkPropertyChange(value,store.schema.properties[attribute])); |
---|
281 | } |
---|
282 | if(attribute == store.idAttribute){ |
---|
283 | throw new Error("Can not change the identity attribute for an item"); |
---|
284 | } |
---|
285 | store.changing(item); |
---|
286 | item[attribute]=value; |
---|
287 | if(value && !value.__parent){ |
---|
288 | value.__parent = item; |
---|
289 | } |
---|
290 | store.onSet(item,attribute,old,value); |
---|
291 | }, |
---|
292 | setValues: function(item, attribute, values){ |
---|
293 | // summary: |
---|
294 | // sets 'attribute' on 'item' to 'value' value |
---|
295 | // must be an array. |
---|
296 | |
---|
297 | |
---|
298 | if(!lang.isArray(values)){ |
---|
299 | throw new Error("setValues expects to be passed an Array object as its value"); |
---|
300 | } |
---|
301 | this.setValue(item,attribute,values); |
---|
302 | }, |
---|
303 | |
---|
304 | unsetAttribute: function(item, attribute){ |
---|
305 | // summary: |
---|
306 | // unsets 'attribute' on 'item' |
---|
307 | |
---|
308 | this.changing(item); |
---|
309 | var old = item[attribute]; |
---|
310 | delete item[attribute]; |
---|
311 | this.onSet(item,attribute,old,undefined); |
---|
312 | }, |
---|
313 | save: function(kwArgs){ |
---|
314 | // summary: |
---|
315 | // Saves the dirty data using REST Ajax methods. See dojo.data.api.Write for API. |
---|
316 | // |
---|
317 | // kwArgs.global: |
---|
318 | // This will cause the save to commit the dirty data for all |
---|
319 | // JsonRestStores as a single transaction. |
---|
320 | // |
---|
321 | // kwArgs.revertOnError |
---|
322 | // This will cause the changes to be reverted if there is an |
---|
323 | // error on the save. By default a revert is executed unless |
---|
324 | // a value of false is provide for this parameter. |
---|
325 | // |
---|
326 | // kwArgs.incrementalUpdates |
---|
327 | // For items that have been updated, if this is enabled, the server will be sent a POST request |
---|
328 | // with a JSON object containing the changed properties. By default this is |
---|
329 | // not enabled, and a PUT is used to deliver an update, and will include a full |
---|
330 | // serialization of all the properties of the item/object. |
---|
331 | // If this is true, the POST request body will consist of a JSON object with |
---|
332 | // only the changed properties. The incrementalUpdates parameter may also |
---|
333 | // be a function, in which case it will be called with the updated and previous objects |
---|
334 | // and an object update representation can be returned. |
---|
335 | // |
---|
336 | // kwArgs.alwaysPostNewItems |
---|
337 | // If this is true, new items will always be sent with a POST request. By default |
---|
338 | // this is not enabled, and the JsonRestStore will send a POST request if |
---|
339 | // the item does not include its identifier (expecting server assigned location/ |
---|
340 | // identifier), and will send a PUT request if the item does include its identifier |
---|
341 | // (the PUT will be sent to the URI corresponding to the provided identifier). |
---|
342 | |
---|
343 | if(!(kwArgs && kwArgs.global)){ |
---|
344 | (kwArgs = kwArgs || {}).service = this.service; |
---|
345 | } |
---|
346 | if("syncMode" in kwArgs ? kwArgs.syncMode : this.syncMode){ |
---|
347 | rpcConfig._sync = true; |
---|
348 | } |
---|
349 | |
---|
350 | var actions = rpcJsonRest.commit(kwArgs); |
---|
351 | this.serverVersion = this._updates && this._updates.length; |
---|
352 | return actions; |
---|
353 | }, |
---|
354 | |
---|
355 | revert: function(kwArgs){ |
---|
356 | // summary |
---|
357 | // returns any modified data to its original state prior to a save(); |
---|
358 | // |
---|
359 | // kwArgs.global: |
---|
360 | // This will cause the revert to undo all the changes for all |
---|
361 | // JsonRestStores in a single operation. |
---|
362 | rpcJsonRest.revert(kwArgs && kwArgs.global && this.service); |
---|
363 | }, |
---|
364 | |
---|
365 | isDirty: function(item){ |
---|
366 | // summary |
---|
367 | // returns true if the item is marked as dirty. |
---|
368 | return rpcJsonRest.isDirty(item, this); |
---|
369 | }, |
---|
370 | isItem: function(item, anyStore){ |
---|
371 | // summary: |
---|
372 | // Checks to see if a passed 'item' |
---|
373 | // really belongs to this JsonRestStore. |
---|
374 | // |
---|
375 | // item: /* object */ |
---|
376 | // The value to test for being an item |
---|
377 | // anyStore: /* boolean*/ |
---|
378 | // If true, this will return true if the value is an item for any JsonRestStore, |
---|
379 | // not just this instance |
---|
380 | return item && item.__id && (anyStore || this.service == rpcJsonRest.getServiceAndId(item.__id).service); |
---|
381 | }, |
---|
382 | _doQuery: function(args){ |
---|
383 | var query= typeof args.queryStr == 'string' ? args.queryStr : args.query; |
---|
384 | var deferred = rpcJsonRest.query(this.service,query, args); |
---|
385 | var self = this; |
---|
386 | if(this.loadReferencedSchema){ |
---|
387 | deferred.addCallback(function(result){ |
---|
388 | var contentType = deferred.ioArgs && deferred.ioArgs.xhr && deferred.ioArgs.xhr.getResponseHeader("Content-Type"); |
---|
389 | var schemaRef = contentType && contentType.match(/definedby\s*=\s*([^;]*)/); |
---|
390 | if(contentType && !schemaRef){ |
---|
391 | schemaRef = deferred.ioArgs.xhr.getResponseHeader("Link"); |
---|
392 | schemaRef = schemaRef && schemaRef.match(/<([^>]*)>;\s*rel="?definedby"?/); |
---|
393 | } |
---|
394 | schemaRef = schemaRef && schemaRef[1]; |
---|
395 | if(schemaRef){ |
---|
396 | var serviceAndId = rpcJsonRest.getServiceAndId((self.target + schemaRef).replace(/^(.*\/)?(\w+:\/\/)|[^\/\.]+\/\.\.\/|^.*\/(\/)/,"$2$3")); |
---|
397 | var schemaDeferred = rpcJsonRest.byId(serviceAndId.service, serviceAndId.id); |
---|
398 | schemaDeferred.addCallbacks(function(newSchema){ |
---|
399 | lang.mixin(self.schema, newSchema); |
---|
400 | return result; |
---|
401 | }, function(error){ |
---|
402 | console.error(error); // log it, but don't let it cause the main request to fail |
---|
403 | return result; |
---|
404 | }); |
---|
405 | return schemaDeferred; |
---|
406 | } |
---|
407 | return undefined;//don't change anything, and deal with the stupid post-commit lint complaints |
---|
408 | }); |
---|
409 | } |
---|
410 | return deferred; |
---|
411 | }, |
---|
412 | _processResults: function(results, deferred){ |
---|
413 | // index the results |
---|
414 | var count = results.length; |
---|
415 | // if we don't know the length, and it is partial result, we will guess that it is twice as big, that will work for most widgets |
---|
416 | return {totalCount:deferred.fullLength || (deferred.request.count == count ? (deferred.request.start || 0) + count * 2 : count), items: results}; |
---|
417 | }, |
---|
418 | |
---|
419 | getConstructor: function(){ |
---|
420 | // summary: |
---|
421 | // Gets the constructor for objects from this store |
---|
422 | return this._constructor; |
---|
423 | }, |
---|
424 | getIdentity: function(item){ |
---|
425 | var id = item.__clientId || item.__id; |
---|
426 | if(!id){ |
---|
427 | return id; |
---|
428 | } |
---|
429 | var prefix = this.service.servicePath.replace(/[^\/]*$/,''); |
---|
430 | // support for relative or absolute referencing with ids |
---|
431 | return id.substring(0,prefix.length) != prefix ? id : id.substring(prefix.length); // String |
---|
432 | }, |
---|
433 | fetchItemByIdentity: function(args){ |
---|
434 | var id = args.identity; |
---|
435 | var store = this; |
---|
436 | // if it is an absolute id, we want to find the right store to query |
---|
437 | if(id.toString().match(/^(\w*:)?\//)){ |
---|
438 | var serviceAndId = rpcJsonRest.getServiceAndId(id); |
---|
439 | store = serviceAndId.service._store; |
---|
440 | args.identity = serviceAndId.id; |
---|
441 | } |
---|
442 | args._prefix = store.service.servicePath.replace(/[^\/]*$/,''); |
---|
443 | return store.inherited(arguments); |
---|
444 | }, |
---|
445 | //Notifcation Support |
---|
446 | |
---|
447 | onSet: function(){}, |
---|
448 | onNew: function(){}, |
---|
449 | onDelete: function(){}, |
---|
450 | |
---|
451 | getFeatures: function(){ |
---|
452 | // summary: |
---|
453 | // return the store feature set |
---|
454 | var features = this.inherited(arguments); |
---|
455 | features["dojo.data.api.Write"] = true; |
---|
456 | features["dojo.data.api.Notification"] = true; |
---|
457 | return features; |
---|
458 | }, |
---|
459 | |
---|
460 | getParent: function(item){ |
---|
461 | // summary: |
---|
462 | // Returns the parent item (or query) for the given item |
---|
463 | // item: |
---|
464 | // The item to find the parent of |
---|
465 | |
---|
466 | return item && item.__parent; |
---|
467 | } |
---|
468 | |
---|
469 | |
---|
470 | } |
---|
471 | ); |
---|
472 | JsonRestStore.getStore = function(options, Class){ |
---|
473 | // summary: |
---|
474 | // Will retrieve or create a store using the given options (the same options |
---|
475 | // that are passed to JsonRestStore constructor. Returns a JsonRestStore instance |
---|
476 | // options: |
---|
477 | // See the JsonRestStore constructor |
---|
478 | // Class: |
---|
479 | // Constructor to use (for creating stores from JsonRestStore subclasses). |
---|
480 | // This is optional and defaults to JsonRestStore. |
---|
481 | if(typeof options.target == 'string'){ |
---|
482 | options.target = options.target.match(/\/$/) || options.allowNoTrailingSlash ? |
---|
483 | options.target : (options.target + '/'); |
---|
484 | var store = (rpcJsonRest.services[options.target] || {})._store; |
---|
485 | if(store){ |
---|
486 | return store; |
---|
487 | } |
---|
488 | } |
---|
489 | return new (Class || JsonRestStore)(options); |
---|
490 | }; |
---|
491 | |
---|
492 | var dataExtCfg = lang.getObject("dojox.data",true); |
---|
493 | dataExtCfg._getStoreForItem = function(item){ |
---|
494 | if(item.__id){ |
---|
495 | var serviceAndId = rpcJsonRest.getServiceAndId(item.__id); |
---|
496 | if(serviceAndId && serviceAndId.service._store){ |
---|
497 | return serviceAndId.service._store; |
---|
498 | }else{ |
---|
499 | var servicePath = item.__id.toString().match(/.*\//)[0]; |
---|
500 | return new JsonRestStore({target:servicePath}); |
---|
501 | } |
---|
502 | } |
---|
503 | return null; |
---|
504 | }; |
---|
505 | var jsonRefConfig = lang.getObject("dojox.json.ref", true); |
---|
506 | jsonRefConfig._useRefs = true; // Use referencing when identifiable objects are referenced |
---|
507 | |
---|
508 | return JsonRestStore; |
---|
509 | }); |
---|