From 5c8b1c20f7892c6988fb370dde18eca987b4abc6 Mon Sep 17 00:00:00 2001 From: miker Date: Thu, 29 Jul 2010 21:08:36 +0000 Subject: [PATCH] This patch replaces the result page sidebar new-search trees (implemented before "faceting" became a thing, and designed for new-search, not drill-down) with and implementation of what has become the generally accepted "way to do faceting". This is implemented as a Dojo Dijit, and the markup and code footprints within the OPAC proper are small, just a couple
s and a little bit of glue code to help the facet rendering engine find the data it needs. The set and order of facet classes to display in a given facet bar (title, author, subject, etc) is configurable in the markup, as are the number of values displayed for each facet when collapsed (before the [More...] button is pushed). Also included is a Searcher Dijit which implements an embeddable advanced search interface. This is mainly useful for constructing boolean-chained searches (we support AND and OR now, though they're spelled && and || in the query string), and has the beginnings of support for filters and modifiers. git-svn-id: svn://svn.open-ils.org/ILS/trunk@17057 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/examples/fm_IDL.xml | 6 +- .../Application/Storage/Publisher/metabib.pm | 2 + .../src/perlmods/OpenILS/Application/SuperCat.pm | 3 + Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm | 2 +- Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js | 2 +- .../web/js/dojo/openils/widget/FacetSidebar.js | 223 +++++++++ Open-ILS/web/js/dojo/openils/widget/Searcher.js | 543 +++++++++++++++++++++ .../web/js/dojo/openils/widget/nls/Searcher.js | 42 ++ Open-ILS/web/opac/common/js/config.js | 2 + Open-ILS/web/opac/common/js/opac_utils.js | 6 +- Open-ILS/web/opac/locale/en-US/opac.dtd | 1 + Open-ILS/web/opac/skin/default/js/rdetail.js | 6 +- Open-ILS/web/opac/skin/default/js/result_common.js | 6 +- Open-ILS/web/opac/skin/default/js/search_bar.js | 5 +- Open-ILS/web/opac/skin/default/js/sidebar.js | 2 + .../web/opac/skin/default/xml/common/js_common.xml | 2 +- .../web/opac/skin/default/xml/common/searchbar.xml | 36 +- .../web/opac/skin/default/xml/common/sidebar.xml | 67 +-- .../skin/default/xml/rdetail/rdetail_summary.xml | 6 +- Open-ILS/web/opac/theme/default/css/colors.css | 38 +- 20 files changed, 929 insertions(+), 71 deletions(-) create mode 100644 Open-ILS/web/js/dojo/openils/widget/FacetSidebar.js create mode 100644 Open-ILS/web/js/dojo/openils/widget/Searcher.js create mode 100644 Open-ILS/web/js/dojo/openils/widget/nls/Searcher.js diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index 6b64b3f47..bb4fd5836 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -1510,7 +1510,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -1530,7 +1530,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -1549,7 +1549,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/metabib.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/metabib.pm index 22cf19357..10f223ebf 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/metabib.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/metabib.pm @@ -3092,6 +3092,8 @@ sub query_parser_fts_wrapper { my $query = $base_query; $log->debug("Full base query: $base_query", DEBUG); + $query = "$args{facets} $query" if ($args{facets}); + if (!$locale_map{COMPLETE}) { my @locales = config::i18n_locale->search_where({ code => { '<>' => '' } }); diff --git a/Open-ILS/src/perlmods/OpenILS/Application/SuperCat.pm b/Open-ILS/src/perlmods/OpenILS/Application/SuperCat.pm index d80ddc489..d02a5cf84 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/SuperCat.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/SuperCat.pm @@ -1862,6 +1862,9 @@ Returns the XML representation of the requested bibliographic record's holdings { name => 'orgUnit', desc => 'An OpenILS actor::org_unit short name that limits the scope of returned holdings', type => 'text' }, + { name => 'depth', + desc => 'An OpenILS actor::org_unit_type depththat limits the scope of returned holdings', + type => 'number' }, { name => 'hideCopies', desc => 'Flag that prevents the inclusion of copies in the returned holdings', type => 'boolean' }, diff --git a/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm b/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm index a9eaa90e8..a38bcf84b 100644 --- a/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm +++ b/Open-ILS/src/perlmods/OpenILS/WWW/SuperCat.pm @@ -367,7 +367,7 @@ sub unapi { if ($uri =~ m{^tag:[^:]+:([^\/]+)/([^\/[]+)(?:\[([0-9,]+)\])?(?:/(.+))?}o) { $id = $2; $paging = $3; - $lib = uc($4); + ($lib,$depth) = split('/', $4); $type = 'record'; $type = 'metarecord' if ($1 =~ /^m/o); $type = 'authority' if ($1 =~ /^authority/o); diff --git a/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js b/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js index 54ff091d4..9471b8125 100644 --- a/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js +++ b/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js @@ -182,7 +182,7 @@ if(!dojo._hasResource["fieldmapper.Fieldmapper"]){ if (dojo.isObject(params)) { args = params; } else { - args.params = [].splice.call(arguments, 1, arguments.length - 1); + args.params = [].splice.call(arguments, 2, arguments.length - 2); } } diff --git a/Open-ILS/web/js/dojo/openils/widget/FacetSidebar.js b/Open-ILS/web/js/dojo/openils/widget/FacetSidebar.js new file mode 100644 index 000000000..19d69f05f --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/widget/FacetSidebar.js @@ -0,0 +1,223 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2010 Equinox Software, Inc + * Mike Rylander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource["openils.widget.FacetSidebar"]) { + + dojo._hasResource["openils.widget.FacetSidebar"] = true; + dojo.provide("openils.widget.FacetSidebar"); + dojo.require("openils.widget.Searcher"); + + dojo.declare( + 'openils.widget.FacetSidebar', + [dijit._Widget, dijit._Templated], + { + + templateString : '
', + widgetsInTemplate: false, + + facetData : {}, + facetCacheKey : '', + searchBox : '', + classOrder : null, + searchSubmit : '', + facetLimit : 10, + + startup : function () { + this.populate(); + this.inherited(arguments) + }, + + populate : function () {}, + + render : function () { + if (!this.facetCacheKey) return; + + if (openils.widget.Searcher._cache.facetData) { + this.facetData = openils.widget.Searcher._cache.facetData; + } else { + var facetData = fieldmapper.standardRequest( [ 'open-ils.search', 'open-ils.search.facet_cache.retrieve'], this.facetCacheKey ); + if (!facetData) return; + this.facetData = openils.widget.Searcher._cache.facetData = facetData; + } + + var classes = openils.widget.Searcher._cache.arr.cmc; + if (this.classOrder && this.classOrder.length > 0) { + classes = []; + dojo.forEach( + this.classOrder, + function(x) { classes.push({name:x}); } + ); + } + + var me = this; + dojo.forEach( + classes, + function (x) { + var possible_facets = dojo.filter( + openils.widget.Searcher._cache.arr.cmf, + function (y) { + if (y.field_class == x.name && facetData[y.id]) return 1; + return 0; + } + ); + if (possible_facets.length > 0) me.addClass( x.name, possible_facets ); + } + ); + }, + + addClass : function (thisclass, facets) { + return new openils.widget.FacetSidebar.facetClass( + { facetLimit: this.facetLimit, searchBox : this.searchBox, searchSubmit : this.searchSubmit, facetClass : thisclass, facetData : this.facetData, facetList : facets } + ).placeAt(this.facetClasses, 'last'); + } + } + ); + + dojo._hasResource["openils.widget.FacetSidebar.facetClass"] = true; + dojo.provide("openils.widget.FacetSidebar.facetClass"); + + dojo.declare( + 'openils.widget.FacetSidebar.facetClass', + [dijit._Widget, dijit._Templated], + { + + templateString : +'
' + +'
' + +'
' + +'
', + widgetsInTemplate: false, + + facetLimit : 10, + facetClass : '', + facetData : null, + facetList : null, + searchBox : '', + searchSubmit : '', + + postCreate : function () { + if (!this.facetClass) return; + if (!this.facetData) return; + + var myclass = this.facetClass; + + var fclass = dojo.filter(openils.widget.Searcher._cache.arr.cmc, function (x) { if (x.name == myclass) return 1; return 0; })[0]; + this.facetClassLabel.innerHTML = fclass.label; + + var me = this; + dojo.forEach( + this.facetList, + function (f) { me.addFacets(f); } + ); + }, + + addFacets : function (f) { + return new openils.widget.FacetSidebar.facetField( + { facetLimit: this.facetLimit, searchBox : this.searchBox, searchSubmit : this.searchSubmit, facet : f, facetData : this.facetData[f.id] } + ).placeAt( this.facetFields, 'last' ); + } + } + ); + + dojo._hasResource["openils.widget.FacetSidebar.facetField"] = true; + dojo.provide("openils.widget.FacetSidebar.facetField"); + + dojo.declare( + 'openils.widget.FacetSidebar.facetField', + [dijit._Widget, dijit._Templated], + { + + templateString : +'
' + +'
' + +'
' + +'
' + +'
' + +'
', + + widgetsInTemplate: true, + facet : null, + facetData : null, + facetLimit : 10, + searchBox : '', + searchSubmit : '', + extraHidden : true, + + postCreate : function () { + this.nls = dojo.i18n.getLocalization("openils.widget", "Searcher"); + var me = this; + var keylist = []; for (var i in this.facetData) { keylist.push(i); } + + keylist = keylist.sort(function(a,b){ + if (me.facetData[a] < me.facetData[b]) return 1; + if (me.facetData[a] > me.facetData[b]) return -1; + if (a < b) return -1; + if (a > b) return 1; + return 0; + }); + + this.facetLabel.innerHTML = this.facet.label; + this.toggleExtraFacetFields.setLabel(this.nls.more); + + var pos = 0; + dojo.forEach( + keylist, + function(value){ + + var have_it = dojo.byId(me.searchBox).value.indexOf(me.facet.field_class + '|' + me.facet.name + '[' + value + ']') > -1; + + var container = dojo.create('div',{'class':'facetFieldLine'}); + dojo.create('span',{'class':'facetFieldLineCount', innerHTML: me.facetData[value]},container); + + if (have_it) { + dojo.create('a',{href : '#', 'class':'facetFieldLineValue', onclick : function(){ me.undoSearch(value); return false;}, innerHTML: '(' + value + ')'},container); + } else { + dojo.create('a',{href : '#', 'class':'facetFieldLineValue', onclick : function(){ me.doSearch(value); return false;}, innerHTML: value},container); + } + + if (pos >= me.facetLimit) dojo.place(container,me.extraFacetFields,'last'); + else dojo.place(container,me.facetFields,'last'); + + pos++; + } + ); + + if (pos < me.facetLimit + 1) dojo.query(this.toggleExtraFacetFieldsWrapper).toggleClass('hide_me'); + + }, + + toggleExtraFacets : function () { + dojo.query(this.extraFacetFields).toggleClass('hide_me'); + this.extraHidden = !this.extraHidden; + this.extraHidden ? this.toggleExtraFacetFields.setLabel(this.nls.more) : this.toggleExtraFacetFields.setLabel(this.nls.less); + }, + + undoSearch : function (value) { + var sb = dojo.byId(this.searchBox); + sb.value = sb.value.replace(this.facet.field_class + '|' + this.facet.name + '[' + value + ']',''); + dojo.byId(this.searchSubmit).click(); + }, + + doSearch : function (value) { + dojo.byId(this.searchBox).value += ' ' + this.facet.field_class + '|' + this.facet.name + '[' + value + ']'; + dojo.byId(this.searchSubmit).click(); + } + } + ); + + +} + diff --git a/Open-ILS/web/js/dojo/openils/widget/Searcher.js b/Open-ILS/web/js/dojo/openils/widget/Searcher.js new file mode 100644 index 000000000..d2fa58b8a --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/widget/Searcher.js @@ -0,0 +1,543 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2010 Equinox Software, Inc + * Mike Rylander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource["openils.widget.Searcher"]) { + + dojo._hasResource["openils.widget.Searcher"] = true; + dojo.provide("openils.widget.Searcher"); + + dojo.require("fieldmapper.AutoIDL"); + dojo.require("openils.I18N"); + dojo.require("fieldmapper.dojoData"); + dojo.require("DojoSRF"); + dojo.require("dojo.data.ItemFileReadStore"); + dojo.require("dijit._Widget"); + dojo.require("dijit._Templated"); + dojo.require("dijit.form.Button"); + dojo.require("dijit.form.TextBox"); + dojo.require("dijit.form.FilteringSelect"); + dojo.requireLocalization("openils.widget", "Searcher"); + + fieldmapper.IDL.load(['cmf','cmc','cmsa']); + +//------ searching wrapper, uses dijits below ----------------------------------------------- + + dojo.declare( + 'openils.widget.Searcher', + [dijit._Widget, dijit._Templated], + { + + templateString : +"
" + + +"
" + +"
" + +" " + +"
" + + +"
" + +"
" + +"
" + +" " + +"
" + +"
" + +" " + +" " + +"
" + +"
" + + +"
" + +"
" + +" " + +"
" + +" " + +" " + +" " + +"
" + + +"
" + +" " + +" " + +" " + +"
" + +"
" + +" " + +"
" + +" " + +"
" + +"
" + +" " + +"
" + +" " + +"
" + +"
" + + +"
" + +" " + +"
" + +"
" + +"
" + + +"
" + +" " + +"
" + +"
", + widgetsInTemplate: true, + advanced : false, + basicTextBox : '', + facetTextBox : '', + withClassedSearches : true, + withFacetedSearches : false, + withFiltersModifiers : false, + withFilters : false, + withModifiers : false, + basic_query : '', + facet_query : '', + + compileFullSearch : function () { + if (!this.advanced) return dojo.byId( this.basicTextBox ).attr('value'); + + var query = ''; + + var first_cs = true; + + // First, classed searches + dojo.query( '.classedSearch', this.classedSearches ).forEach( + function (csearch) { + csearch = dijit.byNode(csearch); + var part = csearch.searchValue.attr('value'); + if (!part || part.match(/^\s+$/)) return; + + if (first_cs) first_cs = false; + else query += csearch.anded ? '' : ' ||'; + + query += ' ' + csearch.matchIndex.attr('value') + ':'; + if (csearch.matchType.attr('value') == 'exact') { + query += '"'; + } else if (csearch.matchType.attr('value') == 'notcontains') { + part = '-' + part.replace(/\s+/g, ' -'); + } + + query += part; + + if (csearch.matchType.attr('value') == 'exact') query += '"'; + } + ); + + var fquery = ''; + // Now facets + dojo.query( '.facetedSearch', this.facetedSearches ).forEach( + function (facet) { + facet = dijit.byNode(facet); + var part = facet.searchValue.attr('value'); + if (!part || part.match(/^\s+$/)) return; + fquery += ' ' + facet.matchIndex.attr('value') + '[' + part + ']'; + } + ); + + // and filters... + dojo.query( '.filter', this.filters ).forEach( + function (filt) { + filt = dijit.byNode(filt); + var part = filt.valueList.attr('value'); + if (!part || part.match(/^\s+$/) || filt.filterSelect.attr('value') == '-') return; + query += ' ' + filt.filterSelect.attr('value') + '(' + part + ')'; + } + ); + + // finally, modifiers + dojo.query( '.modifier', this.modifiers ).forEach( + function (modifier) { + modifier = dijit.byNode(modifier); + var part = modifier.modifierSelect.attr('value') + if (!part || part == '-') return; + query += ' #' + part; + } + ); + + + this.basic_query = query; + this.facet_query = fquery; + + return query; + }, + + addFilter : function () { + return new openils.widget.Searcher.filter({}, dojo.create('div',null,this.filters)); + }, + + addModifier : function () { + return new openils.widget.Searcher.modifier({}, dojo.create('div',null,this.modifiers)); + }, + + addClassAnd : function () { + return new openils.widget.Searcher.classedSearch({}, dojo.create('div',null,this.classedSearches)); + }, + + addClassOr : function () { + return new openils.widget.Searcher.classedSearch({anded:false}, dojo.create('div',null,this.classedSearches)); + }, + + addFacet : function () { + return new openils.widget.Searcher.facetedSearch({}, dojo.create('div',null,this.facetedSearches)); + }, + + performSearch : function () {}, + populate : function () {}, + + startup : function () { + if (this.advanced) { + dojo.query(this.basicSearch).toggleClass('hide_me'); + dojo.query(this.advSearch).toggleClass('hide_me'); + } + + if (!this.facetTextBox) this.facetTextBox = this.basicTextBox; + + if (this.withFiltersModifiers) { + this.withFilters = true; + this.withModifiers = true; + } else if (this.withFilters || this.withModifiers) { + this.withFiltersModifiers = true; + } + + if (this.withFiltersModifiers) dojo.query(this.searcherFilterModifierContainer).toggleClass('hide_me'); + if (this.withFilters) dojo.query(this.searcherFilterContainer).toggleClass('hide_me'); + if (this.withModifiers) dojo.query(this.searcherModifierContainer).toggleClass('hide_me'); + if (this.withFacetedSearches) dojo.query(this.searcherFacetedSearchesContainer).toggleClass('hide_me'); + if (this.withClassedSearches) dojo.query(this.searcherClassedSearchesContainer).toggleClass('hide_me'); + + this.populate(); + this.inherited(arguments) + }, + + swapDeck : function () { + if (this.advanced) this.advanced = false; + else this.advanced = true; + + dojo.query(this.basicSearch).toggleClass('hide_me'); + dojo.query(this.advSearch).toggleClass('hide_me'); + + if (this.advanced) this.advToggle.setLabel(this.nls.basic); + else this.advToggle.setLabel(this.nls.advanced); + }, + + postCreate : function () { + this.nls = dojo.i18n.getLocalization("openils.widget", "Searcher"); + + this.searcherClassedSearchesLabel.innerHTML = this.nls.classed_searches; + this.searcherFacetedSearchesLabel.innerHTML = this.nls.faceted_searches; + this.searcherFiltersLabel.innerHTML = this.nls.filters; + this.searcherModifiersLabel.innerHTML = this.nls.modifiers; + this.addFacetedSearch.setLabel(this.nls.new_facet); + + this.andClassedSearch.setLabel(this.nls.and); + this.orClassedSearch.setLabel(this.nls.or); + + this.addFilterButton.setLabel(this.nls.new_filter); + this.addModifierButton.setLabel(this.nls.new_modifier); + + this.goButton.setLabel(this.nls.perform_search); + + if (this.advanced) this.advToggle.setLabel(this.nls.basic); + else this.advToggle.setLabel(this.nls.advanced); + + new openils.widget.Searcher.classedSearch( + { noRemove : true }, + dojo.create('div',null,this.classedSearches) + ); + } + + } + ); + + openils.widget.Searcher._cache = {arr : {}, obj : {}, store : {}}; + + dojo.forEach( + [ {ident:'name',classname:'cmc',label:'label'}, {ident:'id',classname:'cmf',label:'label'}, {ident:'alias',classname:'cmsa',label:'alias'} ], + function (c) { + + var q = {}; + q[c.ident] = { '!=' : null }; + + var fielder_result = fieldmapper.standardRequest( + [ 'open-ils.fielder', 'open-ils.fielder.'+c.classname+'.atomic'], + [ { query : q } ] + ); + + var sorted_fielder_result = fielder_result.sort( function(a,b) { + if(a[c.label] > b[c.label]) return 1; + if(a[c.label] < b[c.label]) return -1; + return 0; + }); + + openils.widget.Searcher._cache.arr[c.classname] = sorted_fielder_result; + + var list = []; + openils.widget.Searcher._cache.obj[c.classname] = {}; + + dojo.forEach( + openils.widget.Searcher._cache.arr[c.classname], + function (x) { + openils.widget.Searcher._cache.obj[c.classname][x[c.ident]] = x; + list.push(x); + } + ); + + openils.widget.Searcher._cache.store[c.classname] = new dojo.data.ItemFileReadStore( { data : {identifier : c.ident, label : c.label, items : list } } ); + } + ); + + var facet_list = []; + var search_list = []; + + dojo.forEach( + openils.widget.Searcher._cache.arr.cmc, + function (cmc_obj) { + search_list.push({ + name : cmc_obj.name, + label : cmc_obj.label + }); + dojo.forEach( + dojo.filter( + openils.widget.Searcher._cache.arr.cmf, + function (x) { + if (x.field_class == cmc_obj.name) return 1; + return 0; + } + ), + function (cmf_obj) { + if (cmf_obj.search_field == 't') { + search_list.push({ + name : cmc_obj.name + '|' + cmf_obj.name, + label : ' -- ' + cmf_obj.label + }); + } + if (cmf_obj.facet_field == 't') { + facet_list.push({ + name : cmc_obj.name + '|' + cmf_obj.name, + label : cmc_obj.label + ' : ' + cmf_obj.label + }); + } + } + ) + } + ); + + openils.widget.Searcher.facetStore = new dojo.data.ItemFileReadStore( { data : {identifier : 'name', label : 'label', items : facet_list} } ); + openils.widget.Searcher.searchStore = new dojo.data.ItemFileReadStore( { data : {identifier : 'name', label : 'label', items : search_list} } ); + +//------ modifiers template ----------------------------------------------- + + dojo._hasResource["openils.widget.Searcher.modifier"] = true; + dojo.provide("openils.widget.Searcher.modifier"); + + dojo.declare( + 'openils.widget.Searcher.modifier', + [dijit._Widget, dijit._Templated], + { + + templateString : +'' + +' ' + +' ' + +'
' + +' ' + +'
', + widgetsInTemplate: true, + + postCreate : function () { + this.nls = dojo.i18n.getLocalization("openils.widget", "Searcher"); + + this.modifierDefault.innerHTML = this.nls.modifier_default; + this.modifierAvailable.innerHTML = this.nls.available; + this.modifierDescending.innerHTML = this.nls.descending; + this.modifierAscending.innerHTML = this.nls.ascending; + this.modifierMetabib.innerHTML = this.nls.metabib; + this.modifierStaff.innerHTML = this.nls.staff; + + this.removeButton.setLabel(this.nls.remove); + }, + + killMe : function () { + dijit.byNode(this.myTop).destroyRecursive(); + this.destroy(); + } + } + ); + +//------ filters template ----------------------------------------------- + + dojo._hasResource["openils.widget.Searcher.filter"] = true; + dojo.provide("openils.widget.Searcher.filter"); + + dojo.declare( + 'openils.widget.Searcher.filter', + [dijit._Widget, dijit._Templated], + { + + templateString : +'' + +' ' + +' ' + +' ' + +'
' + +' ' + +'
', + widgetsInTemplate: true, + + postCreate : function () { + this.nls = dojo.i18n.getLocalization("openils.widget", "Searcher"); + + this.filterDefault.innerHTML = this.nls.filter_default; + this.filterSite.innerHTML = this.nls.site; + this.filterDepth.innerHTML = this.nls.depth; + this.filterSort.innerHTML = this.nls.sort; + this.filterStatuses.innerHTML = this.nls.statuses; + this.filterAudience.innerHTML = this.nls.audience; + this.filterBefore.innerHTML = this.nls.before; + this.filterAfter.innerHTML = this.nls.after; + this.filterBetween.innerHTML = this.nls.between; + this.filterDuring.innerHTML = this.nls.during; + this.filterForm.innerHTML = this.nls.item_form; + this.filterType.innerHTML = this.nls.item_type; + this.filterTypeForm.innerHTML = this.nls.format; + this.filterVRFormat.innerHTML = this.nls.vr_format; + this.filterLitForm.innerHTML = this.nls.lit_form; + this.filterBibLevel.innerHTML = this.nls.bib_level; + + this.removeButton.setLabel(this.nls.remove); + }, + + killMe : function () { + dijit.byNode(this.myTop).destroyRecursive(); + this.destroy(); + } + } + ); + +//------ facet search template ----------------------------------------------- + + dojo._hasResource["openils.widget.Searcher.facetedSearch"] = true; + dojo.provide("openils.widget.Searcher.facetedSearch"); + + dojo.declare( + 'openils.widget.Searcher.facetedSearch', + [dijit._Widget, dijit._Templated], + { + + templateString : +'' + +' ' + +' ' + +' ' + +' ' + +'
', + widgetsInTemplate: true, + + noRemove : false, + + postCreate : function () { + this.nls = dojo.i18n.getLocalization("openils.widget", "Searcher"); + + this.exactOption.innerHTML = this.nls.exact; + this.removeButton.setLabel(this.nls.remove); + + if (this.noRemove) dojo.destroy(this.removeWrapper); + }, + + killMe : function () { + dijit.byNode(this.myTop).destroyRecursive(); + this.destroy(); + } + } + ); + +//------ classed search template ----------------------------------------------- + + dojo._hasResource["openils.widget.Searcher.classedSearch"] = true; + dojo.provide("openils.widget.Searcher.classedSearch"); + + dojo.declare( + 'openils.widget.Searcher.classedSearch', + [dijit._Widget, dijit._Templated], + { + + templateString : +'' + +' ' + +' ' + +' ' + +' ' + +' ' + +'
' + +' ' + +'
', + widgetsInTemplate: true, + + anded : true, + noRemove : false, + + postCreate : function () { + this.nls = dojo.i18n.getLocalization("openils.widget", "Searcher"); + + this.containsOption.innerHTML = this.nls.contains; + this.notContainsOption.innerHTML = this.nls.notcontains; + this.exactOption.innerHTML = this.nls.exact; + + this.removeButton.setLabel(this.nls.remove); + + if (this.noRemove) dojo.destroy(this.removeWrapper); + if (!this.noRemove && this.anded) this.joinerSpan.innerHTML = this.nls.and; + if (!this.noRemove && !this.anded) this.joinerSpan.innerHTML = this.nls.or; + }, + + killMe : function () { + dojo.destroy(this.myTop); + this.destroy(); + } + } + ); + + +} diff --git a/Open-ILS/web/js/dojo/openils/widget/nls/Searcher.js b/Open-ILS/web/js/dojo/openils/widget/nls/Searcher.js new file mode 100644 index 000000000..9cd0c8773 --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/widget/nls/Searcher.js @@ -0,0 +1,42 @@ +{ + and : '&&', + or : '||', + more : 'More...', + less : '...Less', + classed_searches : 'Classed Searches', + faceted_searches : 'Facets', + filters : 'Filters', + modifiers : 'Modifiers', + new_facet : 'New Facet', + new_filter : 'New Filter', + new_modifier : 'New Modifier', + remove : 'Remove', + advanced : 'Advanced', + basic : 'Basic', + perform_search : 'Go!', + contains : 'Contains', + notcontains : 'Does Not Contain', + exact : 'Exactly Matches', + modifier_default : '-- Select a Modifier --', + available : 'Available', + descending : 'Sort Descending', + ascending : 'Sort Ascending', + metabib : 'Metarecord Search', + staff : 'Staff Search', + filter_default : '-- Select a Filter --', + site : 'Site', + depth : 'Search Depth', + sort : 'Sort Axis', + statuses : 'Statuses', + audience : 'Audience', + before : 'Published Before', + after : 'Published After', + between : 'Published Between', + during : 'In Publication', + item_form : 'Form', + item_type : 'Type', + format : 'Type and Form', + vr_format : 'Videorecording Format', + lit_form : 'Literary Form', + bib_level : 'Bibliographic Level' +} diff --git a/Open-ILS/web/opac/common/js/config.js b/Open-ILS/web/opac/common/js/config.js index 50aee7512..45fe0c436 100644 --- a/Open-ILS/web/opac/common/js/config.js +++ b/Open-ILS/web/opac/common/js/config.js @@ -9,6 +9,7 @@ var STAFF_WEB_BASE_PATH = '/eg'; // root of the web-based staff interfaces /* URL param names */ var PARAM_TERM = "t"; /* search term */ +var PARAM_FACET = "ft"; /* facet term */ var PARAM_STYPE = "tp"; /* search type */ var PARAM_LOCATION = "l"; /* current location */ var PARAM_LASSO = "sg"; /* current location */ @@ -49,6 +50,7 @@ var PARAM_NOPERSIST_SEARCH = 'nps'; /* URL param values (see comments above) */ var TERM; +var FACET; var STYPE; var LOCATION; var LASSO; diff --git a/Open-ILS/web/opac/common/js/opac_utils.js b/Open-ILS/web/opac/common/js/opac_utils.js index 09163e68d..b6d89ec7c 100644 --- a/Open-ILS/web/opac/common/js/opac_utils.js +++ b/Open-ILS/web/opac/common/js/opac_utils.js @@ -140,6 +140,7 @@ function initParams() { if(isNaN(DEPTH)) DEPTH = null; + FACET = cgi.param(PARAM_FACET); TERM = cgi.param(PARAM_TERM); STYPE = cgi.param(PARAM_STYPE); FORM = cgi.param(PARAM_FORM); @@ -232,6 +233,7 @@ function initCookies() { /* URL param accessors */ function getTerm(){return TERM;} +function getFacet(){return FACET;} function getStype(){return STYPE;} function getLocation(){return LOCATION;} function getLasso(){return LASSO;} @@ -376,6 +378,8 @@ function buildOPACLink(args, slim, ssl) { string += _appendParam(ORIGLOC, PARAM_ORIGLOC, args, getOrigLocation, string); if(getTerm()) string += _appendParam(TERM, PARAM_TERM, args, getTerm, string); + if(getFacet()) + string += _appendParam(FACET, PARAM_FACET, args, getFacet, string); if(getStype()) string += _appendParam(STYPE, PARAM_STYPE, args, getStype, string); if(getLocation() != 1) @@ -504,7 +508,7 @@ function buildTitleDetailLink(rec, link) { function buildSearchLink(type, string, linknode, trunc) { if(!trunc) trunc = 65; var args = {}; - if( SHOW_MR_DEFAULT) { + if( SHOW_MR_DEFAULT || findCurrentPage() == MRESULT ) { args.page = MRESULT; } else { args.page = RRESULT; diff --git a/Open-ILS/web/opac/locale/en-US/opac.dtd b/Open-ILS/web/opac/locale/en-US/opac.dtd index 9c74dd3f9..d1086ad5b 100644 --- a/Open-ILS/web/opac/locale/en-US/opac.dtd +++ b/Open-ILS/web/opac/locale/en-US/opac.dtd @@ -373,6 +373,7 @@ Please see a librarian to renew your account."> + diff --git a/Open-ILS/web/opac/skin/default/js/rdetail.js b/Open-ILS/web/opac/skin/default/js/rdetail.js index 2455b2931..aa78f1cf7 100644 --- a/Open-ILS/web/opac/skin/default/js/rdetail.js +++ b/Open-ILS/web/opac/skin/default/js/rdetail.js @@ -464,9 +464,9 @@ function _rdetailDraw(r) { breq.callback( rdetailCheckDeleted ); breq.send(); - resultBuildCaches( [ record ] ); - resultDrawSubjects(); - resultDrawSeries(); + //resultBuildCaches( [ record ] ); + //resultDrawSubjects(); + //resultDrawSeries(); // grab added content diff --git a/Open-ILS/web/opac/skin/default/js/result_common.js b/Open-ILS/web/opac/skin/default/js/result_common.js index 1ae9074c5..6510750bf 100644 --- a/Open-ILS/web/opac/skin/default/js/result_common.js +++ b/Open-ILS/web/opac/skin/default/js/result_common.js @@ -13,9 +13,7 @@ if( findCurrentPage() == MRESULT || findCurrentPage() == RRESULT ) { G.evt.result.hitCountReceived.push(resultSetHitInfo); G.evt.result.recordReceived.push(resultDisplayRecord, resultAddCopyCounts); G.evt.result.copyCountsReceived.push(resultDisplayCopyCounts); - G.evt.result.allRecordsReceived.push(resultBuildCaches, resultDrawSubjects, - resultDrawAuthors, resultDrawSeries, function(){unHideMe($('result_info_2'))}, - fetchGoogleBooksLink,fetchChiliFreshReviews); + G.evt.result.allRecordsReceived.push( function(){unHideMe($('result_info_2'))}, fetchGoogleBooksLink, fetchChiliFreshReviews); attachEvt('result','lowHits',resultLowHits); attachEvt('result','zeroHits',resultZeroHits); @@ -81,6 +79,8 @@ function resultCollectSearchIds( type, method, handler ) { if(getAvail()) args.available = 1; + if(getFacet()) args.facets = getFacet(); + if(getAudience()) args.audience = getAudience().split(/,/); if(getLitForm()) args.lit_form = getLitForm().split(/,/); if(getLanguage()) args.language = getLanguage().split(/,/); 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 37dc49eaf..76d123db9 100644 --- a/Open-ILS/web/opac/skin/default/js/search_bar.js +++ b/Open-ILS/web/opac/skin/default/js/search_bar.js @@ -31,6 +31,7 @@ function searchBarInit() { /* set up the selector objects, etc */ G.ui.searchbar.text.value = (getTerm() != null) ? getTerm() : ""; + if (!isFrontPage) G.ui.searchbar.facets.value = (getFacet() != null) ? getFacet() : ""; setSelector(_ts, getStype()); setSelector(_fs, getForm()); @@ -51,6 +52,7 @@ function searchBarInit() { function searchBarSubmit(isFilterSort) { var text = G.ui.searchbar.text.value; + var facet_text = isFrontPage ? '' : G.ui.searchbar.facets.value; clearSearchParams(); @@ -61,7 +63,7 @@ function searchBarSubmit(isFilterSort) { var args = {}; - if(SHOW_MR_DEFAULT || (isFilterSort && findCurrentPage() == MRESULT)) { + if(SHOW_MR_DEFAULT || findCurrentPage() == MRESULT) { args.page = MRESULT; } else { args.page = RRESULT; @@ -70,6 +72,7 @@ function searchBarSubmit(isFilterSort) { args[PARAM_STYPE] = _ts.options[_ts.selectedIndex].value; args[PARAM_TERM] = text; + args[PARAM_FACET] = facet_text; args[PARAM_LOCATION] = depthSelGetNewLoc(); args[PARAM_DEPTH] = d; args[PARAM_FORM] = _fs.options[_fs.selectedIndex].value; diff --git a/Open-ILS/web/opac/skin/default/js/sidebar.js b/Open-ILS/web/opac/skin/default/js/sidebar.js index 45296e41b..30a2ffe27 100644 --- a/Open-ILS/web/opac/skin/default/js/sidebar.js +++ b/Open-ILS/web/opac/skin/default/js/sidebar.js @@ -183,12 +183,14 @@ function setSidebarLinks() { } function sidebarTreesFree() { +/* removeChildren($(subjectSidebarTree.rootid)); removeChildren($(authorSidebarTree.rootid)); removeChildren($(seriesSidebarTree.rootid)); subjectSidebarTree = null; authorSidebarTree = null; seriesSidebarTree = null; +*/ } diff --git a/Open-ILS/web/opac/skin/default/xml/common/js_common.xml b/Open-ILS/web/opac/skin/default/xml/common/js_common.xml index af0b79723..70a67b693 100644 --- a/Open-ILS/web/opac/skin/default/xml/common/js_common.xml +++ b/Open-ILS/web/opac/skin/default/xml/common/js_common.xml @@ -71,5 +71,5 @@ dojo.require("dojo.date.locale"); dojo.require("dojo.date.stamp"); dojo.require("dojo.parser"); - dojo.require("openils.I18N"); + dojo.require("openils.widget.FacetSidebar"); // pulls in Searcher and I18N diff --git a/Open-ILS/web/opac/skin/default/xml/common/searchbar.xml b/Open-ILS/web/opac/skin/default/xml/common/searchbar.xml index 505f6ab32..1638370e7 100644 --- a/Open-ILS/web/opac/skin/default/xml/common/searchbar.xml +++ b/Open-ILS/web/opac/skin/default/xml/common/searchbar.xml @@ -1,13 +1,44 @@ -