From 8b3b80ba3403a7e7c4a29a046ee0f4d8b9ab9edd Mon Sep 17 00:00:00 2001 From: Lebbeous Fogle-Weekley Date: Fri, 20 Jan 2012 16:04:49 -0500 Subject: [PATCH] 1) avoid stale data 2) fix backspace issue more specifically: 1 - if a response from the server is not the response to the most recent request issued by the AutoSuggestStore, don't use it 2 - when the search query is empty, empty the store (previously we knew not to talk to the server but didn't empty the store) Signed-off-by: Lebbeous Fogle-Weekley --- Open-ILS/web/js/dojo/openils/AutoSuggestStore.js | 95 +++++++++++------------- 1 file changed, 44 insertions(+), 51 deletions(-) diff --git a/Open-ILS/web/js/dojo/openils/AutoSuggestStore.js b/Open-ILS/web/js/dojo/openils/AutoSuggestStore.js index 2f29ad15cf..5c93d1bb5c 100644 --- a/Open-ILS/web/js/dojo/openils/AutoSuggestStore.js +++ b/Open-ILS/web/js/dojo/openils/AutoSuggestStore.js @@ -3,14 +3,18 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { dojo.provide("openils.AutoSuggestStore"); dojo.require("openils.Util"); - /* an exception class specific to openils.AutoSuggestStore */ + /* Here's an exception class specific to openils.AutoSuggestStore */ function AutoSuggestStoreError(message) { this.message = message; } AutoSuggestStoreError.prototype.toString = function() { return "openils.AutoSuggestStore: " + this.message; }; function TermString(str, field) { this.str = str; this.field = field; } - TermString.prototype.toString = function() { return this.str; }; + /* It doesn't seem to be possible to subclass builtins like String, but + * these are the only methods of String we should actually need */ + TermString.prototype.toString=function(){return this.str;}; + TermString.prototype.toLowerCase=function(){return this.str.toLowerCase();}; + TermString.prototype.substr=function(){return this.str.substr(arguments);}; var _autosuggest_fields = ["id", "match", "term", "field"]; var _cmf_cache, _cmc_cache; @@ -18,6 +22,9 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { dojo.declare( "openils.AutoSuggestStore", null, { + "limit": 10, + "last_fetch": null, + "constructor": function(/* object */ args) { dojo.mixin(this, args); /* XXX very sloppy */ this._current_items = {}; @@ -46,10 +53,8 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { } } - var mfield, mclass; - - mfield = _cmf_cache[field_id]; - mclass = _cmc_cache[mfield.field_class]; + var mfield = _cmf_cache[field_id]; + var mclass = _cmc_cache[mfield.field_class]; return mfield.label + " (" + mclass.label + ")"; }, @@ -63,6 +68,8 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { "_prepare_autosuggest_url": function(req) { var term = req.query.term; /* affected by searchAttr on widget */ + var limit = (!isNaN(req.count) && req.count != Infinity) ? + req.count : this.limit; if (!term || term.length < 1 || term == "*") return null; @@ -71,10 +78,10 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { term += " "; term = term.replace(/\*$/, ""); - /* XXX support limit here? the mod_perl interface does */ return "/opac/extras/autosuggest?" + [ "query=" + encodeURI(term), - "search_class=" + this.type_selector.value + "search_class=" + this.type_selector.value, + "limit=" + limit ].join("&"); }, @@ -85,7 +92,7 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { /* string */ attribute, /* anything */ defaultValue) { if (!this.isItem(item)) - throw new AutoSuggestStoreError("getValue(): bad item: " + item); + throw new AutoSuggestStoreError("getValue(): bad item " + item); else if (typeof attribute != "string") throw new AutoSuggestStoreError("getValue(): bad attribute"); @@ -103,16 +110,16 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { "getAttributes": function(/* object */ item) { if (!this.isItem(item)) - throw new AutoSuggestStoreError("getAttributes(): bad arguments"); + throw new AutoSuggestStoreError("getAttributes(): bad args"); else return _autosuggest_fields; }, "hasAttribute": function(/* object */ item, /* string */ attribute) { if (!this.isItem(item) || typeof attribute != "string") { - throw new AutoSuggestStoreError("hasAttribute(): bad arguments"); + throw new AutoSuggestStoreError("hasAttribute(): bad args"); } else { - return (_autosuggest_fields.indexOf(attribute) >= 0); + return (dojo.indexOf(_autosuggest_fields, attribute) >= 0); } }, @@ -137,40 +144,32 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { if (typeof something[cur] == "undefined") return false; } - return true + return true; }, "isItemLoaded": function(/* anything */ something) { - return this.isItem(something); - }, - - "close": function(/* object */ request) { - return; - }, - - "getLabel": function(/* object */ item) { - return "match"; + return this.isItem(something); /* for this store, + items are always loaded */ }, - "getLabelAttributes": function(/* object */ item) { - return ["match"]; - }, + "close": function(/* object */ request) { /* no-op */ return; }, + "getLabel": function(/* object */ item) { return "match"; }, + "getLabelAttributes": function(/* object */ item) { return ["match"]; }, "loadItem": function(/* object */ keywordArgs) { if (!this.isItem(keywordArgs.item)) - throw new AutoSuggestStoreError("that's not an item; can't load it"); + throw new AutoSuggestStoreError("not an item; can't load it"); keywordArgs.identity = this.getIdentity(item); return this.fetchItemByIdentity(keywordArgs); }, "fetch": function(/* request-object */ req) { - // Respect the following properties of the *req* - // object: + // Respect the following properties of the *req* object: // - // query a dojo-style query, which will need modest + // query a dojo-style query, which will need modest // translation for our server-side service - // limit an int + // count an int // onBegin a callback that takes the number of items // that this call to fetch() will return, but // we always give it -1 (i.e. unknown) @@ -185,30 +184,25 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { // callback to the *req* object for the caller's use, but // the one we provide does nothing but issue an alert(). + this._current_items = {}; + var callback_scope = req.scope || dojo.global; var url = this._prepare_autosuggest_url(req); if (!url) { - if (typeof req.onComplete == "function") { - req.onComplete.call( - callback_scope, - openils.Util.objectValues(this._current_items), - req - ); - } + if (typeof req.onComplete == "function") + req.onComplete.call(callback_scope, [], req); return; } - this._current_items = {}; - - /* set up some closures... */ var self = this; + var process_fetch = function(obj, when) { + if (when < self.last_fetch) /* Stale response. Discard. */ + return; - var process_fetch = function(obj) { - dojo.forEach(obj.val, + dojo.forEach( + obj.val, function(item) { - /* XXX may not need this id field ultimately; still - * trying to use it to solve misc problems. */ item.id = item.field + "_" + item.term; item.term = new TermString(item.term, item.field); @@ -235,18 +229,18 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { alert("The 'abort' operation is not supported"); }; - /* ... and proceed. */ - if (typeof req.onBegin == "function") req.onBegin.call(callback_scope, -1, req); + var fetch_time = this.last_fetch = (new Date().getTime()); + dojo.xhrGet({ "url": url, "handleAs": "json", "sync": false, "preventCache": true, "headers": {"Accept": "application/json"}, - "load": process_fetch + "load": function(obj) { process_fetch(obj, fetch_time); } }); /* as for onError: what to do? */ @@ -263,9 +257,7 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { return item.id; }, - "getIdentityAttributes": function(/* object */ item) { - return ["id"]; - }, + "getIdentityAttributes": function(/* object */ item) { return ["id"]; }, "fetchItemByIdentity": function(/* object */ keywordArgs) { if (keywordArgs.identity == undefined) @@ -287,7 +279,8 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { } }, - /* *** This last method is for classes implementing any dojo APIs *** */ + /* *** Classes implementing any Dojo APIs do this to list which + * APIs they're implementing. *** */ "getFeatures": function() { return { -- 2.11.0