From 7631741bd830f99ca771af69ba19a283715bfa3a Mon Sep 17 00:00:00 2001 From: Lebbeous Fogle-Weekley Date: Thu, 19 Jan 2012 18:27:26 -0500 Subject: [PATCH] fix broken enter/go for search form; also: * apply class/field from selected suggestions to search_class dropdown so it affects any resulting searches * comments and code cleanup still todo at this time: * use selected search lib for opac visibility testing (optional) * numerical range problem * OU setting or other flag for autosuggest enable/disable Signed-off-by: Lebbeous Fogle-Weekley --- Open-ILS/web/js/dojo/openils/AutoSuggestStore.js | 148 ++++++++--------------- Open-ILS/web/opac/skin/default/js/search_bar.js | 71 +++++++++-- 2 files changed, 111 insertions(+), 108 deletions(-) diff --git a/Open-ILS/web/js/dojo/openils/AutoSuggestStore.js b/Open-ILS/web/js/dojo/openils/AutoSuggestStore.js index aa31242aad..2f29ad15cf 100644 --- a/Open-ILS/web/js/dojo/openils/AutoSuggestStore.js +++ b/Open-ILS/web/js/dojo/openils/AutoSuggestStore.js @@ -9,8 +9,11 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { return "openils.AutoSuggestStore: " + this.message; }; - /* XXX TODO make this smarter */ + function TermString(str, field) { this.str = str; this.field = field; } + TermString.prototype.toString = function() { return this.str; }; + var _autosuggest_fields = ["id", "match", "term", "field"]; + var _cmf_cache, _cmc_cache; dojo.declare( "openils.AutoSuggestStore", null, { @@ -21,29 +24,32 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { }, "_label_field": function(field_id) { - /* It seems to be possible to catch openils.widget.Searcher - * in various states of unreadiness, so we have to be gentle with - * it if we want to ask for its cached cmc and cmf objects. - */ - var cmf_cache, cmc_cache; - try { - cmf_cache = openils.widget.Searcher._cache.obj.cmf; - } catch (E) { - console.log("openils.widget.Searcher cmf cache not ready:" + E); - return field_id; + /* It seems to be possible (well, I think I got it to happen once) + * to catch openils.widget.Searcher while it's still loading. + * So let's be gentle with it when we ask for its cached cmc and + * cmf objects. */ + if (!_cmf_cache) { + try { + _cmf_cache = openils.widget.Searcher._cache.obj.cmf; + } catch (E) { + console.log("o.w.Searcher's cmf cache not ready:" + E); + return field_id; + } } - try { - cmc_cache = openils.widget.Searcher._cache.obj.cmc; - } catch (E) { - console.log("openils.widget.Searcher cmc cache not ready:" + E); - return cmf_cache[field_id].label; + if (!_cmc_cache) { + try { + _cmc_cache = openils.widget.Searcher._cache.obj.cmc; + } catch (E) { + console.log("o.w.Searcher's cmc cache not ready:" + E); + return _cmf_cache[field_id].label; + } } var mfield, mclass; - mfield = cmf_cache[field_id]; - mclass = cmc_cache[mfield.field_class]; + mfield = _cmf_cache[field_id]; + mclass = _cmc_cache[mfield.field_class]; return mfield.label + " (" + mclass.label + ")"; }, @@ -55,9 +61,8 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { ); }, - /* req will have attribute 'query' and possibly 'limit', for now */ "_prepare_autosuggest_url": function(req) { - var term = req.query.term; + var term = req.query.term; /* affected by searchAttr on widget */ if (!term || term.length < 1 || term == "*") return null; @@ -79,23 +84,16 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { /* object */ item, /* string */ attribute, /* anything */ defaultValue) { - // Given an *item* and the name of an *attribute* on that item, - // return that attribute's value. -// console.log("getValue(" + item + ", " + attribute + ")"); if (!this.isItem(item)) throw new AutoSuggestStoreError("getValue(): bad item: " + item); else if (typeof attribute != "string") throw new AutoSuggestStoreError("getValue(): bad attribute"); var value = item[attribute]; - return (typeof value == "undefined") ? defaultValue : value; }, "getValues": function(/* object */ item, /* string */ attribute) { - // Same as getValue(), except the result is always an array - // and there is no way to specify a default value. -// console.log("getValues()"); if (!this.isItem(item) || typeof attribute != "string") throw new AutoSuggestStoreError("bad arguments"); @@ -104,8 +102,6 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { }, "getAttributes": function(/* object */ item) { - // Return an array of all of the given *item*'s *attribute*s. -// console.log("getAttributes()"); if (!this.isItem(item)) throw new AutoSuggestStoreError("getAttributes(): bad arguments"); else @@ -113,9 +109,6 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { }, "hasAttribute": function(/* object */ item, /* string */ attribute) { - // Return true or false based on whether *item* has an - // attribute by the name specified in *attribute*. -// console.log("hasAttribute()"); if (!this.isItem(item) || typeof attribute != "string") { throw new AutoSuggestStoreError("hasAttribute(): bad arguments"); } else { @@ -127,9 +120,6 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { /* object */ item, /* string */ attribute, /* anything */ value) { - // Return true or false based on whether *item* has any value - // matching *value* for *attribute*. -// console.log("containsValue(" + item + ", " + attribute + ", " + value + ")"); if (!this.isItem(item) || typeof attribute != "string") throw new AutoSuggestStoreError("bad data"); else @@ -139,9 +129,6 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { }, "isItem": function(/* anything */ something) { - // Return true if *something* is an item (loaded or not - // because to use everything is loaded, really. -// console.log("isItem(" + something + ")"); if (typeof something != "object" || something === null) return false; @@ -154,34 +141,22 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { }, "isItemLoaded": function(/* anything */ something) { - // Return true if *something* is an item. It's always - // "loaded" in this store. -// console.log("isItemLoaded()"); return this.isItem(something); }, "close": function(/* object */ request) { - // summary: - // This is a no-op. return; }, "getLabel": function(/* object */ item) { - // Return the name of the attribute that should serve as the - // label for objects of the same class as *item*. -// console.log("getLabel(" + item + ")"); return "match"; }, "getLabelAttributes": function(/* object */ item) { -// console.log("getLabelAttributes(" + item + ")"); return ["match"]; }, "loadItem": function(/* object */ keywordArgs) { - // Fully load the item specified in the *item* property of - // *keywordArgs* -// console.log("loadItem(" + dojo.toJson(keywordArgs) + ")"); if (!this.isItem(keywordArgs.item)) throw new AutoSuggestStoreError("that's not an item; can't load it"); @@ -190,32 +165,25 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { }, "fetch": function(/* request-object */ req) { - // Basically, fetch objects matching the *query* property of - // the *req* parameter. + // Respect the following properties of the *req* + // object: // - // Translate the *query* into a call we make to the - // autosuggest webserver module, which yields JSON for us, - // and translate those results into items, storing them - // in our internal cache. + // query a dojo-style query, which will need modest + // translation for our server-side service + // limit 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) + // onItem a callback that takes each item as we get it + // onComplete a callback that takes the list of items + // after they're all fetched // - // We also respect the following properties of the *req* - // object (all optional): + // The onError callback is ignored for now (haven't thought + // of anything useful to do with it yet). // - // limit 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) - // onItem a callback that takes each item as we get it - // onComplete a callback that takes the list of items - // after they're all fetched - // - // The onError callback is ignored for now (haven't thought - // of anything useful to do with it yet). - // - // The Read API also charges this method with adding an abort - // callback to the *req* object for the caller's use, but - // the one we provide does nothing but issue an alert(). - //console.log("fetch(" + dojo.toJson(openils.Util.objectProperties(req)) + ")"); + // The Read API also charges this method with adding an abort + // callback to the *req* object for the caller's use, but + // the one we provide does nothing but issue an alert(). var callback_scope = req.scope || dojo.global; var url = this._prepare_autosuggest_url(req); @@ -237,11 +205,12 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { var self = this; var process_fetch = function(obj) { - obj.val.forEach( + dojo.forEach(obj.val, function(item) { - /* XXX May not need this id field ultimately; still + /* 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); item.match = self._prepare_match_for_display( item.match, item.field @@ -271,16 +240,14 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { if (typeof req.onBegin == "function") req.onBegin.call(callback_scope, -1, req); - dojo.xhrGet( - { - "url": url, - "handleAs": "json", - "sync": false, - "preventCache": true, - "headers": {"Accept": "application/json"}, - "load": process_fetch - } - ); + dojo.xhrGet({ + "url": url, + "handleAs": "json", + "sync": false, + "preventCache": true, + "headers": {"Accept": "application/json"}, + "load": process_fetch + }); /* as for onError: what to do? */ @@ -290,9 +257,6 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { /* *** Begin dojo.data.api.Identity methods *** */ "getIdentity": function(/* object */ item) { - // Given an *item* return its unique identifier (the value - // of its primary key). -// console.log("getIdentity(" + item + " [" + item.id + "])"); if (!this.isItem(item)) throw new AutoSuggestStoreError("not an item"); @@ -300,14 +264,10 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { }, "getIdentityAttributes": function(/* object */ item) { - // Given an *item* return the list of the name of the fields - // that constitute the item's unique identifier. -// console.log("getIdentityAttributes()"); return ["id"]; }, "fetchItemByIdentity": function(/* object */ keywordArgs) { -// console.log("fetchItemByIdentity(" + dojo.toJson(keywordArgs) + ")"); if (keywordArgs.identity == undefined) return null; // Identity API spec unclear whether error callback // would need to be run, so we won't. @@ -315,10 +275,6 @@ if (!dojo._hasResource["openils.AutoSuggestStore"]) { var item; if (item = this._current_items[keywordArgs.identity]) { -// console.log( -// "fetchItemByIdentity(): already have " + -// keywordArgs.identity -// ); if (typeof keywordArgs.onItem == "function") keywordArgs.onItem.call(callback_scope, item); diff --git a/Open-ILS/web/opac/skin/default/js/search_bar.js b/Open-ILS/web/opac/skin/default/js/search_bar.js index c78de1229b..b60060336e 100644 --- a/Open-ILS/web/opac/skin/default/js/search_bar.js +++ b/Open-ILS/web/opac/skin/default/js/search_bar.js @@ -16,21 +16,68 @@ var newSearchDepth = null; dojo.require("dijit.form.ComboBox"); dojo.require("openils.AutoSuggestStore"); +function updateSearchTypeSelector(id) { + /* Fail somewhat gracefully if a race condition, which I'm not /certain/ + * actually exists, lets us get here before openils.widget.Search has + * fully initialized. */ + var f; + try { + f = openils.widget.Searcher._cache.obj.cmf[id]; + } catch (E) { + console.log("o.w.Searcher couldn't help us with field #" + id); + return; + } + + var selector = G.ui.searchbar.type_selector; + var search_class = f.field_class + "|" + f.name; + var exact = dojo.indexOf( + dojo.map(selector.options, function(o) { return o.value; }), + search_class + ); + + if (exact > 0) { + selector.selectedIndex = exact; + } else { /* settle for class match if we can get it */ + for (var i = 0; i < selector.options.length; i++) { + /* XXX make sure IE can handle this syntax */ + if (selector.options[i].value.split("|")[0] == f.field_class) { + selector.selectedIndex = i; + break; + } + } + } +} + function autoSuggestInit() { var as_store = new openils.AutoSuggestStore( - {"type_selector": G.ui.searchbar.type_selector} + { + "type_selector": G.ui.searchbar.type_selector + } ); - var widg = new dijit.form.ComboBox({ - "store": as_store, - "labelAttr": "match", - "labelType": "html", - "searchAttr": "term", - "hasDownArrow": false, - "autoComplete": false, - "style": dojo.attr("search_box", "style") - }, "search_box"); - - widg.validate = function() { return true; } /* sic! */ + + var widg = new dijit.form.ComboBox( + { + "store": as_store, + "labelAttr": "match", + "labelType": "html", + "searchAttr": "term", + "hasDownArrow": false, + "autoComplete": false, + "searchDelay": 200, + "onChange": function(value) { + if (typeof value.field == "number") + updateSearchTypeSelector(value.field); + }, + "onKeyPress": function(event) { + if (event.charOrCode == dojo.keys.ENTER) + searchBarSubmit(); + }, + "style": dojo.attr("search_box", "style") + }, "search_box" + ); + + G.ui.searchbar.text = widg.textbox; + widg.focus(); } function searchBarInit() { -- 2.11.0