fix broken enter/go for search form; also:
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Thu, 19 Jan 2012 23:27:26 +0000 (18:27 -0500)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Mon, 23 Jan 2012 17:27:00 +0000 (12:27 -0500)
    * 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 <lebbeous@esilibrary.com>
Open-ILS/web/js/dojo/openils/AutoSuggestStore.js
Open-ILS/web/opac/skin/default/js/search_bar.js

index aa31242..2f29ad1 100644 (file)
@@ -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);
 
index c78de12..b600603 100644 (file)
@@ -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() {