Ticket #4597: QueryReadStore.files.patch
| File QueryReadStore.files.patch, 34.6 KB (added by wolfram, 5 years ago) |
|---|
-
data/QueryReadStore.js
1 dojo.provide("dojox.data.QueryReadStore"); 2 dojo.provide("dojox.data.QueryReadStore.InvalidItemError"); 3 dojo.provide("dojox.data.QueryReadStore.InvalidAttributeError"); 4 5 dojo.require("dojo.string"); 6 dojo.require("dojo.data.util.simpleFetch"); 7 8 dojo.declare("dojox.data.QueryReadStore", null, { 9 /* 10 // summary: 11 // This class provides a store that is mainly intended to be used 12 // for loading data dynamically from the server, used i.e. for 13 // retreiving chunks of data from huge data stores on the server (by server-side filtering!). 14 // Upon calling the fetch() method of this store the data are requested from 15 // the server if they are not yet loaded for paging (or cached). 16 // 17 // For example used for a combobox which works on lots of data. It 18 // can be used to retreive the data partially upon entering the 19 // letters "ac" it returns only items like "action", "acting", etc. 20 // 21 // examples: 22 // // The parameter "query" contains the data that are sent to the server. 23 // var store = new dojox.data.QueryReadStore({url:'/search.php'}); 24 // store.fetch({query:{name:'a'}, queryOptions:{ignoreCase:false}}); 25 // 26 // // Since "serverQuery" is given, it overrules and those data are 27 // // sent to the server. 28 // var store = new dojox.data.QueryReadStore({url:'/search.php'}); 29 // store.fetch({serverQuery:{name:'a'}, queryOptions:{ignoreCase:false}}); 30 // 31 // todo: 32 // - there is a bug in the paging, when i set start:2, count:5 after an initial fetch() and doClientPaging:true 33 // it returns 6 elemetns, though count=5, try it in QueryReadStore.html 34 // - allow configuring if the paging shall takes place on the client or the server 35 // - add optional caching 36 // - when the first query searched for "a" and the next for a subset of 37 // the first, i.e. "ab" then we actually dont need a server request, if 38 // we have client paging, we just need to filter the items we already have 39 // that might also be tooo much logic 40 */ 41 42 url:"", 43 requestMethod:"get", 44 //useCache:false, 45 46 // We use the name in the errors, once the name is fixed hardcode it, may be. 47 _className:"dojox.data.QueryReadStore", 48 49 // This will contain the items we have loaded from the server. 50 // The contents of this array is optimized to satisfy all read-api requirements 51 // and for using lesser storage, so the keys and their content need some explaination: 52 // this._items[0].i - the item itself 53 // this._items[0].r - a reference to the store, so we can identify the item 54 // securly. We set this reference right after receiving the item from the 55 // server. 56 _items:[], 57 58 // Store the last query that triggered xhr request to the server. 59 // So we can compare if the request changed and if we shall reload 60 // (this also depends on other factors, such as is caching used, etc). 61 _lastServerQuery:null, 62 63 64 // Store a timestamp of the last server request. Actually I introduced this 65 // for testing, so I can check if no unnecessary requests were issued for 66 // client-side-paging. But I am sure people will find uses for it. 67 lastRequestTimestamp:null, 68 69 // If this is false, every request is sent to the server. 70 // If it's true a second request with the same query will not issue another 71 // request, but use the already returned data. This assumes that the server 72 // does not do the paging. 73 doClientPaging:true, 74 75 constructor: function(/* Object */ params){ 76 this.url = params.url; 77 this.requestMethod = typeof params.requestMethod=="undefined" ? this.requestMethod : params.requestMethod; 78 this.doClientPaging = typeof params.doClientPaging=="undefined" ? this.doClientPaging : params.doClientPaging; 79 //this.useCache = typeof params.useCache=="undefined" ? this.useCache : params.useCache; 80 }, 81 82 getValue: function( /* item */ item, /* attribute-name-string */ attribute, /* value? */ defaultValue){ 83 this._assertIsItem(item); 84 if (!this.hasAttribute(item, attribute)) { 85 // read api says: return defaultValue "only if *item* does not have a value for *attribute*." 86 // Is this the case here? The attribute doesn't exist, but a defaultValue, sounds reasonable. 87 if (defaultValue) { 88 return defaultValue; 89 } 90 //throw new Error(this._className+".getValue(): an invalid attribute for a given item was passed to the method 'getValue()'."); 91 throw new dojox.data.QueryReadStore.InvalidAttributeError(this._className+".getValue(): Item does not have the attribute '"+attribute+"'."); 92 } 93 return item.i[attribute]; 94 }, 95 96 getValues: function(/* item */ item, /* attribute-name-string */ attribute){ 97 var ret = []; 98 if (this.hasAttribute(item, attribute)) { 99 ret[0] = item.i[attribute]; 100 } 101 return ret; 102 }, 103 104 getAttributes: function(/* item */ item){ 105 this._assertIsItem(item); 106 var ret = []; 107 for (var i in item.i) { 108 ret.push(i); 109 } 110 return ret; 111 }, 112 113 hasAttribute: function(/* item */ item, /* attribute-name-string */ attribute) { 114 // summary: 115 // See dojo.data.api.Read.hasAttribute() 116 return this.isItem(item) && typeof item.i[attribute]!="undefined"; 117 }, 118 119 containsValue: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ value){ 120 var values = this.getValues(item, attribute); 121 var len = values.length; 122 for (var i=0; i<len; i++) { 123 if (values[i]==value) { 124 return true; 125 } 126 } 127 return false; 128 }, 129 130 isItem: function(/* anything */ something){ 131 // Some basic tests, that are quick and easy to do here. 132 // >>> var store = new dojox.data.QueryReadStore({}); 133 // >>> store.isItem(""); 134 // false 135 // 136 // >>> var store = new dojox.data.QueryReadStore({}); 137 // >>> store.isItem({}); 138 // false 139 // 140 // >>> var store = new dojox.data.QueryReadStore({}); 141 // >>> store.isItem(0); 142 // false 143 // 144 // >>> var store = new dojox.data.QueryReadStore({}); 145 // >>> store.isItem({name:"me", label:"me too"}); 146 // false 147 // 148 return typeof something.r!="undefined" && something.r==this; 149 }, 150 151 isItemLoaded: function(/* anything */ something) { 152 // Currently we dont have any state that tells if an item is loaded or not 153 // if the item exists its also loaded. 154 // This might change when we start working with refs inside items ... 155 return this.isItem(something); 156 }, 157 158 loadItem: function(/* object */ args){ 159 if (this.isItemLoaded(args.item)) { 160 return; 161 } 162 // Actually we have nothing to do here, or at least I dont know what to do here ... 163 }, 164 165 getFeatures: function(){ 166 return { 167 'dojo.data.api.Read': true 168 }; 169 }, 170 171 close: function(/*dojo.data.api.Request || keywordArgs || null */ request){ 172 // I have no idea if this is really needed ... 173 }, 174 175 getLabel: function(/* item */ item){ 176 // Override it to return whatever the label shall be, see Read-API. 177 return undefined; 178 }, 179 180 getLabelAttributes: function(/* item */ item){ 181 return null; 182 }, 183 184 _fetchItems: function(request, fetchHandler, errorHandler){ 185 // summary: 186 // The request contains the data as defined in the Read-API. 187 // Additionally there is following keyword "serverQuery". 188 // 189 // The *serverQuery* parameter, optional. 190 // This parameter contains the data that will be sent to the server. 191 // If this parameter is not given the parameter "query"'s 192 // data are sent to the server. This is done for some reasons: 193 // - to specify explicitly which data are sent to the server, they 194 // might also be a mix of what is contained in "query", "queryOptions" 195 // and the paging parameters "start" and "count" or may be even 196 // completely different things. 197 // - don't modify the request.query data, so the interface using this 198 // store can rely on unmodified data, as the combobox dijit currently 199 // does it, it compares if the query has changed 200 // - request.query is required by the Read-API 201 // 202 // I.e. the following examples might be sent via GET: 203 // fetch({query:{name:"abc"}, queryOptions:{ignoreCase:true}}) 204 // the URL will become: /url.php?name=abc 205 // 206 // fetch({serverQuery:{q:"abc", c:true}, query:{name:"abc"}, queryOptions:{ignoreCase:true}}) 207 // the URL will become: /url.php?q=abc&c=true 208 // // The serverQuery-parameter has overruled the query-parameter 209 // // but the query parameter stays untouched, but is not sent to the server! 210 // // The serverQuery contains more data than the query, so they might differ! 211 // 212 213 var serverQuery = typeof request["serverQuery"]=="undefined" ? request.query : request.serverQuery; 214 // Compare the last query and the current query by simply json-encoding them, 215 // so we dont have to do any deep object compare ... is there some dojo.areObjectsEqual()??? 216 if (this.doClientPaging && this._lastServerQuery!==null && 217 dojo.toJson(serverQuery)==dojo.toJson(this._lastServerQuery) 218 ) { 219 fetchHandler(this._items, request); 220 } else { 221 var xhrFunc = this.requestMethod.toLowerCase()=="post" ? dojo.xhrPost : dojo.xhrGet; 222 var xhrHandler = xhrFunc({url:this.url, handleAs:"json-comment-optional", content:serverQuery}); 223 var self = this; 224 xhrHandler.addCallback(function(data){ 225 self._items = []; 226 // Store a ref to "this" in each item, so we can simply check if an item 227 // really origins form here (idea is from ItemFileReadStore, I just don't know 228 // how efficient the real storage use, garbage collection effort, etc. is). 229 for (var i in data.items) { 230 self._items.push({i:data.items[i], r:self}); 231 } 232 // TODO actually we should do the same as dojo.data.ItemFileReadStore._getItemsFromLoadedData() to sanitize 233 // (does it really sanititze them) and store the data optimal. should we? for security reasons??? 234 fetchHandler(self._items, request); 235 }); 236 xhrHandler.addErrback(function(error){ 237 errorHandler(error, request); 238 }); 239 this.lastRequestTimestamp = new Date().getTime(); 240 this._lastServerQuery = serverQuery; 241 } 242 }, 243 244 _assertIsItem: function(/* item */ item){ 245 // summary: 246 // It throws an error if item is not valid, so you can call it in every method that needs to 247 // throw an error when item is invalid. 248 // item: 249 // The item to test for being contained by the store. 250 if(!this.isItem(item)){ 251 throw new dojox.data.QueryReadStore.InvalidItemError(this._className+": a function was passed an item argument that was not an item"); 252 } 253 }, 254 255 _assertIsAttribute: function(/* attribute-name-string */ attribute){ 256 // summary: 257 // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store. 258 // attribute: 259 // The attribute to test for being contained by the store. 260 if(typeof attribute !== "string"){ 261 throw new dojox.data.QueryReadStore.InvalidAttributeError(this._className+": '"+attribute+"' is not a valid attribute identifier."); 262 } 263 } 264 }); 265 dojo.extend(dojox.data.QueryReadStore, dojo.data.util.simpleFetch); 266 267 dojo.declare("dojox.data.QueryReadStore.InvalidItemError", Error, {}); 268 dojo.declare("dojox.data.QueryReadStore.InvalidAttributeError", Error, {}); 269 -
data/tests/QueryReadStore.html
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 2 "http://www.w3.org/TR/html4/strict.dtd"> 3 <html> 4 <head> 5 6 <style type="text/css"> 7 @import "../../../dojo/resources/dojo.css"; 8 @import "../../../dijit/themes/tundra/tundra.css"; 9 @import "../../../dijit/themes/tundra/tundra_rtl.css"; 10 </style> 11 12 <title>Query read store</title> 13 14 <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> 15 <script type="text/javascript" src="../../../dojo/data/util/simpleFetch.js"></script> 16 <script type="text/javascript" src="../../../dojox/data/QueryReadStore.js"></script> 17 <script type="text/javascript"> 18 dojo.require("dijit.form.ComboBox"); 19 dojo.require("dojox.data.QueryReadStore"); 20 21 22 23 24 dojo.provide("ComboBoxReadStore"); 25 dojo.declare("ComboBoxReadStore", dojox.data.QueryReadStore, { 26 fetch:function(request) { 27 // Copy the GET/POST parameters (request.query) we need into 28 // request.serverQuery. We actually want to have 29 // the query added to the URL like so: /url.php?q=<searchString> 30 // The data in "queryOptions" are useless for our backend, 31 // we ignore them, they are not sent to the server. 32 // The combobox puts this into the request-parameter: 33 // { 34 // query: {name:<searchString>}, 35 // queryOptions: {ignoreCase:true, deep:true}, 36 // ... 37 // } 38 // We generate request.serverQuery to be this, since those values will 39 // be sent to the server. 40 // { 41 // q:<searchString>} 42 // } 43 // This results in a xhr request to the following URL (in case of GET): 44 // /url.php?q=<searchString> 45 // 46 47 request.serverQuery = {q:request.query.name}; 48 // If we wanted to send the queryOptions too, we could simply do: 49 // request.serverQuery = { 50 // q:request.query.name, 51 // ignoreCase:request.queryOptions.ignoreCase, 52 // deep:request.queryOptions.deep 53 // }; 54 // This would then result in this URL, for ignoreCase and deep 55 // assumed to be true: 56 // /url.php?q=<searchString>&ignoreCase=true&deep=true 57 return this.inherited("fetch", arguments); 58 } 59 }); 60 61 dojo.provide("ServerPagingReadStore"); 62 dojo.declare("ServerPagingReadStore", dojox.data.QueryReadStore, { 63 fetch:function(request) { 64 request.serverQuery = {q:request.query.name, start:request.start, count:request.count}; 65 return this.inherited("fetch", arguments); 66 } 67 }); 68 69 70 71 var testStore = new ComboBoxReadStore({url:'stores/QueryReadStore.php'});; 72 function doSearch() { 73 74 var queryOptions = {}; 75 if (dojo.byId("ignoreCaseEnabled").checked) { 76 queryOptions.ignoreCase = dojo.query("#fetchForm")[0].ignoreCase[0].checked; 77 } 78 if (dojo.byId("deepEnabled").checked) { 79 queryOptions.deep = dojo.query("#fetchForm")[0].deep[0].checked; 80 } 81 82 var query = {}; 83 query.name = dojo.byId("searchText").value; 84 85 var request = {query:query, queryOptions:queryOptions}; 86 87 request.start = dojo.query("#fetchForm")[0].pagingStart.value; 88 request.count = dojo.query("#fetchForm")[0].pagingCount.value; 89 90 var requestMethod = "get"; 91 var radioButtons = dojo.query("#fetchForm")[0].requestMethod; 92 for (var i=0; i<radioButtons.length; i++){ 93 if (radioButtons[i].checked) { 94 requestMethod = radioButtons[i].value; 95 } 96 } 97 testStore.requestMethod = requestMethod; 98 testStore.doClientPaging = dojo.query("#fetchForm")[0].doClientPaging.checked; 99 function onComplete(items) { 100 var s = "number of items: "+items.length+"<br /><br />"; 101 for (var i=0; i<items.length; i++) { 102 s += i+": name: '"+testStore.getValue(items[i], "name")+"'<br />"; 103 } 104 //s += "<pre>"+dojo.toJson(items)+"</pre>"; 105 dojo.byId("fetchOutput").innerHTML = s; 106 }; 107 request.onComplete = onComplete; 108 testStore.fetch(request); 109 } 110 </script> 111 </head> 112 <body class="tundra" style="margin:20px;"> 113 <div dojoType="ComboBoxReadStore" jsId="store" url="stores/QueryReadStore.php" requestMethod="get"></div> 114 <input dojoType="dijit.form.ComboBox" store="store" pageSize="5" /> 115 <br /><br /><hr /> 116 117 This ComboBox uses a customized QueryReadStore, it prepares the query-string for the URL that 118 way that the paging parameters "start" and "count" are also send.<br /> 119 <div dojoType="ServerPagingReadStore" jsId="serverPagingStore" url="stores/QueryReadStore.php" requestMethod="get" doClientPaging="false"></div> 120 <input dojoType="dijit.form.ComboBox" store="serverPagingStore" pageSize="5" /> 121 <br /> 122 <a href="javascript://" onclick="var d = dojo.byId('pagingCode'); d.style.display= d.style.display=='none'?'block':'none';">Click here to see the code!</a> 123 <div id="pagingCode" style="display:none;"> 124 The HTML might look like this, the important attribute: <em>doClientPaging="false"</em> this takes care that the same query is fired to the server 125 and its not assumed that the client (the store) does the paging on the old data. 126 <pre> 127 <div dojoType="ServerPagingReadStore" jsId="serverPagingStore" url="stores/QueryReadStore.php" requestMethod="get" doClientPaging="false"></div> 128 <input dojoType="dijit.form.ComboBox" store="serverPagingStore" pageSize="10" /> 129 </pre> 130 <pre>dojo.require("dojox.data.QueryReadStore"); 131 132 dojo.provide("ServerPagingReadStore"); 133 dojo.declare("ServerPagingReadStore", dojox.data.QueryReadStore, { 134 fetch:function(request) { 135 request.query = {q:request.query.name, start:request.start, count:request.count}; 136 return this.inherited("fetch", arguments); 137 } 138 });</pre> 139 </div> 140 <br /><br /> 141 142 <hr /> 143 144 <style> 145 fieldset { 146 border:1px solid black; 147 display:inline; 148 padding:10px; 149 } 150 div.disabled { 151 opacity:0.1; 152 } 153 </style> 154 <form id="fetchForm"> 155 <fieldset title="requestMethod"> 156 <legend>requestMethod</legend> 157 get <input type="radio" value="get" checked="checked" name="requestMethod" /> 158 post <input type="radio" value="post" name="requestMethod" /> 159 </fieldset> 160 161 <fieldset title="queryOptions"> 162 <legend>queryOptions</legend> 163 164 <fieldset id="ignoreCaseFieldset"> 165 <legend><input type="checkbox" id="ignoreCaseEnabled" /> ignoreCase</legend> 166 <div class="disabled"> 167 true <input type="radio" value="0" checked="checked" name="ignoreCase" /> 168 false <input type="radio" value="1" name="ignoreCase" /> 169 </div> 170 </fieldset> 171 <fieldset id="deepFieldset"> 172 <legend><input type="checkbox" id="deepEnabled" /> deep</legend> 173 <div class="disabled"> 174 true <input type="radio" value="0" name="deep" /> 175 false <input type="radio" value="1" name="deep" checked="checked" /> 176 </div> 177 </fieldset> 178 </fieldset> 179 <fieldset title="paging"> 180 <legend>paging</legend> 181 start: <input id="pagingStart" value="0" size="3" /> 182 count: <input id="pagingCount" value="10" size="3" /> 183 <br /><br /> 184 do client paging: <input id="doClientPaging" type="checkbox" checked="checked" /> 185 </fieldset> 186 <script> 187 var fieldsets = ["ignoreCaseFieldset", "deepFieldset"]; 188 for (var i=0; i<fieldsets.length; i++) { 189 dojo.connect(dojo.byId(fieldsets[i]), "onchange", toggleFieldset); 190 } 191 function toggleFieldset(el) { 192 var divs = dojo.query("div", el.target.parentNode.parentNode); 193 if (divs.length) { 194 var div = divs[0]; 195 if (el.target.checked) { 196 dojo.removeClass(div, "disabled"); 197 } else { 198 dojo.addClass(div, "disabled"); 199 } 200 } 201 } 202 </script> 203 204 <br /><br /> 205 <input id="searchText" type="text" value="a"> 206 <input id="searchButton" type="button" value="store.fetch()" onclick="doSearch()" /> 207 </form> 208 <div id="fetchOutput" style="background-color:#FFDDDD; margin-top:1em; float:left;"></div> 209 </body> 210 </html> -
data/tests/stores/QueryReadStore.js
1 dojo.provide("dojox.data.tests.stores.QueryReadStore"); 2 dojo.require("dojox.data.QueryReadStore"); 3 4 dojo.require("dojox.testing.DocTest"); 5 6 dojox.data.tests.stores.QueryReadStore.getStore = function(){ 7 return new dojox.data.QueryReadStore({ 8 url: dojo.moduleUrl("dojox.data.tests", "stores/QueryReadStore.php").toString(), 9 doClientPaging:true // "true" is actually also the default, but make sure :-). 10 }); 11 }; 12 13 tests.register("dojox.data.tests.stores.QueryReadStore", 14 [ 15 function testDocTests(t) { 16 // summary: 17 // Run all the doc comments. 18 var doctest = new dojox.testing.DocTest(); 19 doctest.run("dojox.data.QueryReadStore"); 20 t.assertTrue(doctest.errors.length==0); 21 }, 22 23 function testReadApi_getValue(t){ 24 // summary: 25 // description: 26 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 27 28 var d = new doh.Deferred(); 29 function onComplete(items, request){ 30 var item = items[0]; 31 // The good cases. 32 t.assertEqual("Alabama", store.getValue(item, "name")); 33 t.assertEqual("<img src='images/Alabama.jpg'/>Alabama", store.getValue(item, "label")); 34 t.assertEqual("AL", store.getValue(item, "abbreviation")); 35 // Test the defaultValue cases (the third paramter). 36 t.assertEqual("default value", store.getValue(item, "NAME", "default value")); 37 // TODO Test for null somehow ... 38 // Read api says: Returns null if and only if null was explicitly set as the attribute value. 39 40 // Test for not-existing attributes without defaultValues and invalid items. 41 t.assertError(dojox.data.QueryReadStore.InvalidAttributeError, store, "getValue", [item, "NOT THERE"]); 42 t.assertError(dojox.data.QueryReadStore.InvalidItemError, store, "getValue", ["not an item", "NOT THERE"]); 43 44 d.callback(true); 45 } 46 store.fetch({query:{q:"Alabama"}, onComplete: onComplete}); 47 return d; //Object 48 }, 49 50 function testReadApi_getValues(t){ 51 // summary: 52 // description: 53 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 54 55 var d = new doh.Deferred(); 56 function onComplete(items, request){ 57 var item = items[0]; 58 // The good cases. 59 t.assertEqual(["Alabama"], store.getValues(item, "name")); 60 t.assertEqual(["<img src='images/Alabama.jpg'/>Alabama"], store.getValues(item, "label")); 61 t.assertEqual(["AL"], store.getValues(item, "abbreviation")); 62 // TODO Test for null somehow ... 63 // Read api says: Returns null if and only if null was explicitly set as the attribute value. 64 65 // Test for not-existing attributes without defaultValues and invalid items. 66 // TODO 67 //t.assertError(dojox.data.QueryReadStore.InvalidAttributeError, store, "getValues", [item, "NOT THERE"]); 68 //t.assertError(dojox.data.QueryReadStore.InvalidItemError, store, "getValues", ["not an item", "NOT THERE"]); 69 70 d.callback(true); 71 } 72 store.fetch({query:{q:"Alabama"}, onComplete: onComplete}); 73 return d; //Object 74 }, 75 76 function testReadApi_getAttributes(t){ 77 // summary: 78 // description: 79 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 80 81 var d = new doh.Deferred(); 82 function onComplete(items, request){ 83 var item = items[0]; 84 // The good case(s). 85 t.assertEqual(['name', 'label', 'abbreviation'], store.getAttributes(item)); 86 t.assertError(dojox.data.QueryReadStore.InvalidItemError, store, "getAttributes", [{}]); 87 88 d.callback(true); 89 } 90 store.fetch({query:{q:"Alabama"}, onComplete: onComplete}); 91 return d; //Object 92 }, 93 94 function testReadApi_hasAttribute(t){ 95 // summary: 96 // description: 97 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 98 99 var d = new doh.Deferred(); 100 function onComplete(items, request){ 101 var item = items[0]; 102 // The positive cases. 103 t.assertEqual(true, store.hasAttribute(item, "name")); 104 t.assertEqual(true, store.hasAttribute(item, "label")); 105 t.assertEqual(true, store.hasAttribute(item, "abbreviation")); 106 // Make sure attribute case doesnt matter. 107 t.assertEqual(false, store.hasAttribute(item, "NAME")); 108 t.assertEqual(false, store.hasAttribute(item, "Name")); 109 t.assertEqual(false, store.hasAttribute(item, "Label")); 110 // Pass in an invalid item. 111 t.assertEqual(false, store.hasAttribute({}, "abbreviation")); 112 // pass in something that looks like the item with the attribute. 113 t.assertEqual(false, store.hasAttribute({name:"yo"}, "name")); 114 115 d.callback(true); 116 } 117 store.fetch({query:{q:"Alaska"}, onComplete: onComplete}); 118 return d; //Object 119 }, 120 121 function testReadApi_containsValue(t){ 122 // summary: 123 // description: 124 t.assertTrue(false); 125 }, 126 127 function testReadApi_isItem(t){ 128 // summary: 129 // description: 130 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 131 132 var d = new doh.Deferred(); 133 function onComplete(items, request){ 134 // The good case. 135 t.assertEqual(true, store.isItem(items[0])); 136 // Try a pure object. 137 t.assertEqual(false, store.isItem({})); 138 // Try to look like an item. 139 t.assertEqual(false, store.isItem({name:"Alaska", label:"Alaska", abbreviation:"AK"})); 140 d.callback(true); 141 } 142 store.fetch({query:{q:"Alaska"}, onComplete: onComplete}); 143 return d; //Object 144 }, 145 146 function testReadApi_isItemLoaded(t){ 147 // summary: 148 // description: 149 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 150 151 var d = new doh.Deferred(); 152 function onComplete(items, request){ 153 var item = items[0]; 154 // The good case(s). 155 t.assertTrue(store.isItemLoaded(item)); 156 157 d.callback(true); 158 } 159 store.fetch({query:{q:"Alabama"}, onComplete: onComplete}); 160 return d; //Object 161 }, 162 163 //function testReadApi_loadItem(t){ 164 // // summary: 165 // // description: 166 // t.assertTrue(false); 167 //}, 168 169 function testReadApi_fetch_all(t){ 170 // summary: 171 // Simple test of fetching all items. 172 // description: 173 // Simple test of fetching all items. 174 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 175 176 var d = new doh.Deferred(); 177 function onComplete(items, request) { 178 t.assertEqual(8, items.length); 179 d.callback(true); 180 } 181 function onError(error, request) { 182 d.errback(error); 183 } 184 store.fetch({query:{q:"a"}, onComplete: onComplete, onError: onError}); 185 return d; //Object 186 }, 187 188 function testReadApi_fetch_one(t){ 189 // summary: 190 // description: 191 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 192 193 var d = new doh.Deferred(); 194 function onComplete(items, request){ 195 t.assertEqual(1, items.length); 196 d.callback(true); 197 } 198 function onError(error, request) { 199 d.errback(error); 200 } 201 store.fetch({query:{q:"Alaska"}, onComplete: onComplete, onError: onError}); 202 return d; //Object 203 }, 204 205 function testReadApi_fetch_client_paging(t){ 206 // summary: 207 // Lets test that paging on the same request does not trigger 208 // server requests. 209 // description: 210 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 211 212 var lastRequestTimestamp = null; 213 var firstItems = []; 214 var d = new doh.Deferred(); 215 function onComplete(items, request) { 216 t.assertEqual(5, items.length); 217 lastRequestTimestamp = store.lastRequestTimestamp; 218 firstItems = items; 219 220 // Do the next request AFTER the previous one, so we are sure its sequential. 221 // We need to be sure so we can compare to the data from the first request. 222 function onComplete1(items, request) { 223 t.assertEqual(5, items.length); 224 t.assertEqual(lastRequestTimestamp, store.lastRequestTimestamp); 225 t.assertEqual(firstItems[1], items[0]); 226 d.callback(true); 227 } 228 req.start = 1; 229 req.onComplete = onComplete1; 230 store.fetch(req); 231 } 232 function onError(error, request) { 233 d.errback(error); 234 } 235 var req = {query:{q:"a"}, start:0, count:5, 236 onComplete: onComplete, onError: onError}; 237 store.fetch(req); 238 return d; //Object 239 }, 240 241 function testReadApi_fetch_serverQuery(t) { 242 // TODO verify serverQuery vs. query 243 t.assertTrue(false); 244 }, 245 246 function testReadApi_getFeatures(t) { 247 var store = dojox.data.tests.stores.QueryReadStore.getStore(); 248 var features = store.getFeatures(); 249 t.assertTrue(features["dojo.data.api.Read"]); 250 } 251 ] 252 ); -
data/tests/stores/QueryReadStore.php
1 <?php 2 3 header("Content-Type", "text/json"); 4 5 $allItems = array( 6 array('name'=>"Alabama", 'label'=>"<img src='images/Alabama.jpg'/>Alabama", 'abbreviation'=>"AL"), 7 array('name'=>"Alaska", 'label'=>"Alaska", 'abbreviation'=>"AK"), 8 array('name'=>"American Samoa", 'label'=>"American Samoa", 'abbreviation'=>"AS"), 9 array('name'=>"Arizona", 'label'=>"Arizona", 'abbreviation'=>"AZ"), 10 array('name'=>"Arkansas", 'label'=>"Arkansas", 'abbreviation'=>"AR"), 11 array('name'=>"Armed Forces Europe", 'label'=>"Armed Forces Europe", 'abbreviation'=>"AE"), 12 array('name'=>"Armed Forces Pacific", 'label'=>"Armed Forces Pacific", 'abbreviation'=>"AP"), 13 array('name'=>"Armed Forces the Americas", 'label'=>"Armed Forces the Americas", 'abbreviation'=>"AA"), 14 array('name'=>"California", 'label'=>"California", 'abbreviation'=>"CA"), 15 array('name'=>"Colorado", 'label'=>"Colorado", 'abbreviation'=>"CO"), 16 array('name'=>"Connecticut", 'label'=>"Connecticut", 'abbreviation'=>"CT"), 17 array('name'=>"Delaware", 'label'=>"Delaware", 'abbreviation'=>"DE"), 18 array('name'=>"District of Columbia", 'label'=>"District of Columbia", 'abbreviation'=>"DC"), 19 array('name'=>"Federated States of Micronesia", 'label'=>"Federated States of Micronesia", 'abbreviation'=>"FM"), 20 array('name'=>"Florida", 'label'=>"Florida", 'abbreviation'=>"FL"), 21 array('name'=>"Georgia", 'label'=>"Georgia", 'abbreviation'=>"GA"), 22 array('name'=>"Guam", 'label'=>"Guam", 'abbreviation'=>"GU"), 23 array('name'=>"Hawaii", 'label'=>"Hawaii", 'abbreviation'=>"HI"), 24 array('name'=>"Idaho", 'label'=>"Idaho", 'abbreviation'=>"ID"), 25 array('name'=>"Illinois", 'label'=>"Illinois", 'abbreviation'=>"IL"), 26 array('name'=>"Indiana", 'label'=>"Indiana", 'abbreviation'=>"IN"), 27 array('name'=>"Iowa", 'label'=>"Iowa", 'abbreviation'=>"IA"), 28 array('name'=>"Kansas", 'label'=>"Kansas", 'abbreviation'=>"KS"), 29 array('name'=>"Kentucky", 'label'=>"Kentucky", 'abbreviation'=>"KY"), 30 array('name'=>"Louisiana", 'label'=>"Louisiana", 'abbreviation'=>"LA"), 31 array('name'=>"Maine", 'label'=>"Maine", 'abbreviation'=>"ME"), 32 array('name'=>"Marshall Islands", 'label'=>"Marshall Islands", 'abbreviation'=>"MH"), 33 array('name'=>"Maryland", 'label'=>"Maryland", 'abbreviation'=>"MD"), 34 array('name'=>"Massachusetts", 'label'=>"Massachusetts", 'abbreviation'=>"MA"), 35 array('name'=>"Michigan", 'label'=>"Michigan", 'abbreviation'=>"MI"), 36 array('name'=>"Minnesota", 'label'=>"Minnesota", 'abbreviation'=>"MN"), 37 array('name'=>"Mississippi", 'label'=>"Mississippi", 'abbreviation'=>"MS"), 38 array('name'=>"Missouri", 'label'=>"Missouri", 'abbreviation'=>"MO"), 39 array('name'=>"Montana", 'label'=>"Montana", 'abbreviation'=>"MT"), 40 array('name'=>"Nebraska", 'label'=>"Nebraska", 'abbreviation'=>"NE"), 41 array('name'=>"Nevada", 'label'=>"Nevada", 'abbreviation'=>"NV"), 42 array('name'=>"New Hampshire", 'label'=>"New Hampshire", 'abbreviation'=>"NH"), 43 array('name'=>"New Jersey", 'label'=>"New Jersey", 'abbreviation'=>"NJ"), 44 array('name'=>"New Mexico", 'label'=>"New Mexico", 'abbreviation'=>"NM"), 45 array('name'=>"New York", 'label'=>"New York", 'abbreviation'=>"NY"), 46 array('name'=>"North Carolina", 'label'=>"North Carolina", 'abbreviation'=>"NC"), 47 array('name'=>"North Dakota", 'label'=>"North Dakota", 'abbreviation'=>"ND"), 48 array('name'=>"Northern Mariana Islands", 'label'=>"Northern Mariana Islands", 'abbreviation'=>"MP"), 49 array('name'=>"Ohio", 'label'=>"Ohio", 'abbreviation'=>"OH"), 50 array('name'=>"Oklahoma", 'label'=>"Oklahoma", 'abbreviation'=>"OK"), 51 array('name'=>"Oregon", 'label'=>"Oregon", 'abbreviation'=>"OR"), 52 array('name'=>"Pennsylvania", 'label'=>"Pennsylvania", 'abbreviation'=>"PA"), 53 array('name'=>"Puerto Rico", 'label'=>"Puerto Rico", 'abbreviation'=>"PR"), 54 array('name'=>"Rhode Island", 'label'=>"Rhode Island", 'abbreviation'=>"RI"), 55 array('name'=>"South Carolina", 'label'=>"South Carolina", 'abbreviation'=>"SC"), 56 array('name'=>"South Dakota", 'label'=>"South Dakota", 'abbreviation'=>"SD"), 57 array('name'=>"Tennessee", 'label'=>"Tennessee", 'abbreviation'=>"TN"), 58 array('name'=>"Texas", 'label'=>"Texas", 'abbreviation'=>"TX"), 59 array('name'=>"Utah", 'label'=>"Utah", 'abbreviation'=>"UT"), 60 array('name'=>"Vermont", 'label'=>"Vermont", 'abbreviation'=>"VT"), 61 array('name'=> "Virgin Islands, U.S.", 'label'=>"Virgin Islands, U.S.", 'abbreviation'=>"VI"), 62 array('name'=>"Virginia", 'label'=>"Virginia", 'abbreviation'=>"VA"), 63 array('name'=>"Washington", 'label'=>"Washington", 'abbreviation'=>"WA"), 64 array('name'=>"West Virginia", 'label'=>"West Virginia", 'abbreviation'=>"WV"), 65 array('name'=>"Wisconsin", 'label'=>"Wisconsin", 'abbreviation'=>"WI"), 66 array('name'=>"Wyoming", 'label'=>"Wyoming", 'abbreviation'=>"WY"), 67 // array('id'=>, 'name'=>''), 68 ); 69 70 $q = ""; 71 if (array_key_exists("q", $_REQUEST)) { 72 $q = $_REQUEST['q']; 73 } 74 if (strlen($q) && $q[strlen($q)-1]=="*") { 75 $q = substr($q, 0, strlen($q)-1); 76 } 77 $ret = array(); 78 foreach ($allItems as $item) { 79 if (!$q || strpos(strtolower($item['name']), strtolower($q))===0) { 80 $ret[] = $item; 81 } 82 } 83 84 // Handle paging, if given. 85 if (array_key_exists("start", $_REQUEST)) { 86 $ret = array_slice($ret, $_REQUEST['start']); 87 } 88 if (array_key_exists("count", $_REQUEST)) { 89 $ret = array_slice($ret, 0, $_REQUEST['count']); 90 } 91 92 print '/*'.json_encode(array('items'=>$ret)).'*/';