├── .gitignore ├── COPYRIGHT.txt ├── examples ├── reuters │ ├── images │ │ ├── header.gif │ │ └── ajax-loader.gif │ ├── js │ │ ├── reuters.1.js │ │ ├── reuters.2.js │ │ ├── reuters.3.js │ │ ├── reuters.4.js │ │ ├── reuters.5.js │ │ ├── reuters.6.js │ │ ├── reuters.7.js │ │ ├── reuters.8.js │ │ ├── reuters.9.js │ │ ├── reuters.js │ │ └── reuters.h.js │ ├── widgets │ │ ├── TextWidget.js │ │ ├── ResultWidget.2.0.js │ │ ├── CalendarWidget.js │ │ ├── TagcloudWidget.js │ │ ├── CountryCodeWidget.8.0.js │ │ ├── AutocompleteWidget.7.0.js │ │ ├── CurrentSearchWidget.js │ │ ├── CurrentSearchWidget.9.js │ │ ├── AutocompleteWidget.js │ │ ├── CountryCodeWidget.js │ │ ├── ResultWidget.js │ │ └── ResultWidget.f.js │ ├── index.0.html │ ├── index.1.html │ ├── index.2.html │ ├── index.2.0.html │ ├── index.3.html │ ├── index.4.html │ ├── index.5.html │ ├── index.6.html │ ├── index.7.html │ ├── index.7.0.html │ ├── index.8.html │ ├── index.8.0.html │ ├── index.html │ ├── index.9.0.html │ ├── index.9.html │ ├── index.f.html │ ├── index.h.html │ └── css │ │ └── reuters.css ├── reuters-requirejs │ ├── images │ │ ├── header.gif │ │ └── ajax-loader.gif │ ├── widgets │ │ ├── TextWidget.js │ │ ├── ResultWidget.2.0.js │ │ ├── CalendarWidget.js │ │ ├── TagcloudWidget.js │ │ ├── CountryCodeWidget.8.0.js │ │ ├── AutocompleteWidget.7.0.js │ │ ├── CurrentSearchWidget.js │ │ ├── CurrentSearchWidget.9.js │ │ ├── AutocompleteWidget.js │ │ ├── CountryCodeWidget.js │ │ ├── ResultWidget.js │ │ └── ResultWidget.f.js │ ├── index.html │ ├── css │ │ └── reuters.css │ └── js │ │ └── reuters.js └── solr-home │ ├── stopwords.txt │ ├── solrconfig.xml │ ├── synonyms.txt │ ├── README.md │ └── schema.xml ├── ASL-LICENSE ├── Rakefile ├── README.md ├── managers └── Manager.jquery.js ├── core ├── AbstractSpatialWidget.js ├── AbstractWidget.js ├── AbstractSpellcheckWidget.js ├── AbstractTextWidget.js ├── ParameterHashStore.js ├── Core.js ├── AbstractManager.js ├── Parameter.js ├── AbstractFacetWidget.js └── ParameterStore.js └── widgets ├── ParameterHistoryStore.js ├── ParameterYUIStore.js └── jquery ├── AutocompleteTermWidget.js └── PagerWidget.js /.gitignore: -------------------------------------------------------------------------------- 1 | ajax-solr.min.js 2 | -------------------------------------------------------------------------------- /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | AJAX Solr is licensed under the Apache License (ASL) v2.0. See ASL-LICENSE. -------------------------------------------------------------------------------- /examples/reuters/images/header.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolvingweb/ajax-solr/HEAD/examples/reuters/images/header.gif -------------------------------------------------------------------------------- /examples/reuters/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolvingweb/ajax-solr/HEAD/examples/reuters/images/ajax-loader.gif -------------------------------------------------------------------------------- /examples/reuters-requirejs/images/header.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolvingweb/ajax-solr/HEAD/examples/reuters-requirejs/images/header.gif -------------------------------------------------------------------------------- /examples/reuters-requirejs/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolvingweb/ajax-solr/HEAD/examples/reuters-requirejs/images/ajax-loader.gif -------------------------------------------------------------------------------- /examples/reuters/js/reuters.1.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | }); 9 | Manager.init(); 10 | Manager.store.addByValue('q', '*:*'); 11 | Manager.doRequest(); 12 | }); 13 | 14 | })(jQuery); 15 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.2.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | }); 9 | Manager.addWidget(new AjaxSolr.ResultWidget({ 10 | id: 'result', 11 | target: '#docs' 12 | })); 13 | Manager.init(); 14 | Manager.store.addByValue('q', '*:*'); 15 | Manager.doRequest(); 16 | }); 17 | 18 | })(jQuery); 19 | -------------------------------------------------------------------------------- /examples/reuters/widgets/TextWidget.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.TextWidget = AjaxSolr.AbstractTextWidget.extend({ 4 | init: function () { 5 | var self = this; 6 | $(this.target).find('input').bind('keydown', function(e) { 7 | if (e.which == 13) { 8 | var value = $(this).val(); 9 | if (value && self.set(value)) { 10 | self.doRequest(); 11 | } 12 | } 13 | }); 14 | }, 15 | 16 | afterRequest: function () { 17 | $(this.target).find('input').val(''); 18 | } 19 | }); 20 | 21 | })(jQuery); 22 | -------------------------------------------------------------------------------- /ASL-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2009 Evolving Web Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/TextWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractTextWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.TextWidget = AjaxSolr.AbstractTextWidget.extend({ 13 | init: function () { 14 | var self = this; 15 | $(this.target).find('input').bind('keydown', function(e) { 16 | if (e.which == 13) { 17 | var value = $(this).val(); 18 | if (value && self.set(value)) { 19 | self.doRequest(); 20 | } 21 | } 22 | }); 23 | }, 24 | 25 | afterRequest: function () { 26 | $(this.target).find('input').val(''); 27 | } 28 | }); 29 | 30 | })(jQuery); 31 | 32 | })); 33 | -------------------------------------------------------------------------------- /examples/solr-home/stopwords.txt: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.3.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | }); 9 | Manager.addWidget(new AjaxSolr.ResultWidget({ 10 | id: 'result', 11 | target: '#docs' 12 | })); 13 | Manager.addWidget(new AjaxSolr.PagerWidget({ 14 | id: 'pager', 15 | target: '#pager', 16 | prevLabel: '<', 17 | nextLabel: '>', 18 | innerWindow: 1, 19 | renderHeader: function (perPage, offset, total) { 20 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 21 | } 22 | })); 23 | Manager.init(); 24 | Manager.store.addByValue('q', '*:*'); 25 | Manager.doRequest(); 26 | }); 27 | 28 | })(jQuery); 29 | -------------------------------------------------------------------------------- /examples/solr-home/solrconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LUCENE_42 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *:* 16 | 17 | 18 | 19 | 20 | solrpingquery 21 | 22 | 23 | all 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/reuters/widgets/ResultWidget.2.0.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.ResultWidget = AjaxSolr.AbstractWidget.extend({ 4 | afterRequest: function () { 5 | $(this.target).empty(); 6 | for (var i = 0, l = this.manager.response.response.docs.length; i < l; i++) { 7 | var doc = this.manager.response.response.docs[i]; 8 | $(this.target).append(this.template(doc)); 9 | } 10 | }, 11 | 12 | template: function (doc) { 13 | var snippet = ''; 14 | if (doc.text.length > 300) { 15 | snippet += doc.dateline + ' ' + doc.text.substring(0, 300); 16 | snippet += '' + doc.text.substring(300); 17 | snippet += ' more'; 18 | } 19 | else { 20 | snippet += doc.dateline + ' ' + doc.text; 21 | } 22 | 23 | var output = '

' + doc.title + '

'; 24 | output += ''; 25 | output += '

' + snippet + '

'; 26 | return output; 27 | } 28 | }); 29 | 30 | })(jQuery); -------------------------------------------------------------------------------- /examples/reuters/widgets/CalendarWidget.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.CalendarWidget = AjaxSolr.AbstractFacetWidget.extend({ 4 | afterRequest: function () { 5 | var self = this; 6 | $(this.target).datepicker('destroy').datepicker({ 7 | dateFormat: 'yy-mm-dd', 8 | defaultDate: new Date(1987, 2, 1), 9 | maxDate: $.datepicker.parseDate('yy-mm-dd', this.manager.store.get('facet.date.end').val().substr(0, 10)), 10 | minDate: $.datepicker.parseDate('yy-mm-dd', this.manager.store.get('facet.date.start').val().substr(0, 10)), 11 | nextText: '>', 12 | prevText: '<', 13 | beforeShowDay: function (date) { 14 | var value = $.datepicker.formatDate('yy-mm-dd', date) + 'T00:00:00Z'; 15 | var count = self.manager.response.facet_counts.facet_dates[self.field][value]; 16 | return [ parseInt(count) > 0, '', count + ' documents found!' ]; 17 | }, 18 | onSelect: function (dateText, inst) { 19 | if (self.add('[' + dateText + 'T00:00:00Z TO ' + dateText + 'T23:59:59Z]')) { 20 | self.doRequest(); 21 | } 22 | } 23 | }); 24 | } 25 | }); 26 | 27 | })(jQuery); 28 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'yui/compressor' 3 | 4 | desc 'Aggregate all javascript files' 5 | task :aggregate, :compress do |t, args| 6 | args.with_defaults(:compress => false) 7 | output_file = ENV['output'] || 'ajax-solr.min.js' 8 | 9 | core = [ 10 | 'Core', 11 | 'AbstractManager', 12 | 'ParameterStore', 13 | 'Parameter', 14 | 'AbstractWidget', 15 | 'AbstractFacetWidget' 16 | ] 17 | 18 | dirs = [ 19 | 'core', 20 | 'helpers', 21 | 'managers', 22 | 'widgets' 23 | ] 24 | 25 | compressor = YUI::JavaScriptCompressor.new(:munge => true) if args[:compress] 26 | 27 | files = core.map{ |name| "core/#{name}.js" } + dirs.map{ |dir| Dir["#{dir}/**/*.js"] }.flatten 28 | files.uniq! 29 | 30 | action = args[:compress] ? "Compressing" : "Aggregating" 31 | 32 | File.open(output_file, 'w') do |output| 33 | files.each do |file_name| 34 | puts "#{action} #{file_name}" 35 | input = File.read(file_name) 36 | output.write(args[:compress] ? compressor.compress(input) : input) 37 | end 38 | end 39 | end 40 | 41 | desc 'Compress all javascript files' 42 | task :compress do 43 | Rake::Task[:aggregate].invoke(true) 44 | end 45 | -------------------------------------------------------------------------------- /examples/solr-home/synonyms.txt: -------------------------------------------------------------------------------- 1 | # The ASF licenses this file to You under the Apache License, Version 2.0 2 | # (the "License"); you may not use this file except in compliance with 3 | # the License. You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | #----------------------------------------------------------------------- 14 | #some test synonym mappings unlikely to appear in real input text 15 | aaafoo => aaabar 16 | bbbfoo => bbbfoo bbbbar 17 | cccfoo => cccbar cccbaz 18 | fooaaa,baraaa,bazaaa 19 | 20 | # Some synonym groups specific to this example 21 | GB,gib,gigabyte,gigabytes 22 | MB,mib,megabyte,megabytes 23 | Television, Televisions, TV, TVs 24 | #notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming 25 | #after us won't split it into two words. 26 | 27 | # Synonym mappings can be used for spelling correction too 28 | pixima => pixma 29 | 30 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/ResultWidget.2.0.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.ResultWidget = AjaxSolr.AbstractWidget.extend({ 13 | afterRequest: function () { 14 | $(this.target).empty(); 15 | for (var i = 0, l = this.manager.response.response.docs.length; i < l; i++) { 16 | var doc = this.manager.response.response.docs[i]; 17 | $(this.target).append(this.template(doc)); 18 | } 19 | }, 20 | 21 | template: function (doc) { 22 | var snippet = ''; 23 | if (doc.text.length > 300) { 24 | snippet += doc.dateline + ' ' + doc.text.substring(0, 300); 25 | snippet += '' + doc.text.substring(300); 26 | snippet += ' more'; 27 | } 28 | else { 29 | snippet += doc.dateline + ' ' + doc.text; 30 | } 31 | 32 | var output = '

' + doc.title + '

'; 33 | output += ''; 34 | output += '

' + snippet + '

'; 35 | return output; 36 | } 37 | }); 38 | 39 | })(jQuery); 40 | 41 | })); 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AJAX Solr 2 | 3 | AJAX Solr is a JavaScript library for creating user interfaces to [Apache Solr][1]. 4 | 5 | * Play with the [demo site](https://evolvingweb.github.io/ajax-solr/examples/reuters/index.html)[5] 6 | * Follow the [tutorial][7] 7 | * Browse the [wiki][3] 8 | * Read the [JSDoc documentation][2] (the [tutorial][7] is recommended for first-time users) 9 | * Explore the [code][6] 10 | * [Ask questions and discuss][8] 11 | 12 | Get an offline copy of the documentation, examples, and tutorial files by downloading either a [zipball](http://github.com/evolvingweb/ajax-solr/zipball/gh-pages) or [tarball](http://github.com/evolvingweb/ajax-solr/tarball/gh-pages). 13 | 14 | Documentation is generated by [JsDoc Toolkit][4] with the command: 15 | 16 | `java -jar jsrun.jar app/run.js -a -p -r=3 -t=templates/jsdoc /path/to/library/` 17 | 18 | [1]: http://lucene.apache.org/solr/ 19 | [2]: http://evolvingweb.github.com/ajax-solr/docs/index.html 20 | [3]: http://wiki.github.com/evolvingweb/ajax-solr 21 | [4]: http://code.google.com/p/jsdoc-toolkit/ 22 | [5]: http://evolvingweb.github.com/ajax-solr/examples/reuters/index.html 23 | [6]: http://github.com/evolvingweb/ajax-solr 24 | [7]: http://wiki.github.com/evolvingweb/ajax-solr/reuters-tutorial 25 | [8]: http://groups.google.com/group/ajax-solr 26 | -------------------------------------------------------------------------------- /examples/reuters/widgets/TagcloudWidget.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.TagcloudWidget = AjaxSolr.AbstractFacetWidget.extend({ 4 | afterRequest: function () { 5 | if (this.manager.response.facet_counts.facet_fields[this.field] === undefined) { 6 | $(this.target).html('no items found in current selection'); 7 | return; 8 | } 9 | 10 | var maxCount = 0; 11 | var objectedItems = []; 12 | for (var facet in this.manager.response.facet_counts.facet_fields[this.field]) { 13 | var count = parseInt(this.manager.response.facet_counts.facet_fields[this.field][facet]); 14 | if (count > maxCount) { 15 | maxCount = count; 16 | } 17 | objectedItems.push({ facet: facet, count: count }); 18 | } 19 | objectedItems.sort(function (a, b) { 20 | return a.facet < b.facet ? -1 : 1; 21 | }); 22 | 23 | $(this.target).empty(); 24 | for (var i = 0, l = objectedItems.length; i < l; i++) { 25 | var facet = objectedItems[i].facet; 26 | $(this.target).append( 27 | $('') 28 | .text(facet) 29 | .addClass('tagcloud_size_' + parseInt(objectedItems[i].count / maxCount * 10)) 30 | .click(this.clickHandler(facet)) 31 | ); 32 | } 33 | } 34 | }); 35 | 36 | })(jQuery); 37 | -------------------------------------------------------------------------------- /managers/Manager.jquery.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractManager'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * @see http://wiki.apache.org/solr/SolJSON#JSON_specific_parameters 12 | * @class Manager 13 | * @augments AjaxSolr.AbstractManager 14 | */ 15 | AjaxSolr.Manager = AjaxSolr.AbstractManager.extend( 16 | /** @lends AjaxSolr.Manager.prototype */ 17 | { 18 | executeRequest: function (servlet, string, handler, errorHandler, disableJsonp) { 19 | var self = this, 20 | options = {dataType: 'json'}; 21 | string = string || this.store.string(); 22 | handler = handler || function (data) { 23 | self.handleResponse(data); 24 | }; 25 | errorHandler = errorHandler || function (jqXHR, textStatus, errorThrown) { 26 | self.handleError(textStatus + ', ' + errorThrown); 27 | }; 28 | if (this.proxyUrl) { 29 | options.url = this.proxyUrl; 30 | options.data = {query: string}; 31 | options.type = 'POST'; 32 | } 33 | else { 34 | options.url = this.solrUrl + servlet + '?' + string + '&wt=json' + (disableJsonp ? '' : '&json.wrf=?'); 35 | } 36 | jQuery.ajax(options).done(handler).fail(errorHandler); 37 | } 38 | }); 39 | 40 | })); 41 | -------------------------------------------------------------------------------- /examples/reuters/widgets/CountryCodeWidget.8.0.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.CountryCodeWidget = AjaxSolr.AbstractFacetWidget.extend({ 4 | afterRequest: function () { 5 | var self = this; 6 | 7 | $(this.target).empty(); 8 | 9 | var maxCount = 0; 10 | var options = { '': '--select--' }; 11 | for (var facet in this.manager.response.facet_counts.facet_fields[this.field]) { 12 | if (facet.length == 2) { // only display country codes 13 | var count = this.manager.response.facet_counts.facet_fields[this.field][facet]; 14 | if (count > maxCount) { 15 | maxCount = count; 16 | } 17 | options[facet] = facet + ' (' + count + ')'; 18 | } 19 | } 20 | $(this.target).append(this.template('country', options)); 21 | 22 | $(this.target).find('#country').change(function () { 23 | var value = $(this).val(); 24 | if (value && self.add(value)) { 25 | self.doRequest(); 26 | } 27 | }); 28 | }, 29 | 30 | template: function (name, container) { 31 | var options = []; 32 | for (var value in container) { 33 | options.push(''); 34 | } 35 | return ''; 36 | } 37 | }); 38 | 39 | })(jQuery); 40 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/CalendarWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractFacetWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.CalendarWidget = AjaxSolr.AbstractFacetWidget.extend({ 13 | afterRequest: function () { 14 | var self = this; 15 | $(this.target).datepicker('destroy').datepicker({ 16 | dateFormat: 'yy-mm-dd', 17 | defaultDate: new Date(1987, 2, 1), 18 | maxDate: $.datepicker.parseDate('yy-mm-dd', this.manager.store.get('facet.date.end').val().substr(0, 10)), 19 | minDate: $.datepicker.parseDate('yy-mm-dd', this.manager.store.get('facet.date.start').val().substr(0, 10)), 20 | nextText: '>', 21 | prevText: '<', 22 | beforeShowDay: function (date) { 23 | var value = $.datepicker.formatDate('yy-mm-dd', date) + 'T00:00:00Z'; 24 | var count = self.manager.response.facet_counts.facet_dates[self.field][value]; 25 | return [ parseInt(count) > 0, '', count + ' documents found!' ]; 26 | }, 27 | onSelect: function (dateText, inst) { 28 | if (self.add('[' + dateText + 'T00:00:00Z TO ' + dateText + 'T23:59:59Z]')) { 29 | self.doRequest(); 30 | } 31 | } 32 | }); 33 | } 34 | }); 35 | 36 | })(jQuery); 37 | 38 | })); 39 | -------------------------------------------------------------------------------- /examples/solr-home/README.md: -------------------------------------------------------------------------------- 1 | This directory contains Solr configuration files for running the [Solr 4 Reuters index](http://public.slashpoundbang.com.s3.amazonaws.com/data-solr-4-index.zip) locally. The `stopwords.txt` and `synonyms.txt` are the Solr defaults. `schema.xml` and `solrconfig.xml` were trimmed down to the essentials. 2 | 3 | ## Notes on how the Solr 4 index was created 4 | 5 | The following steps were used to convert the [Solr 1.4 Reuters index](https://github.com/downloads/evolvingweb/ajax-solr/reuters_data.tar.gz) to a [Solr 4 index](http://public.slashpoundbang.com.s3.amazonaws.com/data-solr-4-index.zip): 6 | 7 | 1. Download and install Solr 3.6, because Solr 3.6 supports Solr 1.4.x indexes. 8 | 1. Copy `schema.xml` and `solrconfig.xml` to the Solr 3.6 `conf` directory. 9 | 1. Copy the Solr 1.4 index to the Solr 3.6 `data` directory. 10 | 1. Start Solr 3.6 by running `java -jar start.jar` from the Solr 3.6 directory. 11 | 1. Optimize the index to save it in the Solr 3.6 format: `curl -X POST http://localhost:8983/update?commit=true -H "Content-Type: text/xml" --data-binary ''` 12 | 1. Stop Solr 3.6. 13 | 1. Copy the Solr 3.6 `conf` directory to your Solr 4 directory. 14 | 1. Copy the Solr 3.6 `data` index to your Solr 4 directory. 15 | 1. Start Solr 4. 16 | 17 | ## References 18 | 19 | * [Reuters-21578 Text Categorization Collection](http://kdd.ics.uci.edu/databases/reuters21578/reuters21578.html) 20 | * [SolrJS wiki](http://wiki.apache.org/solr/SolrJS#Creating_the_reuters_example) 21 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/TagcloudWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractFacetWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.TagcloudWidget = AjaxSolr.AbstractFacetWidget.extend({ 13 | afterRequest: function () { 14 | if (this.manager.response.facet_counts.facet_fields[this.field] === undefined) { 15 | $(this.target).html('no items found in current selection'); 16 | return; 17 | } 18 | 19 | var maxCount = 0; 20 | var objectedItems = []; 21 | for (var facet in this.manager.response.facet_counts.facet_fields[this.field]) { 22 | var count = parseInt(this.manager.response.facet_counts.facet_fields[this.field][facet]); 23 | if (count > maxCount) { 24 | maxCount = count; 25 | } 26 | objectedItems.push({ facet: facet, count: count }); 27 | } 28 | objectedItems.sort(function (a, b) { 29 | return a.facet < b.facet ? -1 : 1; 30 | }); 31 | 32 | $(this.target).empty(); 33 | for (var i = 0, l = objectedItems.length; i < l; i++) { 34 | var facet = objectedItems[i].facet; 35 | $(this.target).append( 36 | $('') 37 | .text(facet) 38 | .addClass('tagcloud_size_' + parseInt(objectedItems[i].count / maxCount * 10)) 39 | .click(this.clickHandler(facet)) 40 | ); 41 | } 42 | } 43 | }); 44 | 45 | })(jQuery); 46 | 47 | })); 48 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.4.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | }); 9 | Manager.addWidget(new AjaxSolr.ResultWidget({ 10 | id: 'result', 11 | target: '#docs' 12 | })); 13 | Manager.addWidget(new AjaxSolr.PagerWidget({ 14 | id: 'pager', 15 | target: '#pager', 16 | prevLabel: '<', 17 | nextLabel: '>', 18 | innerWindow: 1, 19 | renderHeader: function (perPage, offset, total) { 20 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 21 | } 22 | })); 23 | var fields = [ 'topics', 'organisations', 'exchanges' ]; 24 | for (var i = 0, l = fields.length; i < l; i++) { 25 | Manager.addWidget(new AjaxSolr.TagcloudWidget({ 26 | id: fields[i], 27 | target: '#' + fields[i], 28 | field: fields[i] 29 | })); 30 | } 31 | Manager.init(); 32 | Manager.store.addByValue('q', '*:*'); 33 | var params = { 34 | facet: true, 35 | 'facet.field': [ 'topics', 'organisations', 'exchanges' ], 36 | 'facet.limit': 20, 37 | 'facet.mincount': 1, 38 | 'f.topics.facet.limit': 50, 39 | 'json.nl': 'map' 40 | }; 41 | for (var name in params) { 42 | Manager.store.addByValue(name, params[name]); 43 | } 44 | Manager.doRequest(); 45 | }); 46 | 47 | })(jQuery); 48 | -------------------------------------------------------------------------------- /examples/reuters/widgets/AutocompleteWidget.7.0.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.AutocompleteWidget = AjaxSolr.AbstractTextWidget.extend({ 4 | afterRequest: function () { 5 | $(this.target).find('input').unbind().removeData('events').val(''); 6 | 7 | var self = this; 8 | 9 | var list = []; 10 | for (var i = 0; i < this.fields.length; i++) { 11 | var field = this.fields[i]; 12 | for (var facet in this.manager.response.facet_counts.facet_fields[field]) { 13 | list.push({ 14 | field: field, 15 | value: facet, 16 | label: facet + ' (' + this.manager.response.facet_counts.facet_fields[field][facet] + ') - ' + field 17 | }); 18 | } 19 | } 20 | 21 | this.requestSent = false; 22 | $(this.target).find('input').autocomplete('destroy').autocomplete({ 23 | source: list, 24 | select: function(event, ui) { 25 | if (ui.item) { 26 | self.requestSent = true; 27 | if (self.manager.store.addByValue('fq', ui.item.field + ':' + AjaxSolr.Parameter.escapeValue(ui.item.value))) { 28 | self.doRequest(); 29 | } 30 | } 31 | } 32 | }); 33 | 34 | // This has lower priority so that requestSent is set. 35 | $(this.target).find('input').bind('keydown', function(e) { 36 | if (self.requestSent === false && e.which == 13) { 37 | var value = $(this).val(); 38 | if (value && self.set(value)) { 39 | self.doRequest(); 40 | } 41 | } 42 | }); 43 | } 44 | }); 45 | 46 | })(jQuery); 47 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/CountryCodeWidget.8.0.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractFacetWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.CountryCodeWidget = AjaxSolr.AbstractFacetWidget.extend({ 13 | afterRequest: function () { 14 | var self = this; 15 | 16 | $(this.target).empty(); 17 | 18 | var maxCount = 0; 19 | var options = { '': '--select--' }; 20 | for (var facet in this.manager.response.facet_counts.facet_fields[this.field]) { 21 | if (facet.length == 2) { // only display country codes 22 | var count = this.manager.response.facet_counts.facet_fields[this.field][facet]; 23 | if (count > maxCount) { 24 | maxCount = count; 25 | } 26 | options[facet] = facet + ' (' + count + ')'; 27 | } 28 | } 29 | $(this.target).append(this.template('country', options)); 30 | 31 | $(this.target).find('#country').change(function () { 32 | var value = $(this).val(); 33 | if (value && self.add(value)) { 34 | self.doRequest(); 35 | } 36 | }); 37 | }, 38 | 39 | template: function (name, container) { 40 | var options = []; 41 | for (var value in container) { 42 | options.push(''); 43 | } 44 | return ''; 45 | } 46 | }); 47 | 48 | })(jQuery); 49 | 50 | })); 51 | -------------------------------------------------------------------------------- /examples/reuters/widgets/CurrentSearchWidget.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.CurrentSearchWidget = AjaxSolr.AbstractWidget.extend({ 4 | start: 0, 5 | 6 | afterRequest: function () { 7 | var self = this; 8 | var links = []; 9 | 10 | var q = this.manager.store.get('q').val(); 11 | if (q != '*:*') { 12 | links.push($('').text('(x) ' + q).click(function () { 13 | self.manager.store.get('q').val('*:*'); 14 | self.doRequest(); 15 | return false; 16 | })); 17 | } 18 | 19 | var fq = this.manager.store.values('fq'); 20 | for (var i = 0, l = fq.length; i < l; i++) { 21 | links.push($('').text('(x) ' + fq[i]).click(self.removeFacet(fq[i]))); 22 | } 23 | 24 | if (links.length > 1) { 25 | links.unshift($('').text('remove all').click(function () { 26 | self.manager.store.get('q').val('*:*'); 27 | self.manager.store.remove('fq'); 28 | self.doRequest(); 29 | return false; 30 | })); 31 | } 32 | 33 | if (links.length) { 34 | var $target = $(this.target); 35 | $target.empty(); 36 | for (var i = 0, l = links.length; i < l; i++) { 37 | $target.append($('
  • ').append(links[i])); 38 | } 39 | } 40 | else { 41 | $(this.target).html('
  • Viewing all documents!
  • '); 42 | } 43 | }, 44 | 45 | removeFacet: function (facet) { 46 | var self = this; 47 | return function () { 48 | if (self.manager.store.removeByValue('fq', facet)) { 49 | self.doRequest(); 50 | } 51 | return false; 52 | }; 53 | } 54 | }); 55 | 56 | })(jQuery); 57 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.5.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | }); 9 | Manager.addWidget(new AjaxSolr.ResultWidget({ 10 | id: 'result', 11 | target: '#docs' 12 | })); 13 | Manager.addWidget(new AjaxSolr.PagerWidget({ 14 | id: 'pager', 15 | target: '#pager', 16 | prevLabel: '<', 17 | nextLabel: '>', 18 | innerWindow: 1, 19 | renderHeader: function (perPage, offset, total) { 20 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 21 | } 22 | })); 23 | var fields = [ 'topics', 'organisations', 'exchanges' ]; 24 | for (var i = 0, l = fields.length; i < l; i++) { 25 | Manager.addWidget(new AjaxSolr.TagcloudWidget({ 26 | id: fields[i], 27 | target: '#' + fields[i], 28 | field: fields[i] 29 | })); 30 | } 31 | Manager.addWidget(new AjaxSolr.CurrentSearchWidget({ 32 | id: 'currentsearch', 33 | target: '#selection' 34 | })); 35 | Manager.init(); 36 | Manager.store.addByValue('q', '*:*'); 37 | var params = { 38 | facet: true, 39 | 'facet.field': [ 'topics', 'organisations', 'exchanges' ], 40 | 'facet.limit': 20, 41 | 'facet.mincount': 1, 42 | 'f.topics.facet.limit': 50, 43 | 'json.nl': 'map' 44 | }; 45 | for (var name in params) { 46 | Manager.store.addByValue(name, params[name]); 47 | } 48 | Manager.doRequest(); 49 | }); 50 | 51 | })(jQuery); 52 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/AutocompleteWidget.7.0.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractTextWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.AutocompleteWidget = AjaxSolr.AbstractTextWidget.extend({ 13 | afterRequest: function () { 14 | $(this.target).find('input').unbind().removeData('events').val(''); 15 | 16 | var self = this; 17 | 18 | var list = []; 19 | for (var i = 0; i < this.fields.length; i++) { 20 | var field = this.fields[i]; 21 | for (var facet in this.manager.response.facet_counts.facet_fields[field]) { 22 | list.push({ 23 | field: field, 24 | value: facet, 25 | label: facet + ' (' + this.manager.response.facet_counts.facet_fields[field][facet] + ') - ' + field 26 | }); 27 | } 28 | } 29 | 30 | this.requestSent = false; 31 | $(this.target).find('input').autocomplete('destroy').autocomplete({ 32 | source: list, 33 | select: function(event, ui) { 34 | if (ui.item) { 35 | self.requestSent = true; 36 | if (self.manager.store.addByValue('fq', ui.item.field + ':' + AjaxSolr.Parameter.escapeValue(ui.item.value))) { 37 | self.doRequest(); 38 | } 39 | } 40 | } 41 | }); 42 | 43 | // This has lower priority so that requestSent is set. 44 | $(this.target).find('input').bind('keydown', function(e) { 45 | if (self.requestSent === false && e.which == 13) { 46 | var value = $(this).val(); 47 | if (value && self.set(value)) { 48 | self.doRequest(); 49 | } 50 | } 51 | }); 52 | } 53 | }); 54 | 55 | })(jQuery); 56 | 57 | })); 58 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.6.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | }); 9 | Manager.addWidget(new AjaxSolr.ResultWidget({ 10 | id: 'result', 11 | target: '#docs' 12 | })); 13 | Manager.addWidget(new AjaxSolr.PagerWidget({ 14 | id: 'pager', 15 | target: '#pager', 16 | prevLabel: '<', 17 | nextLabel: '>', 18 | innerWindow: 1, 19 | renderHeader: function (perPage, offset, total) { 20 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 21 | } 22 | })); 23 | var fields = [ 'topics', 'organisations', 'exchanges' ]; 24 | for (var i = 0, l = fields.length; i < l; i++) { 25 | Manager.addWidget(new AjaxSolr.TagcloudWidget({ 26 | id: fields[i], 27 | target: '#' + fields[i], 28 | field: fields[i] 29 | })); 30 | } 31 | Manager.addWidget(new AjaxSolr.CurrentSearchWidget({ 32 | id: 'currentsearch', 33 | target: '#selection' 34 | })); 35 | Manager.addWidget(new AjaxSolr.TextWidget({ 36 | id: 'text', 37 | target: '#search' 38 | })); 39 | Manager.init(); 40 | Manager.store.addByValue('q', '*:*'); 41 | var params = { 42 | facet: true, 43 | 'facet.field': [ 'topics', 'organisations', 'exchanges' ], 44 | 'facet.limit': 20, 45 | 'facet.mincount': 1, 46 | 'f.topics.facet.limit': 50, 47 | 'json.nl': 'map' 48 | }; 49 | for (var name in params) { 50 | Manager.store.addByValue(name, params[name]); 51 | } 52 | Manager.doRequest(); 53 | }); 54 | 55 | })(jQuery); 56 | -------------------------------------------------------------------------------- /examples/reuters/index.0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 17 | 18 |
    19 |
    20 | 24 |
    25 |
    26 |
    27 | 28 |
    29 |

    Current Selection

    30 |
      31 | 32 |

      Search

      33 | (press ESC to close suggestions) 34 | 37 | 38 |

      Top Topics

      39 |
      40 | 41 |

      Top Organisations

      42 |
      43 | 44 |

      Top Exchanges

      45 |
      46 | 47 |

      By Country

      48 |
      49 |
      50 | 51 |

      By Date

      52 |
      53 | 54 |
      55 |
      56 |
      57 |
      58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.7.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | }); 9 | Manager.addWidget(new AjaxSolr.ResultWidget({ 10 | id: 'result', 11 | target: '#docs' 12 | })); 13 | Manager.addWidget(new AjaxSolr.PagerWidget({ 14 | id: 'pager', 15 | target: '#pager', 16 | prevLabel: '<', 17 | nextLabel: '>', 18 | innerWindow: 1, 19 | renderHeader: function (perPage, offset, total) { 20 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 21 | } 22 | })); 23 | var fields = [ 'topics', 'organisations', 'exchanges' ]; 24 | for (var i = 0, l = fields.length; i < l; i++) { 25 | Manager.addWidget(new AjaxSolr.TagcloudWidget({ 26 | id: fields[i], 27 | target: '#' + fields[i], 28 | field: fields[i] 29 | })); 30 | } 31 | Manager.addWidget(new AjaxSolr.CurrentSearchWidget({ 32 | id: 'currentsearch', 33 | target: '#selection' 34 | })); 35 | Manager.addWidget(new AjaxSolr.AutocompleteWidget({ 36 | id: 'text', 37 | target: '#search', 38 | fields: [ 'topics', 'organisations', 'exchanges' ] 39 | })); 40 | Manager.init(); 41 | Manager.store.addByValue('q', '*:*'); 42 | var params = { 43 | facet: true, 44 | 'facet.field': [ 'topics', 'organisations', 'exchanges' ], 45 | 'facet.limit': 20, 46 | 'facet.mincount': 1, 47 | 'f.topics.facet.limit': 50, 48 | 'json.nl': 'map' 49 | }; 50 | for (var name in params) { 51 | Manager.store.addByValue(name, params[name]); 52 | } 53 | Manager.doRequest(); 54 | }); 55 | 56 | })(jQuery); 57 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/CurrentSearchWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.CurrentSearchWidget = AjaxSolr.AbstractWidget.extend({ 13 | start: 0, 14 | 15 | afterRequest: function () { 16 | var self = this; 17 | var links = []; 18 | 19 | var q = this.manager.store.get('q').val(); 20 | if (q != '*:*') { 21 | links.push($('').text('(x) ' + q).click(function () { 22 | self.manager.store.get('q').val('*:*'); 23 | self.doRequest(); 24 | return false; 25 | })); 26 | } 27 | 28 | var fq = this.manager.store.values('fq'); 29 | for (var i = 0, l = fq.length; i < l; i++) { 30 | links.push($('').text('(x) ' + fq[i]).click(self.removeFacet(fq[i]))); 31 | } 32 | 33 | if (links.length > 1) { 34 | links.unshift($('').text('remove all').click(function () { 35 | self.manager.store.get('q').val('*:*'); 36 | self.manager.store.remove('fq'); 37 | self.doRequest(); 38 | return false; 39 | })); 40 | } 41 | 42 | if (links.length) { 43 | var $target = $(this.target); 44 | $target.empty(); 45 | for (var i = 0, l = links.length; i < l; i++) { 46 | $target.append($('
    • ').append(links[i])); 47 | } 48 | } 49 | else { 50 | $(this.target).html('
    • Viewing all documents!
    • '); 51 | } 52 | }, 53 | 54 | removeFacet: function (facet) { 55 | var self = this; 56 | return function () { 57 | if (self.manager.store.removeByValue('fq', facet)) { 58 | self.doRequest(); 59 | } 60 | return false; 61 | }; 62 | } 63 | }); 64 | 65 | })(jQuery); 66 | 67 | })); 68 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
      14 | 18 | 19 |
      20 |
      21 | 25 |
      26 |
      27 |
      28 | 29 |
      30 |

      Current Selection

      31 |
        32 | 33 |

        Search

        34 | (press ESC to close suggestions) 35 | 38 | 39 |

        Top Topics

        40 |
        41 | 42 |

        Top Organisations

        43 |
        44 | 45 |

        Top Exchanges

        46 |
        47 | 48 |

        By Country

        49 |
        50 |
        51 | 52 |

        By Date

        53 |
        54 | 55 |
        56 |
        57 |
        58 |
        59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/reuters/widgets/CurrentSearchWidget.9.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.CurrentSearchWidget = AjaxSolr.AbstractWidget.extend({ 4 | start: 0, 5 | 6 | afterRequest: function () { 7 | var self = this; 8 | var links = []; 9 | 10 | var q = this.manager.store.get('q').val(); 11 | if (q != '*:*') { 12 | links.push($('').text('(x) ' + q).click(function () { 13 | self.manager.store.get('q').val('*:*'); 14 | self.doRequest(); 15 | return false; 16 | })); 17 | } 18 | 19 | var fq = this.manager.store.values('fq'); 20 | for (var i = 0, l = fq.length; i < l; i++) { 21 | if (fq[i].match(/[\[\{]\S+ TO \S+[\]\}]/)) { 22 | var field = fq[i].match(/^\w+:/)[0]; 23 | var value = fq[i].substr(field.length + 1, 10); 24 | links.push($('').text('(x) ' + field + value).click(self.removeFacet(fq[i]))); 25 | } 26 | else { 27 | links.push($('').text('(x) ' + fq[i]).click(self.removeFacet(fq[i]))); 28 | } 29 | } 30 | 31 | if (links.length > 1) { 32 | links.unshift($('').text('remove all').click(function () { 33 | self.manager.store.get('q').val('*:*'); 34 | self.manager.store.remove('fq'); 35 | self.doRequest(); 36 | return false; 37 | })); 38 | } 39 | 40 | if (links.length) { 41 | var $target = $(this.target); 42 | $target.empty(); 43 | for (var i = 0, l = links.length; i < l; i++) { 44 | $target.append($('
      • ').append(links[i])); 45 | } 46 | } 47 | else { 48 | $(this.target).html('
      • Viewing all documents!
      • '); 49 | } 50 | }, 51 | 52 | removeFacet: function (facet) { 53 | var self = this; 54 | return function () { 55 | if (self.manager.store.removeByValue('fq', facet)) { 56 | self.doRequest(); 57 | } 58 | return false; 59 | }; 60 | } 61 | }); 62 | 63 | })(jQuery); 64 | -------------------------------------------------------------------------------- /examples/reuters/index.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
        19 | 23 | 24 |
        25 |
        26 | 30 |
        31 |
        32 |
        33 | 34 |
        35 |

        Current Selection

        36 |
          37 | 38 |

          Search

          39 | (press ESC to close suggestions) 40 | 43 | 44 |

          Top Topics

          45 |
          46 | 47 |

          Top Organisations

          48 |
          49 | 50 |

          Top Exchanges

          51 |
          52 | 53 |

          By Country

          54 |
          55 |
          56 | 57 |

          By Date

          58 |
          59 | 60 |
          61 |
          62 |
          63 |
          64 | 65 | 66 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/CurrentSearchWidget.9.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.CurrentSearchWidget = AjaxSolr.AbstractWidget.extend({ 13 | start: 0, 14 | 15 | afterRequest: function () { 16 | var self = this; 17 | var links = []; 18 | 19 | var q = this.manager.store.get('q').val(); 20 | if (q != '*:*') { 21 | links.push($('').text('(x) ' + q).click(function () { 22 | self.manager.store.get('q').val('*:*'); 23 | self.doRequest(); 24 | return false; 25 | })); 26 | } 27 | 28 | var fq = this.manager.store.values('fq'); 29 | for (var i = 0, l = fq.length; i < l; i++) { 30 | if (fq[i].match(/[\[\{]\S+ TO \S+[\]\}]/)) { 31 | var field = fq[i].match(/^\w+:/)[0]; 32 | var value = fq[i].substr(field.length + 1, 10); 33 | links.push($('').text('(x) ' + field + value).click(self.removeFacet(fq[i]))); 34 | } 35 | else { 36 | links.push($('').text('(x) ' + fq[i]).click(self.removeFacet(fq[i]))); 37 | } 38 | } 39 | 40 | if (links.length > 1) { 41 | links.unshift($('').text('remove all').click(function () { 42 | self.manager.store.get('q').val('*:*'); 43 | self.manager.store.remove('fq'); 44 | self.doRequest(); 45 | return false; 46 | })); 47 | } 48 | 49 | if (links.length) { 50 | var $target = $(this.target); 51 | $target.empty(); 52 | for (var i = 0, l = links.length; i < l; i++) { 53 | $target.append($('
        • ').append(links[i])); 54 | } 55 | } 56 | else { 57 | $(this.target).html('
        • Viewing all documents!
        • '); 58 | } 59 | }, 60 | 61 | removeFacet: function (facet) { 62 | var self = this; 63 | return function () { 64 | if (self.manager.store.removeByValue('fq', facet)) { 65 | self.doRequest(); 66 | } 67 | return false; 68 | }; 69 | } 70 | }); 71 | 72 | })(jQuery); 73 | 74 | })); 75 | -------------------------------------------------------------------------------- /examples/reuters/widgets/AutocompleteWidget.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.AutocompleteWidget = AjaxSolr.AbstractTextWidget.extend({ 4 | afterRequest: function () { 5 | $(this.target).find('input').unbind().removeData('events').val(''); 6 | 7 | var self = this; 8 | 9 | var callback = function (response) { 10 | var list = []; 11 | for (var i = 0; i < self.fields.length; i++) { 12 | var field = self.fields[i]; 13 | for (var facet in response.facet_counts.facet_fields[field]) { 14 | list.push({ 15 | field: field, 16 | value: facet, 17 | label: facet + ' (' + response.facet_counts.facet_fields[field][facet] + ') - ' + field 18 | }); 19 | } 20 | } 21 | 22 | self.requestSent = false; 23 | $(self.target).find('input').autocomplete('destroy').autocomplete({ 24 | source: list, 25 | select: function(event, ui) { 26 | if (ui.item) { 27 | self.requestSent = true; 28 | if (self.manager.store.addByValue('fq', ui.item.field + ':' + AjaxSolr.Parameter.escapeValue(ui.item.value))) { 29 | self.doRequest(); 30 | } 31 | } 32 | } 33 | }); 34 | 35 | // This has lower priority so that requestSent is set. 36 | $(self.target).find('input').bind('keydown', function(e) { 37 | if (self.requestSent === false && e.which == 13) { 38 | var value = $(this).val(); 39 | if (value && self.set(value)) { 40 | self.doRequest(); 41 | } 42 | } 43 | }); 44 | } // end callback 45 | 46 | var params = [ 'rows=0&facet=true&facet.limit=-1&facet.mincount=1&json.nl=map' ]; 47 | for (var i = 0; i < this.fields.length; i++) { 48 | params.push('facet.field=' + this.fields[i]); 49 | } 50 | var values = this.manager.store.values('fq'); 51 | for (var i = 0; i < values.length; i++) { 52 | params.push('fq=' + encodeURIComponent(values[i])); 53 | } 54 | params.push('q=' + this.manager.store.get('q').val()); 55 | $.getJSON(this.manager.solrUrl + 'select?' + params.join('&') + '&wt=json&json.wrf=?', {}, callback); 56 | } 57 | }); 58 | 59 | })(jQuery); 60 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.8.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | }); 9 | Manager.addWidget(new AjaxSolr.ResultWidget({ 10 | id: 'result', 11 | target: '#docs' 12 | })); 13 | Manager.addWidget(new AjaxSolr.PagerWidget({ 14 | id: 'pager', 15 | target: '#pager', 16 | prevLabel: '<', 17 | nextLabel: '>', 18 | innerWindow: 1, 19 | renderHeader: function (perPage, offset, total) { 20 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 21 | } 22 | })); 23 | var fields = [ 'topics', 'organisations', 'exchanges' ]; 24 | for (var i = 0, l = fields.length; i < l; i++) { 25 | Manager.addWidget(new AjaxSolr.TagcloudWidget({ 26 | id: fields[i], 27 | target: '#' + fields[i], 28 | field: fields[i] 29 | })); 30 | } 31 | Manager.addWidget(new AjaxSolr.CurrentSearchWidget({ 32 | id: 'currentsearch', 33 | target: '#selection' 34 | })); 35 | Manager.addWidget(new AjaxSolr.AutocompleteWidget({ 36 | id: 'text', 37 | target: '#search', 38 | fields: [ 'topics', 'organisations', 'exchanges' ] 39 | })); 40 | Manager.addWidget(new AjaxSolr.CountryCodeWidget({ 41 | id: 'countries', 42 | target: '#countries', 43 | field: 'countryCodes' 44 | })); 45 | Manager.init(); 46 | Manager.store.addByValue('q', '*:*'); 47 | var params = { 48 | facet: true, 49 | 'facet.field': [ 'topics', 'organisations', 'exchanges', 'countryCodes' ], 50 | 'facet.limit': 20, 51 | 'facet.mincount': 1, 52 | 'f.topics.facet.limit': 50, 53 | 'f.countryCodes.facet.limit': -1, 54 | 'json.nl': 'map' 55 | }; 56 | for (var name in params) { 57 | Manager.store.addByValue(name, params[name]); 58 | } 59 | Manager.doRequest(); 60 | }); 61 | 62 | $.fn.showIf = function (condition) { 63 | if (condition) { 64 | return this.show(); 65 | } 66 | else { 67 | return this.hide(); 68 | } 69 | } 70 | 71 | })(jQuery); 72 | -------------------------------------------------------------------------------- /examples/reuters/index.2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
          21 | 25 | 26 |
          27 |
          28 | 32 |
          33 |
          34 |
          35 | 36 |
          37 |

          Current Selection

          38 |
            39 | 40 |

            Search

            41 | (press ESC to close suggestions) 42 | 45 | 46 |

            Top Topics

            47 |
            48 | 49 |

            Top Organisations

            50 |
            51 | 52 |

            Top Exchanges

            53 |
            54 | 55 |

            By Country

            56 |
            57 |
            58 | 59 |

            By Date

            60 |
            61 | 62 |
            63 |
            64 |
            65 |
            66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/reuters/index.2.0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
            21 | 25 | 26 |
            27 |
            28 | 32 |
            33 |
            34 |
            35 | 36 |
            37 |

            Current Selection

            38 |
              39 | 40 |

              Search

              41 | (press ESC to close suggestions) 42 | 45 | 46 |

              Top Topics

              47 |
              48 | 49 |

              Top Organisations

              50 |
              51 | 52 |

              Top Exchanges

              53 |
              54 | 55 |

              By Country

              56 |
              57 |
              58 | 59 |

              By Date

              60 |
              61 | 62 |
              63 |
              64 |
              65 |
              66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/reuters/index.3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
              22 | 26 | 27 |
              28 |
              29 | 33 |
              34 |
              35 |
              36 | 37 |
              38 |

              Current Selection

              39 |
                40 | 41 |

                Search

                42 | (press ESC to close suggestions) 43 | 46 | 47 |

                Top Topics

                48 |
                49 | 50 |

                Top Organisations

                51 |
                52 | 53 |

                Top Exchanges

                54 |
                55 | 56 |

                By Country

                57 |
                58 |
                59 | 60 |

                By Date

                61 |
                62 | 63 |
                64 |
                65 |
                66 |
                67 | 68 | 69 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/AutocompleteWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractTextWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.AutocompleteWidget = AjaxSolr.AbstractTextWidget.extend({ 13 | afterRequest: function () { 14 | $(this.target).find('input').unbind().removeData('events').val(''); 15 | 16 | var self = this; 17 | 18 | var callback = function (response) { 19 | var list = []; 20 | for (var i = 0; i < self.fields.length; i++) { 21 | var field = self.fields[i]; 22 | for (var facet in response.facet_counts.facet_fields[field]) { 23 | list.push({ 24 | field: field, 25 | value: facet, 26 | label: facet + ' (' + response.facet_counts.facet_fields[field][facet] + ') - ' + field 27 | }); 28 | } 29 | } 30 | 31 | self.requestSent = false; 32 | $(self.target).find('input').autocomplete('destroy').autocomplete({ 33 | source: list, 34 | select: function(event, ui) { 35 | if (ui.item) { 36 | self.requestSent = true; 37 | if (self.manager.store.addByValue('fq', ui.item.field + ':' + AjaxSolr.Parameter.escapeValue(ui.item.value))) { 38 | self.doRequest(); 39 | } 40 | } 41 | } 42 | }); 43 | 44 | // This has lower priority so that requestSent is set. 45 | $(self.target).find('input').bind('keydown', function(e) { 46 | if (self.requestSent === false && e.which == 13) { 47 | var value = $(this).val(); 48 | if (value && self.set(value)) { 49 | self.doRequest(); 50 | } 51 | } 52 | }); 53 | } // end callback 54 | 55 | var params = [ 'rows=0&facet=true&facet.limit=-1&facet.mincount=1&json.nl=map' ]; 56 | for (var i = 0; i < this.fields.length; i++) { 57 | params.push('facet.field=' + this.fields[i]); 58 | } 59 | var values = this.manager.store.values('fq'); 60 | for (var i = 0; i < values.length; i++) { 61 | params.push('fq=' + encodeURIComponent(values[i])); 62 | } 63 | params.push('q=' + this.manager.store.get('q').val()); 64 | $.getJSON(this.manager.solrUrl + 'select?' + params.join('&') + '&wt=json&json.wrf=?', {}, callback); 65 | } 66 | }); 67 | 68 | })(jQuery); 69 | 70 | })); 71 | -------------------------------------------------------------------------------- /examples/reuters/index.4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
                24 | 28 | 29 |
                30 |
                31 | 35 |
                36 |
                37 |
                38 | 39 |
                40 |

                Current Selection

                41 |
                  42 | 43 |

                  Search

                  44 | (press ESC to close suggestions) 45 | 48 | 49 |

                  Top Topics

                  50 |
                  51 | 52 |

                  Top Organisations

                  53 |
                  54 | 55 |

                  Top Exchanges

                  56 |
                  57 | 58 |

                  By Country

                  59 |
                  60 |
                  61 | 62 |

                  By Date

                  63 |
                  64 | 65 |
                  66 |
                  67 |
                  68 |
                  69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/reuters/index.5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
                  25 | 29 | 30 |
                  31 |
                  32 | 36 |
                  37 |
                  38 |
                  39 | 40 |
                  41 |

                  Current Selection

                  42 |
                    43 | 44 |

                    Search

                    45 | (press ESC to close suggestions) 46 | 49 | 50 |

                    Top Topics

                    51 |
                    52 | 53 |

                    Top Organisations

                    54 |
                    55 | 56 |

                    Top Exchanges

                    57 |
                    58 | 59 |

                    By Country

                    60 |
                    61 |
                    62 | 63 |

                    By Date

                    64 |
                    65 | 66 |
                    67 |
                    68 |
                    69 |
                    70 | 71 | 72 | -------------------------------------------------------------------------------- /examples/reuters/widgets/CountryCodeWidget.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.CountryCodeWidget = AjaxSolr.AbstractFacetWidget.extend({ 4 | afterRequest: function () { 5 | var self = this; 6 | 7 | $(this.target).empty(); 8 | 9 | var maps = { 10 | world: 'view the World', 11 | africa: 'view Africa', 12 | asia: 'view Asia', 13 | europe: 'view Europe', 14 | middle_east: 'view the Middle East', 15 | south_america: 'view South America', 16 | usa: 'view North America' 17 | }; 18 | $(this.target).append(this.template('region', maps)); 19 | 20 | $(this.target).find('#region').change(function () { 21 | $(self.target).find('img').hide(); 22 | $('#' + self.id + $(this).val()).show(); 23 | }); 24 | 25 | var maxCount = 0; 26 | var options = { '': '--select--' }; 27 | for (var facet in this.manager.response.facet_counts.facet_fields[this.field]) { 28 | if (facet.length == 2) { // only display country codes 29 | var count = this.manager.response.facet_counts.facet_fields[this.field][facet]; 30 | if (count > maxCount) { 31 | maxCount = count; 32 | } 33 | options[facet] = facet + ' (' + count + ')'; 34 | } 35 | } 36 | $(this.target).append(this.template('country', options)); 37 | 38 | $(this.target).find('#country').change(function () { 39 | var value = $(this).val(); 40 | if (value && self.add(value)) { 41 | self.doRequest(); 42 | } 43 | }); 44 | 45 | var chd = []; 46 | var chld = ''; 47 | for (var facet in this.manager.response.facet_counts.facet_fields[this.field]) { 48 | if (facet.length == 2) { // only display country codes 49 | chd.push(parseInt(this.manager.response.facet_counts.facet_fields[this.field][facet] / maxCount * 100) + '.0'); 50 | chld += facet; 51 | } 52 | } 53 | for (var value in maps) { 54 | var src = 'http://chart.apis.google.com/chart?chco=f5f5f5,edf0d4,6c9642,365e24,13390a&chd=t:' + chd.join(',') + '&chf=bg,s,eaf7fe&chtm=' + value + '&chld=' + chld + '&chs=350x180&cht=t'; 55 | $('').attr('id', this.id + value).showIf(value == 'world').attr('src', src).appendTo(this.target); 56 | } 57 | }, 58 | 59 | template: function (name, container) { 60 | var options = []; 61 | for (var value in container) { 62 | options.push(''); 63 | } 64 | return ''; 65 | } 66 | }); 67 | 68 | })(jQuery); 69 | -------------------------------------------------------------------------------- /core/AbstractSpatialWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * Offers an interface to the local parameters used by the Spatial Solr plugin. 12 | * 13 | * @see http://www.jteam.nl/news/spatialsolr 14 | * 15 | * @class AbstractSpatialWidget 16 | * @augments AjaxSolr.AbstractWidget 17 | */ 18 | AjaxSolr.AbstractSpatialWidget = AjaxSolr.AbstractWidget.extend( 19 | /** @lends AjaxSolr.AbstractSpatialWidget.prototype */ 20 | { 21 | /** 22 | * Sets the Spatial Solr local parameters. 23 | * 24 | * @param {Object} params The local parameters to set. 25 | * @param {Number} params.lat Latitude of the center of the search area. 26 | * @param {Number} params.lng Longitude of the center of the search area. 27 | * @param {Number} params.radius Radius of the search area. 28 | * @param {String} [params.unit] Unit the distances should be calculated in: 29 | * "km" or "miles". 30 | * @param {String} [params.calc] GeoDistanceCalculator that will be 31 | * used to calculate the distances. "arc" for 32 | * ArchGeoDistanceCalculator and "plane" for 33 | * PlaneGeoDistanceCalculator. 34 | * @param {Number} [params.threadCount] Number of threads that will be used 35 | * by the ThreadedDistanceFilter. 36 | */ 37 | set: function (params) { 38 | this.manager.store.get('q').local('type', 'spatial'); 39 | this.manager.store.get('q').local('lat', params.lat); 40 | this.manager.store.get('q').local('long', params.lng); 41 | this.manager.store.get('q').local('radius', params.radius); 42 | if (params.unit !== undefined) { 43 | this.manager.store.get('q').local('unit', params.unit); 44 | } 45 | if (params.calc !== undefined) { 46 | this.manager.store.get('q').local('calc', params.calc); 47 | } 48 | if (params.threadCount !== undefined) { 49 | this.manager.store.get('q').local('threadCount', params.threadCount); 50 | } 51 | }, 52 | 53 | /** 54 | * Removes the Spatial Solr local parameters. 55 | */ 56 | clear: function () { 57 | this.manager.store.get('q').remove('type'); 58 | this.manager.store.get('q').remove('lat'); 59 | this.manager.store.get('q').remove('long'); 60 | this.manager.store.get('q').remove('radius'); 61 | this.manager.store.get('q').remove('unit'); 62 | this.manager.store.get('q').remove('calc'); 63 | this.manager.store.get('q').remove('threadCount'); 64 | } 65 | }); 66 | 67 | })); 68 | -------------------------------------------------------------------------------- /core/AbstractWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/Core'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * Baseclass for all widgets. 12 | * 13 | * Provides abstract hooks for child classes. 14 | * 15 | * @param properties A map of fields to set. May be new or public fields. 16 | * @class AbstractWidget 17 | */ 18 | AjaxSolr.AbstractWidget = AjaxSolr.Class.extend( 19 | /** @lends AjaxSolr.AbstractWidget.prototype */ 20 | { 21 | /** 22 | * @param {Object} attributes 23 | * @param {String} attributes.id A unique identifier of this widget. 24 | * @param {String} [attributes.target] The CSS selector for this widget's 25 | * target HTML element, e.g. a specific div or ul. A 26 | * Widget is usually implemented to perform all its UI changes relative to 27 | * its target HTML element. 28 | * @param {Number} [attributes.start] The offset parameter. Set this field to 29 | * make the widget reset the offset parameter to the given value on each 30 | * request. 31 | * @param {String} [attributes.servlet] The Solr servlet for this widget. You 32 | * may prepend the servlet with a core if using multiple cores. If none is 33 | * set, it will default to the manager's servlet. 34 | */ 35 | constructor: function (attributes) { 36 | AjaxSolr.extend(this, { 37 | id: null, 38 | target: null, 39 | start: undefined, 40 | servlet: undefined, 41 | // A reference to the widget's manager. 42 | manager: null 43 | }, attributes); 44 | }, 45 | 46 | /** 47 | * An abstract hook for child implementations. 48 | * 49 | *

                    This method should do any necessary one-time initializations.

                    50 | */ 51 | init: function () {}, 52 | 53 | /** 54 | * An abstract hook for child implementations. 55 | * 56 | *

                    This method is executed before the Solr request is sent.

                    57 | */ 58 | beforeRequest: function () {}, 59 | 60 | /** 61 | * An abstract hook for child implementations. 62 | * 63 | *

                    This method is executed after the Solr response is received.

                    64 | */ 65 | afterRequest: function () {}, 66 | 67 | /** 68 | * A proxy to the manager's doRequest method. 69 | * 70 | * @param {Boolean} [start] The Solr start offset parameter. 71 | * @param {String} [servlet] The Solr servlet to send the request to. 72 | */ 73 | doRequest: function (start, servlet) { 74 | this.manager.doRequest(start || this.start, servlet || this.servlet); 75 | } 76 | }); 77 | 78 | })); 79 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.9.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | }); 9 | Manager.addWidget(new AjaxSolr.ResultWidget({ 10 | id: 'result', 11 | target: '#docs' 12 | })); 13 | Manager.addWidget(new AjaxSolr.PagerWidget({ 14 | id: 'pager', 15 | target: '#pager', 16 | prevLabel: '<', 17 | nextLabel: '>', 18 | innerWindow: 1, 19 | renderHeader: function (perPage, offset, total) { 20 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 21 | } 22 | })); 23 | var fields = [ 'topics', 'organisations', 'exchanges' ]; 24 | for (var i = 0, l = fields.length; i < l; i++) { 25 | Manager.addWidget(new AjaxSolr.TagcloudWidget({ 26 | id: fields[i], 27 | target: '#' + fields[i], 28 | field: fields[i] 29 | })); 30 | } 31 | Manager.addWidget(new AjaxSolr.CurrentSearchWidget({ 32 | id: 'currentsearch', 33 | target: '#selection' 34 | })); 35 | Manager.addWidget(new AjaxSolr.AutocompleteWidget({ 36 | id: 'text', 37 | target: '#search', 38 | fields: [ 'topics', 'organisations', 'exchanges' ] 39 | })); 40 | Manager.addWidget(new AjaxSolr.CountryCodeWidget({ 41 | id: 'countries', 42 | target: '#countries', 43 | field: 'countryCodes' 44 | })); 45 | Manager.addWidget(new AjaxSolr.CalendarWidget({ 46 | id: 'calendar', 47 | target: '#calendar', 48 | field: 'date' 49 | })); 50 | Manager.init(); 51 | Manager.store.addByValue('q', '*:*'); 52 | var params = { 53 | facet: true, 54 | 'facet.field': [ 'topics', 'organisations', 'exchanges', 'countryCodes' ], 55 | 'facet.limit': 20, 56 | 'facet.mincount': 1, 57 | 'f.topics.facet.limit': 50, 58 | 'f.countryCodes.facet.limit': -1, 59 | 'facet.date': 'date', 60 | 'facet.date.start': '1987-02-26T00:00:00.000Z/DAY', 61 | 'facet.date.end': '1987-10-20T00:00:00.000Z/DAY+1DAY', 62 | 'facet.date.gap': '+1DAY', 63 | 'json.nl': 'map' 64 | }; 65 | for (var name in params) { 66 | Manager.store.addByValue(name, params[name]); 67 | } 68 | Manager.doRequest(); 69 | }); 70 | 71 | $.fn.showIf = function (condition) { 72 | if (condition) { 73 | return this.show(); 74 | } 75 | else { 76 | return this.hide(); 77 | } 78 | } 79 | 80 | })(jQuery); 81 | -------------------------------------------------------------------------------- /examples/reuters/index.6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
                    27 | 31 | 32 |
                    33 |
                    34 | 38 |
                    39 |
                    40 |
                    41 | 42 |
                    43 |

                    Current Selection

                    44 |
                      45 | 46 |

                      Search

                      47 | (press ESC to close suggestions) 48 | 51 | 52 |

                      Top Topics

                      53 |
                      54 | 55 |

                      Top Organisations

                      56 |
                      57 | 58 |

                      Top Exchanges

                      59 |
                      60 | 61 |

                      By Country

                      62 |
                      63 |
                      64 | 65 |

                      By Date

                      66 |
                      67 | 68 |
                      69 |
                      70 |
                      71 |
                      72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/reuters/index.7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
                      27 | 31 | 32 |
                      33 |
                      34 | 38 |
                      39 |
                      40 |
                      41 | 42 |
                      43 |

                      Current Selection

                      44 |
                        45 | 46 |

                        Search

                        47 | (press ESC to close suggestions) 48 | 51 | 52 |

                        Top Topics

                        53 |
                        54 | 55 |

                        Top Organisations

                        56 |
                        57 | 58 |

                        Top Exchanges

                        59 |
                        60 | 61 |

                        By Country

                        62 |
                        63 |
                        64 | 65 |

                        By Date

                        66 |
                        67 | 68 |
                        69 |
                        70 |
                        71 |
                        72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/reuters/index.7.0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
                        27 | 31 | 32 |
                        33 |
                        34 | 38 |
                        39 |
                        40 |
                        41 | 42 |
                        43 |

                        Current Selection

                        44 |
                          45 | 46 |

                          Search

                          47 | (press ESC to close suggestions) 48 | 51 | 52 |

                          Top Topics

                          53 |
                          54 | 55 |

                          Top Organisations

                          56 |
                          57 | 58 |

                          Top Exchanges

                          59 |
                          60 | 61 |

                          By Country

                          62 |
                          63 |
                          64 | 65 |

                          By Date

                          66 |
                          67 | 68 |
                          69 |
                          70 |
                          71 |
                          72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/reuters/index.8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
                          28 | 32 | 33 |
                          34 |
                          35 | 39 |
                          40 |
                          41 |
                          42 | 43 |
                          44 |

                          Current Selection

                          45 |
                            46 | 47 |

                            Search

                            48 | (press ESC to close suggestions) 49 | 52 | 53 |

                            Top Topics

                            54 |
                            55 | 56 |

                            Top Organisations

                            57 |
                            58 | 59 |

                            Top Exchanges

                            60 |
                            61 | 62 |

                            By Country

                            63 |
                            64 |
                            65 | 66 |

                            By Date

                            67 |
                            68 | 69 |
                            70 |
                            71 |
                            72 |
                            73 | 74 | 75 | -------------------------------------------------------------------------------- /examples/reuters/index.8.0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
                            28 | 32 | 33 |
                            34 |
                            35 | 39 |
                            40 |
                            41 |
                            42 | 43 |
                            44 |

                            Current Selection

                            45 |
                              46 | 47 |

                              Search

                              48 | (press ESC to close suggestions) 49 | 52 | 53 |

                              Top Topics

                              54 |
                              55 | 56 |

                              Top Organisations

                              57 |
                              58 | 59 |

                              Top Exchanges

                              60 |
                              61 | 62 |

                              By Country

                              63 |
                              64 |
                              65 | 66 |

                              By Date

                              67 |
                              68 | 69 |
                              70 |
                              71 |
                              72 |
                              73 | 74 | 75 | -------------------------------------------------------------------------------- /examples/reuters/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
                              29 | 33 | 34 |
                              35 |
                              36 | 40 |
                              41 |
                              42 |
                              43 | 44 |
                              45 |

                              Current Selection

                              46 |
                                47 | 48 |

                                Search

                                49 | (press ESC to close suggestions) 50 | 53 | 54 |

                                Top Topics

                                55 |
                                56 | 57 |

                                Top Organisations

                                58 |
                                59 | 60 |

                                Top Exchanges

                                61 |
                                62 | 63 |

                                By Country

                                64 |
                                65 |
                                66 | 67 |

                                By Date

                                68 |
                                69 | 70 |
                                71 |
                                72 |
                                73 |
                                74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/reuters/index.9.0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
                                29 | 33 | 34 |
                                35 |
                                36 | 40 |
                                41 |
                                42 |
                                43 | 44 |
                                45 |

                                Current Selection

                                46 |
                                  47 | 48 |

                                  Search

                                  49 | (press ESC to close suggestions) 50 | 53 | 54 |

                                  Top Topics

                                  55 |
                                  56 | 57 |

                                  Top Organisations

                                  58 |
                                  59 | 60 |

                                  Top Exchanges

                                  61 |
                                  62 | 63 |

                                  By Country

                                  64 |
                                  65 |
                                  66 | 67 |

                                  By Date

                                  68 |
                                  69 | 70 |
                                  71 |
                                  72 |
                                  73 |
                                  74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/reuters/index.9.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
                                  29 | 33 | 34 |
                                  35 |
                                  36 | 40 |
                                  41 |
                                  42 |
                                  43 | 44 |
                                  45 |

                                  Current Selection

                                  46 |
                                    47 | 48 |

                                    Search

                                    49 | (press ESC to close suggestions) 50 | 53 | 54 |

                                    Top Topics

                                    55 |
                                    56 | 57 |

                                    Top Organisations

                                    58 |
                                    59 | 60 |

                                    Top Exchanges

                                    61 |
                                    62 | 63 |

                                    By Country

                                    64 |
                                    65 |
                                    66 | 67 |

                                    By Date

                                    68 |
                                    69 | 70 |
                                    71 |
                                    72 |
                                    73 |
                                    74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/reuters/index.f.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
                                    29 | 33 | 34 |
                                    35 |
                                    36 | 40 |
                                    41 |
                                    42 |
                                    43 | 44 |
                                    45 |

                                    Current Selection

                                    46 |
                                      47 | 48 |

                                      Search

                                      49 | (press ESC to close suggestions) 50 | 53 | 54 |

                                      Top Topics

                                      55 |
                                      56 | 57 |

                                      Top Organisations

                                      58 |
                                      59 | 60 |

                                      Top Exchanges

                                      61 |
                                      62 | 63 |

                                      By Country

                                      64 |
                                      65 |
                                      66 | 67 |

                                      By Date

                                      68 |
                                      69 | 70 |
                                      71 |
                                      72 |
                                      73 |
                                      74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/CountryCodeWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractFacetWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.CountryCodeWidget = AjaxSolr.AbstractFacetWidget.extend({ 13 | afterRequest: function () { 14 | var self = this; 15 | 16 | $(this.target).empty(); 17 | 18 | var maps = { 19 | world: 'view the World', 20 | africa: 'view Africa', 21 | asia: 'view Asia', 22 | europe: 'view Europe', 23 | middle_east: 'view the Middle East', 24 | south_america: 'view South America', 25 | usa: 'view North America' 26 | }; 27 | $(this.target).append(this.template('region', maps)); 28 | 29 | $(this.target).find('#region').change(function () { 30 | $(self.target).find('img').hide(); 31 | $('#' + self.id + $(this).val()).show(); 32 | }); 33 | 34 | var maxCount = 0; 35 | var options = { '': '--select--' }; 36 | for (var facet in this.manager.response.facet_counts.facet_fields[this.field]) { 37 | if (facet.length == 2) { // only display country codes 38 | var count = this.manager.response.facet_counts.facet_fields[this.field][facet]; 39 | if (count > maxCount) { 40 | maxCount = count; 41 | } 42 | options[facet] = facet + ' (' + count + ')'; 43 | } 44 | } 45 | $(this.target).append(this.template('country', options)); 46 | 47 | $(this.target).find('#country').change(function () { 48 | var value = $(this).val(); 49 | if (value && self.add(value)) { 50 | self.doRequest(); 51 | } 52 | }); 53 | 54 | var chd = []; 55 | var chld = ''; 56 | for (var facet in this.manager.response.facet_counts.facet_fields[this.field]) { 57 | if (facet.length == 2) { // only display country codes 58 | chd.push(parseInt(this.manager.response.facet_counts.facet_fields[this.field][facet] / maxCount * 100) + '.0'); 59 | chld += facet; 60 | } 61 | } 62 | for (var value in maps) { 63 | var src = 'http://chart.apis.google.com/chart?chco=f5f5f5,edf0d4,6c9642,365e24,13390a&chd=t:' + chd.join(',') + '&chf=bg,s,eaf7fe&chtm=' + value + '&chld=' + chld + '&chs=350x180&cht=t'; 64 | $('').attr('id', this.id + value).showIf(value == 'world').attr('src', src).appendTo(this.target); 65 | } 66 | }, 67 | 68 | template: function (name, container) { 69 | var options = []; 70 | for (var value in container) { 71 | options.push(''); 72 | } 73 | return ''; 74 | } 75 | }); 76 | 77 | })(jQuery); 78 | 79 | })); 80 | -------------------------------------------------------------------------------- /core/AbstractSpellcheckWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * Interacts with Solr's SpellCheckComponent. 12 | * 13 | * @see http://wiki.apache.org/solr/SpellCheckComponent 14 | * 15 | * @class AbstractSpellcheckWidget 16 | * @augments AjaxSolr.AbstractWidget 17 | */ 18 | AjaxSolr.AbstractSpellcheckWidget = AjaxSolr.AbstractWidget.extend( 19 | /** @lends AjaxSolr.AbstractSpellcheckWidget.prototype */ 20 | { 21 | constructor: function (attributes) { 22 | AjaxSolr.AbstractSpellcheckWidget.__super__.constructor.apply(this, arguments); 23 | AjaxSolr.extend(this, { 24 | // The suggestions. 25 | suggestions: {} 26 | }, attributes); 27 | }, 28 | 29 | /** 30 | * Uses the top suggestion for each word to return a suggested query. 31 | * 32 | * @returns {String} A suggested query. 33 | */ 34 | suggestion: function () { 35 | var suggestion = this.manager.response.responseHeader.params['spellcheck.q']; 36 | for (var word in this.suggestions) { 37 | suggestion = suggestion.replace(new RegExp(word, 'g'), this.suggestions[word][0]); 38 | } 39 | return suggestion; 40 | }, 41 | 42 | beforeRequest: function () { 43 | if (this.manager.store.get('spellcheck').val() && this.manager.store.get('q').val()) { 44 | this.manager.store.get('spellcheck.q').val(this.manager.store.get('q').val()); 45 | } 46 | else { 47 | this.manager.store.remove('spellcheck.q'); 48 | } 49 | }, 50 | 51 | afterRequest: function () { 52 | this.suggestions = {}; 53 | 54 | if (this.manager.response.spellcheck && this.manager.response.spellcheck.suggestions) { 55 | var suggestions = this.manager.response.spellcheck.suggestions, 56 | empty = true; 57 | 58 | for (var word in suggestions) { 59 | if (word == 'collation' || word == 'correctlySpelled') continue; 60 | 61 | this.suggestions[word] = []; 62 | for (var i = 0, l = suggestions[word].suggestion.length; i < l; i++) { 63 | if (this.manager.response.responseHeader.params['spellcheck.extendedResults']) { 64 | this.suggestions[word].push(suggestions[word].suggestion[i].word); 65 | } 66 | else { 67 | this.suggestions[word].push(suggestions[word].suggestion[i]); 68 | } 69 | } 70 | empty = false; 71 | } 72 | 73 | if (!empty) { 74 | this.handleSuggestions(this.manager.response); 75 | } 76 | } 77 | }, 78 | 79 | /** 80 | * An abstract hook for child implementations. 81 | * 82 | *

                                      Allow the child to handle the suggestions without parsing the response.

                                      83 | */ 84 | handleSuggestions: function () {} 85 | }); 86 | 87 | })); 88 | -------------------------------------------------------------------------------- /widgets/ParameterHistoryStore.js: -------------------------------------------------------------------------------- 1 | ;(function(history) { 2 | /** 3 | * A parameter store that stores the values of exposed parameters in the URL via History.js 4 | * to maintain the application's state. This uses the HTML5 History API for newer browsers, and 5 | * falls back to using the hash in older browsers. Don't forget to add the following (or similar) 6 | * inside your head tag: 7 | * 8 | *
                                       9 |    * 
                                      10 |    * 
                                      11 | * 12 | * Configure the manager with: 13 | * 14 | *
                                      15 |    * Manager.setStore(new AjaxSolr.ParameterHistoryStore());
                                      16 |    * 
                                      17 | * 18 | * @class ParameterHistoryStore 19 | * @augments AjaxSolr.ParameterStore 20 | * @see https://github.com/browserstate/history.js 21 | * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html 22 | */ 23 | AjaxSolr.ParameterHistoryStore = AjaxSolr.ParameterStore.extend( 24 | /** @lends AjaxSolr.ParameterHistoryStore.prototype */ 25 | { 26 | init: function () { 27 | if (this.exposed.length) { 28 | if (!history) { 29 | throw 'ParameterHistoryStore requires History.js'; 30 | } 31 | 32 | history.Adapter.bind(window, 'statechange', this.stateChangeFunction(this)); 33 | } 34 | }, 35 | 36 | /** 37 | * Stores the values of the exposed parameters in both the local hash and History.js 38 | * No other code should be made to change these two values. 39 | */ 40 | save: function () { 41 | this.hash = this.exposedString(); 42 | history.pushState({ params: this.hash }, null, '?' + this.hash); 43 | }, 44 | 45 | /** 46 | * @see ParameterStore#storedString() 47 | */ 48 | storedString: function () { 49 | var state = history.getState(); 50 | 51 | // Load the state from the History object. 52 | if (state.data && state.data.params) { 53 | return state.data.params; 54 | } 55 | 56 | // If initial load, load the state from the URL. 57 | var url = state.cleanUrl, index = url.indexOf('?'); 58 | if (index == -1) { 59 | return ''; 60 | } 61 | else { 62 | return url.substr(index + 1); 63 | } 64 | }, 65 | 66 | /** 67 | * Called when History.js detects a state change. Checks if state is different to previous state, 68 | * and if so, sends a request to Solr. This needs to check if the state has changed since it also 69 | * gets called when we call pushState above. 70 | */ 71 | stateChangeFunction: function (self) { 72 | return function () { 73 | var hash = self.storedString(); 74 | 75 | if (self.hash != hash) { 76 | self.load(); 77 | self.manager.doRequest(); 78 | } 79 | } 80 | } 81 | }); 82 | })(window.History); 83 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | // If you are using a local Solr instance with a "reuters" core, use: 9 | // solrUrl: 'http://localhost:8983/solr/reuters/' 10 | // If you are using a local Solr instance with a single core, use: 11 | // solrUrl: 'http://localhost:8983/solr/' 12 | }); 13 | Manager.addWidget(new AjaxSolr.ResultWidget({ 14 | id: 'result', 15 | target: '#docs' 16 | })); 17 | Manager.addWidget(new AjaxSolr.PagerWidget({ 18 | id: 'pager', 19 | target: '#pager', 20 | prevLabel: '<', 21 | nextLabel: '>', 22 | innerWindow: 1, 23 | renderHeader: function (perPage, offset, total) { 24 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 25 | } 26 | })); 27 | var fields = [ 'topics', 'organisations', 'exchanges' ]; 28 | for (var i = 0, l = fields.length; i < l; i++) { 29 | Manager.addWidget(new AjaxSolr.TagcloudWidget({ 30 | id: fields[i], 31 | target: '#' + fields[i], 32 | field: fields[i] 33 | })); 34 | } 35 | Manager.addWidget(new AjaxSolr.CurrentSearchWidget({ 36 | id: 'currentsearch', 37 | target: '#selection' 38 | })); 39 | Manager.addWidget(new AjaxSolr.AutocompleteWidget({ 40 | id: 'text', 41 | target: '#search', 42 | fields: [ 'topics', 'organisations', 'exchanges' ] 43 | })); 44 | Manager.addWidget(new AjaxSolr.CountryCodeWidget({ 45 | id: 'countries', 46 | target: '#countries', 47 | field: 'countryCodes' 48 | })); 49 | Manager.addWidget(new AjaxSolr.CalendarWidget({ 50 | id: 'calendar', 51 | target: '#calendar', 52 | field: 'date' 53 | })); 54 | Manager.init(); 55 | Manager.store.addByValue('q', '*:*'); 56 | var params = { 57 | facet: true, 58 | 'facet.field': [ 'topics', 'organisations', 'exchanges', 'countryCodes' ], 59 | 'facet.limit': 20, 60 | 'facet.mincount': 1, 61 | 'f.topics.facet.limit': 50, 62 | 'f.countryCodes.facet.limit': -1, 63 | 'facet.date': 'date', 64 | 'facet.date.start': '1987-02-26T00:00:00.000Z/DAY', 65 | 'facet.date.end': '1987-10-20T00:00:00.000Z/DAY+1DAY', 66 | 'facet.date.gap': '+1DAY', 67 | 'json.nl': 'map' 68 | }; 69 | for (var name in params) { 70 | Manager.store.addByValue(name, params[name]); 71 | } 72 | Manager.doRequest(); 73 | }); 74 | 75 | $.fn.showIf = function (condition) { 76 | if (condition) { 77 | return this.show(); 78 | } 79 | else { 80 | return this.hide(); 81 | } 82 | } 83 | 84 | })(jQuery); 85 | -------------------------------------------------------------------------------- /examples/reuters/index.h.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AJAX Solr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
                                      31 | 35 | 36 |
                                      37 |
                                      38 | 42 |
                                      43 |
                                      44 |
                                      45 | 46 |
                                      47 |

                                      Current Selection

                                      48 |
                                        49 | 50 |

                                        Search

                                        51 | (press ESC to close suggestions) 52 | 55 | 56 |

                                        Top Topics

                                        57 |
                                        58 | 59 |

                                        Top Organisations

                                        60 |
                                        61 | 62 |

                                        Top Exchanges

                                        63 |
                                        64 | 65 |

                                        By Country

                                        66 |
                                        67 |
                                        68 | 69 |

                                        By Date

                                        70 |
                                        71 | 72 |
                                        73 |
                                        74 |
                                        75 |
                                        76 | 77 | 78 | -------------------------------------------------------------------------------- /examples/reuters/css/reuters.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | 6 | body { 7 | background: #fff url(../images/header.gif) repeat-x; 8 | color: #333; 9 | font-family: Verdana, Arial, Helvetica, sans-serif; 10 | font-size: 12px; 11 | line-height: 18px; 12 | } 13 | img { 14 | border: none; 15 | } 16 | a { 17 | color: #6998f0; 18 | text-decoration: none; 19 | } 20 | a:hover { 21 | color: #000; 22 | text-decoration: underline; 23 | } 24 | 25 | #wrap { 26 | width: 1000px; 27 | margin: 0px auto; 28 | } 29 | .clear { 30 | clear: both; 31 | } 32 | 33 | /* header */ 34 | 35 | #header { 36 | height: 90px; 37 | } 38 | #header h1 { 39 | padding: 20px 0 5px 10px; 40 | font-size: 20px; 41 | letter-spacing: -2px; 42 | } 43 | #header h2 { 44 | padding-left: 10px; 45 | color: #666; 46 | font-size: 14px; 47 | font-weight: 100; 48 | } 49 | 50 | /* right */ 51 | 52 | .right { 53 | float: right; 54 | width: 538px; 55 | padding: 10px 20px 10px 40px; 56 | border-left: 1px solid #aaa; 57 | text-align: justify; 58 | } 59 | .right h2 { 60 | padding: 15px 0 5px 0; 61 | color: #B4241B; 62 | font-size: 20px; 63 | font-weight: 100; 64 | letter-spacing: -2px; 65 | } 66 | #result a { 67 | margin-right: 5px; 68 | } 69 | #navigation { 70 | border-bottom: 1px solid #000; 71 | } 72 | #pager, 73 | #pager-header { 74 | display: inline; 75 | } 76 | #pager li, 77 | .links li { 78 | list-style-type: none; 79 | display: inline; 80 | } 81 | #selection { 82 | padding: 10px 15px 0; 83 | } 84 | #selection li { 85 | list-style-type: none; 86 | } 87 | #docs { 88 | padding-top: 1px; 89 | } 90 | 91 | /* left */ 92 | 93 | .left { 94 | float: left; 95 | width: 390px; 96 | padding: 0 0 10px 10px; 97 | } 98 | .left h2 { 99 | clear: left; 100 | padding-top: 20px; 101 | color: #333; 102 | font-size: 13px; 103 | } 104 | #search input { 105 | width: 350px; 106 | } 107 | #calendar { 108 | width: 160px; 109 | } 110 | #search_help { 111 | font-size: 80%; 112 | } 113 | .tagcloud { 114 | padding: 10px 15px 0; 115 | } 116 | .tagcloud_item { 117 | float: left; 118 | display: block; 119 | margin-right: 4px; 120 | color: #6998f0; 121 | } 122 | .tagcloud_item:hover { 123 | color: #f00; 124 | } 125 | 126 | /* tagcloud */ 127 | 128 | a.tagcloud_size_0 { 129 | font-size: 80%; 130 | } 131 | a.tagcloud_size_1 { 132 | font-size: 90%; 133 | } 134 | a.tagcloud_size_2 { 135 | font-size: 100%; 136 | }Rst 137 | a.tagcloud_size_3 { 138 | font-size: 110%; 139 | } 140 | a.tagcloud_size_4 { 141 | font-size: 120%; 142 | } 143 | a.tagcloud_size_5 { 144 | font-size: 130%; 145 | } 146 | a.tagcloud_size_6 { 147 | font-size: 140%; 148 | } 149 | a.tagcloud_size_7 { 150 | font-size: 150% 151 | } 152 | a.tagcloud_size_8 { 153 | font-size: 160%; 154 | } 155 | a.tagcloud_size_9 { 156 | font-size: 170%; 157 | } 158 | a.tagcloud_size_10 { 159 | font-size: 180%; 160 | } 161 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/css/reuters.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | 6 | body { 7 | background: #fff url(../images/header.gif) repeat-x; 8 | color: #333; 9 | font-family: Verdana, Arial, Helvetica, sans-serif; 10 | font-size: 12px; 11 | line-height: 18px; 12 | } 13 | img { 14 | border: none; 15 | } 16 | a { 17 | color: #6998f0; 18 | text-decoration: none; 19 | } 20 | a:hover { 21 | color: #000; 22 | text-decoration: underline; 23 | } 24 | 25 | #wrap { 26 | width: 1000px; 27 | margin: 0px auto; 28 | } 29 | .clear { 30 | clear: both; 31 | } 32 | 33 | /* header */ 34 | 35 | #header { 36 | height: 90px; 37 | } 38 | #header h1 { 39 | padding: 20px 0 5px 10px; 40 | font-size: 20px; 41 | letter-spacing: -2px; 42 | } 43 | #header h2 { 44 | padding-left: 10px; 45 | color: #666; 46 | font-size: 14px; 47 | font-weight: 100; 48 | } 49 | 50 | /* right */ 51 | 52 | .right { 53 | float: right; 54 | width: 538px; 55 | padding: 10px 20px 10px 40px; 56 | border-left: 1px solid #aaa; 57 | text-align: justify; 58 | } 59 | .right h2 { 60 | padding: 15px 0 5px 0; 61 | color: #B4241B; 62 | font-size: 20px; 63 | font-weight: 100; 64 | letter-spacing: -2px; 65 | } 66 | #result a { 67 | margin-right: 5px; 68 | } 69 | #navigation { 70 | border-bottom: 1px solid #000; 71 | } 72 | #pager, 73 | #pager-header { 74 | display: inline; 75 | } 76 | #pager li, 77 | .links li { 78 | list-style-type: none; 79 | display: inline; 80 | } 81 | #selection { 82 | padding: 10px 15px 0; 83 | } 84 | #selection li { 85 | list-style-type: none; 86 | } 87 | #docs { 88 | padding-top: 1px; 89 | } 90 | 91 | /* left */ 92 | 93 | .left { 94 | float: left; 95 | width: 390px; 96 | padding: 0 0 10px 10px; 97 | } 98 | .left h2 { 99 | clear: left; 100 | padding-top: 20px; 101 | color: #333; 102 | font-size: 13px; 103 | } 104 | #search input { 105 | width: 350px; 106 | } 107 | #calendar { 108 | width: 160px; 109 | } 110 | #search_help { 111 | font-size: 80%; 112 | } 113 | .tagcloud { 114 | padding: 10px 15px 0; 115 | } 116 | .tagcloud_item { 117 | float: left; 118 | display: block; 119 | margin-right: 4px; 120 | color: #6998f0; 121 | } 122 | .tagcloud_item:hover { 123 | color: #f00; 124 | } 125 | 126 | /* tagcloud */ 127 | 128 | a.tagcloud_size_0 { 129 | font-size: 80%; 130 | } 131 | a.tagcloud_size_1 { 132 | font-size: 90%; 133 | } 134 | a.tagcloud_size_2 { 135 | font-size: 100%; 136 | }Rst 137 | a.tagcloud_size_3 { 138 | font-size: 110%; 139 | } 140 | a.tagcloud_size_4 { 141 | font-size: 120%; 142 | } 143 | a.tagcloud_size_5 { 144 | font-size: 130%; 145 | } 146 | a.tagcloud_size_6 { 147 | font-size: 140%; 148 | } 149 | a.tagcloud_size_7 { 150 | font-size: 150% 151 | } 152 | a.tagcloud_size_8 { 153 | font-size: 160%; 154 | } 155 | a.tagcloud_size_9 { 156 | font-size: 170%; 157 | } 158 | a.tagcloud_size_10 { 159 | font-size: 180%; 160 | } 161 | -------------------------------------------------------------------------------- /examples/reuters/js/reuters.h.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | (function ($) { 4 | 5 | $(function () { 6 | Manager = new AjaxSolr.Manager({ 7 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 8 | // If you are using a local Solr instance with a "reuters" core, use: 9 | // solrUrl: 'http://localhost:8983/solr/reuters/' 10 | // If you are using a local Solr instance with a single core, use: 11 | // solrUrl: 'http://localhost:8983/solr/' 12 | }); 13 | Manager.addWidget(new AjaxSolr.ResultWidget({ 14 | id: 'result', 15 | target: '#docs' 16 | })); 17 | Manager.addWidget(new AjaxSolr.PagerWidget({ 18 | id: 'pager', 19 | target: '#pager', 20 | prevLabel: '<', 21 | nextLabel: '>', 22 | innerWindow: 1, 23 | renderHeader: function (perPage, offset, total) { 24 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 25 | } 26 | })); 27 | var fields = [ 'topics', 'organisations', 'exchanges' ]; 28 | for (var i = 0, l = fields.length; i < l; i++) { 29 | Manager.addWidget(new AjaxSolr.TagcloudWidget({ 30 | id: fields[i], 31 | target: '#' + fields[i], 32 | field: fields[i] 33 | })); 34 | } 35 | Manager.addWidget(new AjaxSolr.CurrentSearchWidget({ 36 | id: 'currentsearch', 37 | target: '#selection' 38 | })); 39 | Manager.addWidget(new AjaxSolr.AutocompleteWidget({ 40 | id: 'text', 41 | target: '#search', 42 | fields: [ 'topics', 'organisations', 'exchanges' ] 43 | })); 44 | Manager.addWidget(new AjaxSolr.CountryCodeWidget({ 45 | id: 'countries', 46 | target: '#countries', 47 | field: 'countryCodes' 48 | })); 49 | Manager.addWidget(new AjaxSolr.CalendarWidget({ 50 | id: 'calendar', 51 | target: '#calendar', 52 | field: 'date' 53 | })); 54 | Manager.setStore(new AjaxSolr.ParameterHistoryStore()); 55 | Manager.store.exposed = [ 'fq', 'q', 'start' ]; 56 | Manager.init(); 57 | Manager.store.addByValue('q', '*:*'); 58 | var params = { 59 | facet: true, 60 | 'facet.field': [ 'topics', 'organisations', 'exchanges', 'countryCodes' ], 61 | 'facet.limit': 20, 62 | 'facet.mincount': 1, 63 | 'f.topics.facet.limit': 50, 64 | 'f.countryCodes.facet.limit': -1, 65 | 'facet.date': 'date', 66 | 'facet.date.start': '1987-02-26T00:00:00.000Z/DAY', 67 | 'facet.date.end': '1987-10-20T00:00:00.000Z/DAY+1DAY', 68 | 'facet.date.gap': '+1DAY', 69 | 'json.nl': 'map' 70 | }; 71 | for (var name in params) { 72 | Manager.store.addByValue(name, params[name]); 73 | } 74 | Manager.doRequest(); 75 | }); 76 | 77 | $.fn.showIf = function (condition) { 78 | if (condition) { 79 | return this.show(); 80 | } 81 | else { 82 | return this.hide(); 83 | } 84 | } 85 | 86 | })(jQuery); 87 | -------------------------------------------------------------------------------- /examples/reuters/widgets/ResultWidget.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.ResultWidget = AjaxSolr.AbstractWidget.extend({ 4 | start: 0, 5 | 6 | beforeRequest: function () { 7 | $(this.target).html($('').attr('src', 'images/ajax-loader.gif')); 8 | }, 9 | 10 | facetLinks: function (facet_field, facet_values) { 11 | var links = []; 12 | if (facet_values) { 13 | for (var i = 0, l = facet_values.length; i < l; i++) { 14 | if (facet_values[i] !== undefined) { 15 | links.push( 16 | $('') 17 | .text(facet_values[i]) 18 | .click(this.facetHandler(facet_field, facet_values[i])) 19 | ); 20 | } 21 | else { 22 | links.push('no items found in current selection'); 23 | } 24 | } 25 | } 26 | return links; 27 | }, 28 | 29 | facetHandler: function (facet_field, facet_value) { 30 | var self = this; 31 | return function () { 32 | self.manager.store.remove('fq'); 33 | self.manager.store.addByValue('fq', facet_field + ':' + AjaxSolr.Parameter.escapeValue(facet_value)); 34 | self.doRequest(0); 35 | return false; 36 | }; 37 | }, 38 | 39 | afterRequest: function () { 40 | $(this.target).empty(); 41 | for (var i = 0, l = this.manager.response.response.docs.length; i < l; i++) { 42 | var doc = this.manager.response.response.docs[i]; 43 | $(this.target).append(this.template(doc)); 44 | 45 | var items = []; 46 | items = items.concat(this.facetLinks('topics', doc.topics)); 47 | items = items.concat(this.facetLinks('organisations', doc.organisations)); 48 | items = items.concat(this.facetLinks('exchanges', doc.exchanges)); 49 | 50 | var $links = $('#links_' + doc.id); 51 | $links.empty(); 52 | for (var j = 0, m = items.length; j < m; j++) { 53 | $links.append($('
                                      • ').append(items[j])); 54 | } 55 | } 56 | }, 57 | 58 | template: function (doc) { 59 | var snippet = ''; 60 | if (doc.text.length > 300) { 61 | snippet += doc.dateline + ' ' + doc.text.substring(0, 300); 62 | snippet += '' + doc.text.substring(300); 63 | snippet += ' more'; 64 | } 65 | else { 66 | snippet += doc.dateline + ' ' + doc.text; 67 | } 68 | 69 | var output = '

                                        ' + doc.title + '

                                        '; 70 | output += ''; 71 | output += '

                                        ' + snippet + '

                                        '; 72 | return output; 73 | }, 74 | 75 | init: function () { 76 | $(document).on('click', 'a.more', function () { 77 | var $this = $(this), 78 | span = $this.parent().find('span'); 79 | 80 | if (span.is(':visible')) { 81 | span.hide(); 82 | $this.text('more'); 83 | } 84 | else { 85 | span.show(); 86 | $this.text('less'); 87 | } 88 | 89 | return false; 90 | }); 91 | } 92 | }); 93 | 94 | })(jQuery); -------------------------------------------------------------------------------- /examples/reuters/widgets/ResultWidget.f.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | AjaxSolr.ResultWidget = AjaxSolr.AbstractWidget.extend({ 4 | start: 0, 5 | 6 | beforeRequest: function () { 7 | $(this.target).html($('').attr('src', 'images/ajax-loader.gif')); 8 | }, 9 | 10 | facetLinks: function (facet_field, facet_values) { 11 | var links = []; 12 | if (facet_values) { 13 | for (var i = 0, l = facet_values.length; i < l; i++) { 14 | if (facet_values[i] !== undefined) { 15 | links = links.concat([ 16 | facet_field + ':', 17 | $('') 18 | .text(facet_values[i]) 19 | .click(this.facetHandler(facet_field, facet_values[i])) 20 | ]); 21 | } 22 | else { 23 | links.push('no items found in current selection'); 24 | } 25 | } 26 | } 27 | return links; 28 | }, 29 | 30 | facetHandler: function (facet_field, facet_value) { 31 | var self = this; 32 | return function () { 33 | self.manager.store.remove('fq'); 34 | self.manager.store.addByValue('fq', facet_field + ':' + AjaxSolr.Parameter.escapeValue(facet_value)); 35 | self.doRequest(0); 36 | return false; 37 | }; 38 | }, 39 | 40 | afterRequest: function () { 41 | $(this.target).empty(); 42 | for (var i = 0, l = this.manager.response.response.docs.length; i < l; i++) { 43 | var doc = this.manager.response.response.docs[i]; 44 | $(this.target).append(this.template(doc)); 45 | 46 | var items = []; 47 | items = items.concat(this.facetLinks('topics', doc.topics)); 48 | items = items.concat(this.facetLinks('organisations', doc.organisations)); 49 | items = items.concat(this.facetLinks('exchanges', doc.exchanges)); 50 | 51 | var $links = $('#links_' + doc.id); 52 | $links.empty(); 53 | for (var j = 0, m = items.length; j < m; j++) { 54 | $links.append($('
                                      • ').append(items[j])); 55 | } 56 | } 57 | }, 58 | 59 | template: function (doc) { 60 | var snippet = ''; 61 | if (doc.text.length > 300) { 62 | snippet += doc.dateline + ' ' + doc.text.substring(0, 300); 63 | snippet += '' + doc.text.substring(300); 64 | snippet += ' more'; 65 | } 66 | else { 67 | snippet += doc.dateline + ' ' + doc.text; 68 | } 69 | 70 | var output = '

                                        ' + doc.title + '

                                        '; 71 | output += ''; 72 | output += '

                                        ' + snippet + '

                                        '; 73 | return output; 74 | }, 75 | 76 | init: function () { 77 | $(document).on('click', 'a.more', function () { 78 | var $this = $(this), 79 | span = $this.parent().find('span'); 80 | 81 | if (span.is(':visible')) { 82 | span.hide(); 83 | $this.text('more'); 84 | } 85 | else { 86 | span.show(); 87 | $this.text('less'); 88 | } 89 | 90 | return false; 91 | }); 92 | } 93 | }); 94 | 95 | })(jQuery); -------------------------------------------------------------------------------- /core/AbstractTextWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * Baseclass for all free-text widgets. 12 | * 13 | * @class AbstractTextWidget 14 | * @augments AjaxSolr.AbstractWidget 15 | */ 16 | AjaxSolr.AbstractTextWidget = AjaxSolr.AbstractWidget.extend( 17 | /** @lends AjaxSolr.AbstractTextWidget.prototype */ 18 | { 19 | /** 20 | * @param {Object} [attributes] 21 | * @param {Number} [attributes.start] This widget will by default set the 22 | * offset parameter to 0 on each request. 23 | */ 24 | constructor: function (attributes) { 25 | AjaxSolr.AbstractTextWidget.__super__.constructor.apply(this, arguments); 26 | AjaxSolr.extend(this, { 27 | start: 0 28 | }, attributes); 29 | }, 30 | 31 | /** 32 | * Sets the main Solr query to the given string. 33 | * 34 | * @param {String} q The new Solr query. 35 | * @returns {Boolean} Whether the selection changed. 36 | */ 37 | set: function (q) { 38 | return this.changeSelection(function () { 39 | this.manager.store.get('q').val(q); 40 | }); 41 | }, 42 | 43 | /** 44 | * Sets the main Solr query to the empty string. 45 | * 46 | * @returns {Boolean} Whether the selection changed. 47 | */ 48 | clear: function () { 49 | return this.changeSelection(function () { 50 | this.manager.store.remove('q'); 51 | }); 52 | }, 53 | 54 | /** 55 | * Helper for selection functions. 56 | * 57 | * @param {Function} Selection function to call. 58 | * @returns {Boolean} Whether the selection changed. 59 | */ 60 | changeSelection: function (func) { 61 | var before = this.manager.store.get('q').val(); 62 | func.apply(this); 63 | var after = this.manager.store.get('q').val(); 64 | if (after !== before) { 65 | this.afterChangeSelection(after); 66 | } 67 | return after !== before; 68 | }, 69 | 70 | /** 71 | * An abstract hook for child implementations. 72 | * 73 | *

                                        This method is executed after the main Solr query changes.

                                        74 | * 75 | * @param {String} value The current main Solr query. 76 | */ 77 | afterChangeSelection: function (value) {}, 78 | 79 | /** 80 | * Returns a function to unset the main Solr query. 81 | * 82 | * @returns {Function} 83 | */ 84 | unclickHandler: function () { 85 | var self = this; 86 | return function () { 87 | if (self.clear()) { 88 | self.doRequest(); 89 | } 90 | return false; 91 | } 92 | }, 93 | 94 | /** 95 | * Returns a function to set the main Solr query. 96 | * 97 | * @param {String} value The new Solr query. 98 | * @returns {Function} 99 | */ 100 | clickHandler: function (q) { 101 | var self = this; 102 | return function () { 103 | if (self.set(q)) { 104 | self.doRequest(); 105 | } 106 | return false; 107 | } 108 | } 109 | }); 110 | 111 | })); 112 | -------------------------------------------------------------------------------- /widgets/ParameterYUIStore.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A parameter store that stores the values of exposed parameters using the YUI 3 | * History Manager to maintain the application's state. Don't forget to add the 4 | * following inside your head tag: 5 | * 6 | *
                                         7 |  * 
                                         8 |  * 
                                         9 |  * 
                                        10 |  * 
                                        11 | * 12 | * And the following inside your body tag: 13 | * 14 | *
                                        15 |  * 
                                        16 |  * 
                                        17 |  * 
                                        18 | * 19 | * Configure the manager with: 20 | * 21 | *
                                        22 |  * Manager.setStore(new AjaxSolr.ParameterYUIStore());
                                        23 |  * 
                                        24 | * 25 | * @see http://developer.yahoo.com/yui/history/ 26 | * @class ParameterYUIStore 27 | * @augments AjaxSolr.ParameterStore 28 | */ 29 | AjaxSolr.ParameterYUIStore = AjaxSolr.ParameterStore.extend( 30 | /** @lends AjaxSolr.ParameterYUIStore.prototype */ 31 | { 32 | /** 33 | * @param {Object} [attributes] 34 | * @param {String} [attributes.module] The name of the YUI History Manager 35 | * module to use for the parameter store. Defaults to "q". 36 | */ 37 | constructor: function (attributes) { 38 | AjaxSolr.ParameterYUIStore.__super__.constructor.apply(this, arguments); 39 | AjaxSolr.extend(this, { 40 | module: 'q', 41 | // Whether the YUI History Manager is initialized. 42 | initialized: false, 43 | // Whether the parameter store is curring loading state. 44 | loading: false, 45 | // Whether the parameter store is curring saving state. 46 | saving: false 47 | }, attributes); 48 | }, 49 | 50 | /** 51 | * Initializes the YUI History Manager. 52 | */ 53 | init: function () { 54 | if (this.exposed.length) { 55 | var self = this; 56 | YAHOO.util.History.register(this.module, YAHOO.util.History.getBookmarkedState(this.module) || this.exposedString(), function () { 57 | if (!self.saving) { 58 | self.loading = true; 59 | self.load(); 60 | self.manager.doRequest(); 61 | self.loading = false; 62 | } 63 | }); 64 | YAHOO.util.History.onReady(function () { 65 | self.initialized = true; 66 | self.load(); 67 | self.manager.doRequest(); 68 | }); 69 | YAHOO.util.History.initialize('yui-history-field', 'yui-history-iframe'); 70 | } 71 | }, 72 | 73 | /** 74 | * Stores the values of the exposed parameters in the YUI History Manager. 75 | */ 76 | save: function () { 77 | if (!self.loading) { 78 | this.saving = true; 79 | YAHOO.util.History.navigate(this.module, this.exposedString()); 80 | this.saving = false; 81 | } 82 | }, 83 | 84 | /** 85 | * @see ParameterStore#storedString() 86 | */ 87 | storedString: function () { 88 | return this.initialized ? YAHOO.util.History.getCurrentState(this.module) : this.exposedString(); 89 | } 90 | }); 91 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/js/reuters.js: -------------------------------------------------------------------------------- 1 | var Manager; 2 | 3 | require.config({ 4 | paths: { 5 | core: '../../../core', 6 | managers: '../../../managers', 7 | widgets: '../../../widgets', 8 | reuters: '../widgets' 9 | }, 10 | urlArgs: "bust=" + (new Date()).getTime() 11 | }); 12 | 13 | (function ($) { 14 | 15 | define([ 16 | 'managers/Manager.jquery', 17 | 'core/ParameterStore', 18 | 'reuters/ResultWidget', 19 | 'reuters/TagcloudWidget', 20 | 'reuters/CurrentSearchWidget.9', 21 | 'reuters/AutocompleteWidget', 22 | 'reuters/CountryCodeWidget', 23 | 'reuters/CalendarWidget', 24 | 'widgets/jquery/PagerWidget' 25 | ], function () { 26 | $(function () { 27 | Manager = new AjaxSolr.Manager({ 28 | solrUrl: 'https://reuters-demo.tree.ewdev.ca:9090/reuters/' 29 | }); 30 | Manager.addWidget(new AjaxSolr.ResultWidget({ 31 | id: 'result', 32 | target: '#docs' 33 | })); 34 | Manager.addWidget(new AjaxSolr.PagerWidget({ 35 | id: 'pager', 36 | target: '#pager', 37 | prevLabel: '<', 38 | nextLabel: '>', 39 | innerWindow: 1, 40 | renderHeader: function (perPage, offset, total) { 41 | $('#pager-header').html($('').text('displaying ' + Math.min(total, offset + 1) + ' to ' + Math.min(total, offset + perPage) + ' of ' + total)); 42 | } 43 | })); 44 | var fields = [ 'topics', 'organisations', 'exchanges' ]; 45 | for (var i = 0, l = fields.length; i < l; i++) { 46 | Manager.addWidget(new AjaxSolr.TagcloudWidget({ 47 | id: fields[i], 48 | target: '#' + fields[i], 49 | field: fields[i] 50 | })); 51 | } 52 | Manager.addWidget(new AjaxSolr.CurrentSearchWidget({ 53 | id: 'currentsearch', 54 | target: '#selection' 55 | })); 56 | Manager.addWidget(new AjaxSolr.AutocompleteWidget({ 57 | id: 'text', 58 | target: '#search', 59 | fields: [ 'topics', 'organisations', 'exchanges' ] 60 | })); 61 | Manager.addWidget(new AjaxSolr.CountryCodeWidget({ 62 | id: 'countries', 63 | target: '#countries', 64 | field: 'countryCodes' 65 | })); 66 | Manager.addWidget(new AjaxSolr.CalendarWidget({ 67 | id: 'calendar', 68 | target: '#calendar', 69 | field: 'date' 70 | })); 71 | Manager.init(); 72 | Manager.store.addByValue('q', '*:*'); 73 | var params = { 74 | facet: true, 75 | 'facet.field': [ 'topics', 'organisations', 'exchanges', 'countryCodes' ], 76 | 'facet.limit': 20, 77 | 'facet.mincount': 1, 78 | 'f.topics.facet.limit': 50, 79 | 'f.countryCodes.facet.limit': -1, 80 | 'facet.date': 'date', 81 | 'facet.date.start': '1987-02-26T00:00:00.000Z/DAY', 82 | 'facet.date.end': '1987-10-20T00:00:00.000Z/DAY+1DAY', 83 | 'facet.date.gap': '+1DAY', 84 | 'json.nl': 'map' 85 | }; 86 | for (var name in params) { 87 | Manager.store.addByValue(name, params[name]); 88 | } 89 | Manager.doRequest(); 90 | }); 91 | 92 | $.fn.showIf = function (condition) { 93 | if (condition) { 94 | return this.show(); 95 | } 96 | else { 97 | return this.hide(); 98 | } 99 | } 100 | }); 101 | 102 | })(jQuery); 103 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/ResultWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.ResultWidget = AjaxSolr.AbstractWidget.extend({ 13 | start: 0, 14 | 15 | beforeRequest: function () { 16 | $(this.target).html($('').attr('src', 'images/ajax-loader.gif')); 17 | }, 18 | 19 | facetLinks: function (facet_field, facet_values) { 20 | var links = []; 21 | if (facet_values) { 22 | for (var i = 0, l = facet_values.length; i < l; i++) { 23 | if (facet_values[i] !== undefined) { 24 | links.push( 25 | $('') 26 | .text(facet_values[i]) 27 | .click(this.facetHandler(facet_field, facet_values[i])) 28 | ); 29 | } 30 | else { 31 | links.push('no items found in current selection'); 32 | } 33 | } 34 | } 35 | return links; 36 | }, 37 | 38 | facetHandler: function (facet_field, facet_value) { 39 | var self = this; 40 | return function () { 41 | self.manager.store.remove('fq'); 42 | self.manager.store.addByValue('fq', facet_field + ':' + AjaxSolr.Parameter.escapeValue(facet_value)); 43 | self.doRequest(0); 44 | return false; 45 | }; 46 | }, 47 | 48 | afterRequest: function () { 49 | $(this.target).empty(); 50 | for (var i = 0, l = this.manager.response.response.docs.length; i < l; i++) { 51 | var doc = this.manager.response.response.docs[i]; 52 | $(this.target).append(this.template(doc)); 53 | 54 | var items = []; 55 | items = items.concat(this.facetLinks('topics', doc.topics)); 56 | items = items.concat(this.facetLinks('organisations', doc.organisations)); 57 | items = items.concat(this.facetLinks('exchanges', doc.exchanges)); 58 | 59 | var $links = $('#links_' + doc.id); 60 | $links.empty(); 61 | for (var j = 0, m = items.length; j < m; j++) { 62 | $links.append($('
                                      • ').append(items[j])); 63 | } 64 | } 65 | }, 66 | 67 | template: function (doc) { 68 | var snippet = ''; 69 | if (doc.text.length > 300) { 70 | snippet += doc.dateline + ' ' + doc.text.substring(0, 300); 71 | snippet += '' + doc.text.substring(300); 72 | snippet += ' more'; 73 | } 74 | else { 75 | snippet += doc.dateline + ' ' + doc.text; 76 | } 77 | 78 | var output = '

                                        ' + doc.title + '

                                        '; 79 | output += ''; 80 | output += '

                                        ' + snippet + '

                                        '; 81 | return output; 82 | }, 83 | 84 | init: function () { 85 | $(document).on('click', 'a.more', function () { 86 | var $this = $(this), 87 | span = $this.parent().find('span'); 88 | 89 | if (span.is(':visible')) { 90 | span.hide(); 91 | $this.text('more'); 92 | } 93 | else { 94 | span.show(); 95 | $this.text('less'); 96 | } 97 | 98 | return false; 99 | }); 100 | } 101 | }); 102 | 103 | })(jQuery); 104 | 105 | })); 106 | -------------------------------------------------------------------------------- /examples/reuters-requirejs/widgets/ResultWidget.f.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | AjaxSolr.ResultWidget = AjaxSolr.AbstractWidget.extend({ 13 | start: 0, 14 | 15 | beforeRequest: function () { 16 | $(this.target).html($('').attr('src', 'images/ajax-loader.gif')); 17 | }, 18 | 19 | facetLinks: function (facet_field, facet_values) { 20 | var links = []; 21 | if (facet_values) { 22 | for (var i = 0, l = facet_values.length; i < l; i++) { 23 | if (facet_values[i] !== undefined) { 24 | links = links.concat([ 25 | facet_field + ':', 26 | $('') 27 | .text(facet_values[i]) 28 | .click(this.facetHandler(facet_field, facet_values[i])) 29 | ]); 30 | } 31 | else { 32 | links.push('no items found in current selection'); 33 | } 34 | } 35 | } 36 | return links; 37 | }, 38 | 39 | facetHandler: function (facet_field, facet_value) { 40 | var self = this; 41 | return function () { 42 | self.manager.store.remove('fq'); 43 | self.manager.store.addByValue('fq', facet_field + ':' + AjaxSolr.Parameter.escapeValue(facet_value)); 44 | self.doRequest(0); 45 | return false; 46 | }; 47 | }, 48 | 49 | afterRequest: function () { 50 | $(this.target).empty(); 51 | for (var i = 0, l = this.manager.response.response.docs.length; i < l; i++) { 52 | var doc = this.manager.response.response.docs[i]; 53 | $(this.target).append(this.template(doc)); 54 | 55 | var items = []; 56 | items = items.concat(this.facetLinks('topics', doc.topics)); 57 | items = items.concat(this.facetLinks('organisations', doc.organisations)); 58 | items = items.concat(this.facetLinks('exchanges', doc.exchanges)); 59 | 60 | var $links = $('#links_' + doc.id); 61 | $links.empty(); 62 | for (var j = 0, m = items.length; j < m; j++) { 63 | $links.append($('
                                      • ').append(items[j])); 64 | } 65 | } 66 | }, 67 | 68 | template: function (doc) { 69 | var snippet = ''; 70 | if (doc.text.length > 300) { 71 | snippet += doc.dateline + ' ' + doc.text.substring(0, 300); 72 | snippet += '' + doc.text.substring(300); 73 | snippet += ' more'; 74 | } 75 | else { 76 | snippet += doc.dateline + ' ' + doc.text; 77 | } 78 | 79 | var output = '

                                        ' + doc.title + '

                                        '; 80 | output += ''; 81 | output += '

                                        ' + snippet + '

                                        '; 82 | return output; 83 | }, 84 | 85 | init: function () { 86 | $(document).on('click', 'a.more', function () { 87 | var $this = $(this), 88 | span = $this.parent().find('span'); 89 | 90 | if (span.is(':visible')) { 91 | span.hide(); 92 | $this.text('more'); 93 | } 94 | else { 95 | span.show(); 96 | $this.text('less'); 97 | } 98 | 99 | return false; 100 | }); 101 | } 102 | }); 103 | 104 | })(jQuery); 105 | 106 | })); 107 | -------------------------------------------------------------------------------- /examples/solr-home/schema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | id 51 | 52 | text 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /core/ParameterHashStore.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/ParameterStore'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * A parameter store that stores the values of exposed parameters in the URL 12 | * hash to maintain the application's state. 13 | * 14 | *

                                        The ParameterHashStore observes the hash for changes and loads Solr 15 | * parameters from the hash if it observes a change or if the hash is empty. 16 | * The onhashchange event is used if the browser supports it.

                                        17 | * 18 | *

                                        Configure the manager with:

                                        19 | * 20 | * @class ParameterHashStore 21 | * @augments AjaxSolr.ParameterStore 22 | * @see https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange 23 | */ 24 | AjaxSolr.ParameterHashStore = AjaxSolr.ParameterStore.extend( 25 | /** @lends AjaxSolr.ParameterHashStore.prototype */ 26 | { 27 | /** 28 | * @param {Object} [attributes] 29 | * @param {Number} [attributes.interval] The interval in milliseconds to use 30 | * in setInterval(). Do not set the interval too low as you may set 31 | * up a race condition. Defaults to 250. 32 | */ 33 | constructor: function (attributes) { 34 | AjaxSolr.ParameterHashStore.__super__.constructor.apply(this, arguments); 35 | AjaxSolr.extend(this, { 36 | interval: 250, 37 | // Reference to the setInterval() function. 38 | intervalId: null, 39 | // A local copy of the URL hash, so we can detect changes to it. 40 | hash: '' 41 | }, attributes); 42 | }, 43 | 44 | /** 45 | * If loading and saving the hash take longer than interval, we'll 46 | * hit a race condition. However, this should never happen. 47 | */ 48 | init: function () { 49 | if (this.exposed.length) { 50 | // Check if the browser supports the onhashchange event. IE 8 and 9 in compatibility mode 51 | // incorrectly report support for onhashchange. 52 | if ('onhashchange' in window && (!document.documentMode || document.documentMode > 7)) { 53 | if (window.addEventListener) { 54 | window.addEventListener('hashchange', this.intervalFunction(this), false); 55 | } 56 | else if (window.attachEvent) { 57 | window.attachEvent('onhashchange', this.intervalFunction(this)); 58 | } 59 | else { 60 | window.onhashchange = this.intervalFunction(this); 61 | } 62 | } 63 | else { 64 | this.intervalId = window.setInterval(this.intervalFunction(this), this.interval); 65 | } 66 | } 67 | }, 68 | 69 | /** 70 | * Stores the values of the exposed parameters in both the local hash and the 71 | * URL hash. No other code should be made to change these two values. 72 | */ 73 | save: function () { 74 | this.hash = this.exposedString(); 75 | if (this.storedString()) { 76 | // Make a new history entry. 77 | window.location.hash = this.hash; 78 | } 79 | else { 80 | // Replace the old history entry. 81 | window.location.replace(window.location.href.replace('#', '') + '#' + this.hash); 82 | } 83 | }, 84 | 85 | /** 86 | * @see ParameterStore#storedString() 87 | */ 88 | storedString: function () { 89 | // Some browsers automatically unescape characters in the hash, others 90 | // don't. Fortunately, all leave window.location.href alone. So, use that. 91 | var index = window.location.href.indexOf('#'); 92 | if (index == -1) { 93 | return ''; 94 | } 95 | else { 96 | return window.location.href.substr(index + 1); 97 | } 98 | }, 99 | 100 | /** 101 | * Checks the hash for changes, and loads Solr parameters from the hash and 102 | * sends a request to Solr if it observes a change or if the hash is empty 103 | */ 104 | intervalFunction: function (self) { 105 | return function () { 106 | // Support the back/forward buttons. If the hash changes, do a request. 107 | var hash = self.storedString(); 108 | if (self.hash != hash && decodeURIComponent(self.hash) != decodeURIComponent(hash)) { 109 | self.load(); 110 | self.manager.doRequest(); 111 | } 112 | } 113 | } 114 | }); 115 | 116 | })); 117 | -------------------------------------------------------------------------------- /core/Core.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @namespace A unique namespace for the AJAX Solr library. 3 | */ 4 | AjaxSolr = function () {}; 5 | 6 | /** 7 | * @namespace Baseclass for all classes 8 | * @see https://github.com/documentcloud/backbone/blob/51eed189bf4d25877be4acdf51e0a4c6039583c5/backbone.js#L243 9 | */ 10 | AjaxSolr.Class = function(attributes) { 11 | AjaxSolr.extend(this, attributes); 12 | }; 13 | 14 | /** 15 | * A class 'extends' itself into a subclass. 16 | * 17 | * @static 18 | * @param protoProps The properties of the subclass. 19 | * @returns A function that represents the subclass. 20 | * @see https://github.com/documentcloud/backbone/blob/51eed189bf4d25877be4acdf51e0a4c6039583c5/backbone.js#L1516 21 | */ 22 | AjaxSolr.Class.extend = function (protoProps, staticProps) { 23 | var parent = this; 24 | var child; 25 | 26 | // The constructor function for the new subclass is either defined by you 27 | // (the "constructor" property in your `extend` definition), or defaulted 28 | // by us to simply call the parent's constructor. 29 | if (protoProps && Object.prototype.hasOwnProperty.call(protoProps, 'constructor')) { 30 | child = protoProps.constructor; 31 | } else { 32 | child = function(){ return parent.apply(this, arguments); }; 33 | } 34 | 35 | // Add static properties to the constructor function, if supplied. 36 | AjaxSolr.extend(child, parent, staticProps); 37 | 38 | // Set the prototype chain to inherit from `parent`, without calling 39 | // `parent`'s constructor function. 40 | var Surrogate = function(){ this.constructor = child; }; 41 | Surrogate.prototype = parent.prototype; 42 | child.prototype = new Surrogate; 43 | 44 | // Add prototype properties (instance properties) to the subclass, 45 | // if supplied. 46 | if (protoProps) AjaxSolr.extend(child.prototype, protoProps); 47 | 48 | // Set a convenience property in case the parent's prototype is needed 49 | // later. 50 | child.__super__ = parent.prototype; 51 | 52 | return child; 53 | }; 54 | 55 | /** 56 | * @static 57 | * @see https://github.com/documentcloud/underscore/blob/7342e289aa9d91c5aacfb3662ea56e7a6d081200/underscore.js#L789 58 | */ 59 | AjaxSolr.extend = function (child) { 60 | // From _.extend 61 | var obj = Array.prototype.slice.call(arguments, 1); 62 | 63 | // From _.extend 64 | var iterator = function(source) { 65 | if (source) { 66 | for (var prop in source) { 67 | child[prop] = source[prop]; 68 | } 69 | } 70 | }; 71 | 72 | // From _.each 73 | if (obj == null) return; 74 | if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) { 75 | obj.forEach(iterator); 76 | } else if (obj.length === +obj.length) { 77 | for (var i = 0, l = obj.length; i < l; i++) { 78 | iterator.call(undefined, obj[i], i, obj); 79 | } 80 | } else { 81 | for (var key in obj) { 82 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 83 | iterator.call(undefined, obj[key], key, obj); 84 | } 85 | } 86 | } 87 | 88 | return child; 89 | }; 90 | 91 | /** 92 | * @static 93 | * @param value A value. 94 | * @param array An array. 95 | * @returns {Boolean} Whether value exists in the array. 96 | */ 97 | AjaxSolr.inArray = function (value, array) { 98 | if (array) { 99 | for (var i = 0, l = array.length; i < l; i++) { 100 | if (AjaxSolr.equals(array[i], value)) { 101 | return i; 102 | } 103 | } 104 | } 105 | return -1; 106 | }; 107 | 108 | /** 109 | * @static 110 | * @param foo A value. 111 | * @param bar A value. 112 | * @returns {Boolean} Whether the two given values are equal. 113 | */ 114 | AjaxSolr.equals = function (foo, bar) { 115 | if (AjaxSolr.isArray(foo) && AjaxSolr.isArray(bar)) { 116 | if (foo.length !== bar.length) { 117 | return false; 118 | } 119 | for (var i = 0, l = foo.length; i < l; i++) { 120 | if (foo[i] !== bar[i]) { 121 | return false; 122 | } 123 | } 124 | return true; 125 | } 126 | else if (AjaxSolr.isRegExp(foo) && AjaxSolr.isString(bar)) { 127 | return bar.match(foo); 128 | } 129 | else if (AjaxSolr.isRegExp(bar) && AjaxSolr.isString(foo)) { 130 | return foo.match(bar); 131 | } 132 | else { 133 | return foo === bar; 134 | } 135 | }; 136 | 137 | /** 138 | * Can't use toString.call(obj) === "[object Array]", as it may return 139 | * "[xpconnect wrapped native prototype]", which is undesirable. 140 | * 141 | * @static 142 | * @see http://thinkweb2.com/projects/prototype/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/ 143 | * @see http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js 144 | */ 145 | AjaxSolr.isArray = function (obj) { 146 | return obj != null && typeof obj == 'object' && 'splice' in obj && 'join' in obj; 147 | }; 148 | 149 | /** 150 | * @param obj Any object. 151 | * @returns {Boolean} Whether the object is a RegExp object. 152 | */ 153 | AjaxSolr.isRegExp = function (obj) { 154 | return obj != null && (typeof obj == 'object' || typeof obj == 'function') && 'ignoreCase' in obj; 155 | }; 156 | 157 | /** 158 | * @param obj Any object. 159 | * @returns {Boolean} Whether the object is a String object. 160 | */ 161 | AjaxSolr.isString = function (obj) { 162 | return obj != null && typeof obj == 'string'; 163 | }; 164 | -------------------------------------------------------------------------------- /core/AbstractManager.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/Core'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * The Manager acts as the controller in a Model-View-Controller framework. All 12 | * public calls should be performed on the manager object. 13 | * 14 | * @param properties A map of fields to set. Refer to the list of public fields. 15 | * @class AbstractManager 16 | */ 17 | AjaxSolr.AbstractManager = AjaxSolr.Class.extend( 18 | /** @lends AjaxSolr.AbstractManager.prototype */ 19 | { 20 | /** 21 | * @param {Object} [attributes] 22 | * @param {String} [attributes.solrUrl] The fully-qualified URL of the Solr 23 | * application. You must include the trailing slash. Do not include the path 24 | * to any Solr servlet. Defaults to "http://localhost:8983/solr/" 25 | * @param {String} [attributes.proxyUrl] If we want to proxy queries through a 26 | * script, rather than send queries to Solr directly, set this field to the 27 | * fully-qualified URL of the script. 28 | * @param {String} [attributes.servlet] The default Solr servlet. You may 29 | * prepend the servlet with a core if using multiple cores. Defaults to 30 | * "servlet". 31 | */ 32 | constructor: function (attributes) { 33 | AjaxSolr.extend(this, { 34 | solrUrl: 'http://localhost:8983/solr/', 35 | proxyUrl: null, 36 | servlet: 'select', 37 | // The most recent response from Solr. 38 | response: {}, 39 | // A collection of all registered widgets. 40 | widgets: {}, 41 | // The parameter store for the manager and its widgets. 42 | store: null, 43 | // Whether init() has been called yet. 44 | initialized: false 45 | }, attributes); 46 | }, 47 | 48 | /** 49 | * An abstract hook for child implementations. 50 | * 51 | *

                                        This method should be called after the store and the widgets have been 52 | * added. It should initialize the widgets and the store, and do any other 53 | * one-time initializations, e.g., perform the first request to Solr.

                                        54 | * 55 | *

                                        If no store has been set, it sets the store to the basic 56 | * AjaxSolr.ParameterStore.

                                        57 | */ 58 | init: function () { 59 | this.initialized = true; 60 | if (this.store === null) { 61 | this.setStore(new AjaxSolr.ParameterStore()); 62 | } 63 | this.store.load(false); 64 | for (var widgetId in this.widgets) { 65 | this.widgets[widgetId].init(); 66 | } 67 | this.store.init(); 68 | }, 69 | 70 | /** 71 | * Set the manager's parameter store. 72 | * 73 | * @param {AjaxSolr.ParameterStore} store 74 | */ 75 | setStore: function (store) { 76 | store.manager = this; 77 | this.store = store; 78 | }, 79 | 80 | /** 81 | * Adds a widget to the manager. 82 | * 83 | * @param {AjaxSolr.AbstractWidget} widget 84 | */ 85 | addWidget: function (widget) { 86 | widget.manager = this; 87 | this.widgets[widget.id] = widget; 88 | }, 89 | 90 | /** 91 | * Stores the Solr parameters to be sent to Solr and sends a request to Solr. 92 | * 93 | * @param {Boolean} [start] The Solr start offset parameter. 94 | * @param {String} [servlet] The Solr servlet to send the request to. 95 | */ 96 | doRequest: function (start, servlet) { 97 | if (this.initialized === false) { 98 | this.init(); 99 | } 100 | // Allow non-pagination widgets to reset the offset parameter. 101 | if (start !== undefined) { 102 | this.store.get('start').val(start); 103 | } 104 | if (servlet === undefined) { 105 | servlet = this.servlet; 106 | } 107 | 108 | this.store.save(); 109 | 110 | for (var widgetId in this.widgets) { 111 | this.widgets[widgetId].beforeRequest(); 112 | } 113 | 114 | this.executeRequest(servlet); 115 | }, 116 | 117 | /** 118 | * An abstract hook for child implementations. 119 | * 120 | *

                                        Sends the request to Solr, i.e. to this.solrUrl or 121 | * this.proxyUrl, and receives Solr's response. It should pass Solr's 122 | * response to handleResponse() for handling.

                                        123 | * 124 | *

                                        See managers/Manager.jquery.js for a jQuery implementation.

                                        125 | * 126 | * @param {String} servlet The Solr servlet to send the request to. 127 | * @param {String} string The query string of the request. If not set, it 128 | * should default to this.store.string() 129 | * @throws If not defined in child implementation. 130 | */ 131 | executeRequest: function (servlet, string) { 132 | throw 'Abstract method executeRequest must be overridden in a subclass.'; 133 | }, 134 | 135 | /** 136 | * This method is executed after the Solr response data arrives. Allows each 137 | * widget to handle Solr's response separately. 138 | * 139 | * @param {Object} data The Solr response. 140 | */ 141 | handleResponse: function (data) { 142 | this.response = data; 143 | 144 | for (var widgetId in this.widgets) { 145 | this.widgets[widgetId].afterRequest(); 146 | } 147 | }, 148 | 149 | /** 150 | * This method is executed if Solr encounters an error. 151 | * 152 | * @param {String} message An error message. 153 | */ 154 | handleError: function (message) { 155 | window.console && console.log && console.log(message); 156 | } 157 | }); 158 | 159 | })); 160 | -------------------------------------------------------------------------------- /core/Parameter.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/Core'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * Represents a Solr parameter. 12 | * 13 | * @param properties A map of fields to set. Refer to the list of public fields. 14 | * @class Parameter 15 | */ 16 | AjaxSolr.Parameter = AjaxSolr.Class.extend( 17 | /** @lends AjaxSolr.Parameter.prototype */ 18 | { 19 | /** 20 | * @param {Object} attributes 21 | * @param {String} attributes.name The parameter's name. 22 | * @param {String} [attributes.value] The parameter's value. 23 | * @param {Object} [attributes.local] The parameter's local parameters. 24 | */ 25 | constructor: function (attributes) { 26 | AjaxSolr.extend(this, { 27 | name: null, 28 | value: null, 29 | locals: {} 30 | }, attributes); 31 | }, 32 | 33 | /** 34 | * Returns the value. If called with an argument, sets the value. 35 | * 36 | * @param {String|Number|String[]|Number[]} [value] The value to set. 37 | * @returns The value. 38 | */ 39 | val: function (value) { 40 | if (value === undefined) { 41 | return this.value; 42 | } 43 | else { 44 | this.value = value; 45 | } 46 | }, 47 | 48 | /** 49 | * Returns the value of a local parameter. If called with a second argument, 50 | * sets the value of a local parameter. 51 | * 52 | * @param {String} name The name of the local parameter. 53 | * @param {String|Number|String[]|Number[]} [value] The value to set. 54 | * @returns The value. 55 | */ 56 | local: function (name, value) { 57 | if (value === undefined) { 58 | return this.locals[name]; 59 | } 60 | else { 61 | this.locals[name] = value; 62 | } 63 | }, 64 | 65 | /** 66 | * Deletes a local parameter. 67 | * 68 | * @param {String} name The name of the local parameter. 69 | */ 70 | remove: function (name) { 71 | delete this.locals[name]; 72 | }, 73 | 74 | /** 75 | * Returns the Solr parameter as a query string key-value pair. 76 | * 77 | *

                                        IE6 calls the default toString() if you write store.toString() 78 | * . So, we need to choose another name for toString().

                                        79 | */ 80 | string: function () { 81 | var pairs = []; 82 | 83 | for (var name in this.locals) { 84 | if (this.locals[name]) { 85 | pairs.push(name + '=' + encodeURIComponent(this.locals[name])); 86 | } 87 | } 88 | 89 | var prefix = pairs.length ? '{!' + pairs.join('%20') + '}' : ''; 90 | 91 | if (this.value) { 92 | return this.name + '=' + prefix + this.valueString(this.value); 93 | } 94 | // For dismax request handlers, if the q parameter has local params, the 95 | // q parameter must be set to a non-empty value. In case the q parameter 96 | // has local params but is empty, use the q.alt parameter, which accepts 97 | // wildcards. 98 | else if (this.name == 'q' && prefix) { 99 | return 'q.alt=' + prefix + encodeURIComponent('*:*'); 100 | } 101 | else { 102 | return ''; 103 | } 104 | }, 105 | 106 | /** 107 | * Parses a string formed by calling string(). 108 | * 109 | * @param {String} str The string to parse. 110 | */ 111 | parseString: function (str) { 112 | var param = str.match(/^([^=]+)=(?:\{!([^\}]*)\})?(.*)$/); 113 | if (param) { 114 | var matches; 115 | 116 | while (matches = /([^\s=]+)=(\S*)/g.exec(decodeURIComponent(param[2]))) { 117 | this.locals[matches[1]] = decodeURIComponent(matches[2]); 118 | param[2] = param[2].replace(matches[0], ''); // Safari's exec seems not to do this on its own 119 | } 120 | 121 | if (param[1] == 'q.alt') { 122 | this.name = 'q'; 123 | // if q.alt is present, assume it is because q was empty, as above 124 | } 125 | else { 126 | this.name = param[1]; 127 | this.value = this.parseValueString(param[3]); 128 | } 129 | } 130 | }, 131 | 132 | /** 133 | * Returns the value as a URL-encoded string. 134 | * 135 | * @private 136 | * @param {String|Number|String[]|Number[]} value The value. 137 | * @returns {String} The URL-encoded string. 138 | */ 139 | valueString: function (value) { 140 | value = AjaxSolr.isArray(value) ? value.join(',') : value; 141 | return encodeURIComponent(value); 142 | }, 143 | 144 | /** 145 | * Parses a URL-encoded string to return the value. 146 | * 147 | * @private 148 | * @param {String} str The URL-encoded string. 149 | * @returns {Array} The value. 150 | */ 151 | parseValueString: function (str) { 152 | str = decodeURIComponent(str); 153 | return str.indexOf(',') == -1 ? str : str.split(','); 154 | } 155 | }); 156 | 157 | /** 158 | * Escapes a value, to be used in, for example, an fq parameter. Surrounds 159 | * strings containing spaces or colons in double quotes. 160 | * 161 | * @public 162 | * @param {String|Number} value The value. 163 | * @returns {String} The escaped value. 164 | */ 165 | AjaxSolr.Parameter.escapeValue = function (value) { 166 | // If the field value has a space, colon, quotation mark or forward slash 167 | // in it, wrap it in quotes, unless it is a range query or it is already 168 | // wrapped in quotes. 169 | if (value.match(/[ :\/"]/) && !value.match(/[\[\{]\S+ TO \S+[\]\}]/) && !value.match(/^["\(].*["\)]$/)) { 170 | return '"' + value.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; 171 | } 172 | return value; 173 | } 174 | 175 | })); 176 | -------------------------------------------------------------------------------- /widgets/jquery/AutocompleteTermWidget.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | /** 4 | * A term autocomplete search box, using jQueryUI.autocomplete. This 5 | * implementation uses Solr's facet.prefix technique. This technique benefits 6 | * from honoring the filter query state and by being able to put words prior to 7 | * the last one the user is typing into a filter query as well to get even more 8 | * relevant completion suggestions. 9 | * 10 | * Index instructions: 11 | * 1. Put a facet warming query into Solr's "firstSearcher" in solrconfig.xml, 12 | * for the target field. 13 | * 2. Use appropriate text analysis to include a tokenizer (not keyword) and do 14 | * not do stemming or else you will see stems suggested. A 'light' 15 | * stemmer may produce acceptable stems. 16 | * 3. If you are auto-completing in a search box that would normally be using 17 | * the dismax query parser AND your qf parameter references more than one field, 18 | * then you might want to use a catch-all search field to autocomplete on. 19 | * 20 | * For large indexes, another implementation approach like the Suggester feature 21 | * or TermsComponent might be better than a faceting approach. 22 | * 23 | * Other types of autocomplete (a.k.a. suggest) are "search-results", 24 | * "query-log", and "facet-value". This widget does term autocompletion. 25 | * 26 | * @author David Smiley 27 | */ 28 | AjaxSolr.AutocompleteTermWidget = AjaxSolr.AbstractTextWidget.extend( 29 | /** @lends AjaxSolr.AutocompleteTermWidget.prototype */ 30 | { 31 | /** 32 | * @param {Object} attributes 33 | * @param {String} attributes.field The Solr field to autocomplete indexed 34 | * terms from. 35 | * @param {Boolean} [attributes.tokenized] Whether the underlying field is 36 | * tokenized. This component will take words before the last word 37 | * (whitespace separated) and generate a filter query for those words, while 38 | * only the last word will be used for facet.prefix. For field-value 39 | * completion (on just one field) or query log completion, you would have a 40 | * non-tokenized field to complete against. Defaults to true. 41 | * @param {Boolean} [attributes.lowercase] Indicates whether to lowercase the 42 | * facet.prefix value. Defaults to true. 43 | * @param {Number} [attributes.limit] The maximum number of results to show. 44 | * Defaults to 10. 45 | * @param {Number} [attributes.minLength] The minimum number of characters 46 | * required to show suggestions. Defaults to 2. 47 | * @param {String} [attributes.servlet] The URL path that follows the solr 48 | * webapp, for use in auto-complete queries. If not specified, the manager's 49 | * servlet property will be used. You may prepend the servlet with a core if 50 | * using multiple cores. It is a good idea to use a non-default one to 51 | * differentiate these requests in server logs and Solr statistics. 52 | */ 53 | constructor: function (attributes) { 54 | AjaxSolr.AutocompleteTermWidget.__super__.constructor.apply(this, arguments); 55 | AjaxSolr.extend(this, { 56 | field: null, 57 | tokenized: true, 58 | lowercase: true, 59 | limit: 10, 60 | minLength: 2, 61 | servlet: null 62 | }, attributes); 63 | }, 64 | 65 | init: function () { 66 | var self = this; 67 | 68 | if (!this.field) { 69 | throw '"field" must be set on AutocompleteTermWidget.'; 70 | } 71 | this.servlet = this.servlet || this.manager.servlet; 72 | 73 | $(this.target).find('input').bind('keydown', function (e) { 74 | if (e.which == 13) { 75 | var q = $(this).val(); 76 | if (self.set(q)) { 77 | self.doRequest(); 78 | } 79 | } 80 | }); 81 | 82 | $(this.target).find('input').autocomplete({ 83 | source: function (request, response) { // note: must always call response() 84 | // If term ends with a space: 85 | if (request.term.charAt(request.term.length - 1).replace(/^ +/, '').replace(/ +$/, '') == '') { 86 | response(); 87 | return; 88 | } 89 | 90 | var term = request.term, 91 | facetPrefix = term, // before the last word (if we tokenize) 92 | fq = '', 93 | store = new AjaxSolr.ParameterStore(); 94 | 95 | store.addByValue('fq', self.manager.store.values('fq')); 96 | 97 | if (self.tokenized) { 98 | // Split out the last word of the term from the words before it. 99 | var lastSpace = term.lastIndexOf(' '); 100 | if (lastSpace > -1) { 101 | fq = term.substring(0, lastSpace); 102 | facetPrefix = term.substring(lastSpace + 1); 103 | store.addByValue('fq', '{!dismax qf=' + self.field + '}' + fq); 104 | } 105 | } 106 | if (self.lowercase) { 107 | facetPrefix = facetPrefix.toLowerCase(); 108 | } 109 | 110 | store.addByValue('facet.field', self.field); 111 | store.addByValue('facet.limit', self.limit); 112 | store.addByValue('facet.prefix', facetPrefix); 113 | 114 | self.manager.executeRequest(self.servlet, 'json.nl=arrarr&q=*:*&rows=0&facet=true&facet.mincount=1&' + store.string(), function (data) { 115 | response($.map(data.facet_counts.facet_fields[self.field], function (term) { 116 | var q = (fq + ' ' + term[0]).replace(/^ +/, '').replace(/ +$/, ''); 117 | return { 118 | label: q + ' (' + term[1] + ')', 119 | value: q 120 | } 121 | })); 122 | }); 123 | }, 124 | minLength: this.minLength, 125 | select: function(event, ui) { 126 | if (self.set(ui.item.value)) { 127 | self.doRequest(); 128 | } 129 | } 130 | }); 131 | } 132 | }); 133 | 134 | })(jQuery); 135 | -------------------------------------------------------------------------------- /widgets/jquery/PagerWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | (function ($) { 11 | 12 | /** 13 | * A pager widget for jQuery. 14 | * 15 | *

                                        Heavily inspired by the Ruby on Rails will_paginate gem.

                                        16 | * 17 | * @expects this.target to be a list. 18 | * @class PagerWidget 19 | * @augments AjaxSolr.AbstractWidget 20 | * @todo Don't use the manager to send the request. Request only the results, 21 | * not the facets. Update only itself and the results widget. 22 | */ 23 | AjaxSolr.PagerWidget = AjaxSolr.AbstractWidget.extend( 24 | /** @lends AjaxSolr.PagerWidget.prototype */ 25 | { 26 | /** 27 | * @param {Object} [attributes] 28 | * @param {Number} [attributes.innerWindow] How many links are shown around 29 | * the current page. Defaults to 4. 30 | * @param {Number} [attributes.outerWindow] How many links are around the 31 | * first and the last page. Defaults to 1. 32 | * @param {String} [attributes.prevLabel] The previous page link label. 33 | * Defaults to "« Previous". 34 | * @param {String} [attributes.nextLabel] The next page link label. Defaults 35 | * to "Next »". 36 | * @param {String} [attributes.separator] Separator between pagination links. 37 | * Defaults to " ". 38 | */ 39 | constructor: function (attributes) { 40 | AjaxSolr.PagerWidget.__super__.constructor.apply(this, arguments); 41 | AjaxSolr.extend(this, { 42 | innerWindow: 4, 43 | outerWindow: 1, 44 | prevLabel: '« Previous', 45 | nextLabel: 'Next »', 46 | separator: ' ', 47 | // The current page number. 48 | currentPage: null, 49 | // The total number of pages. 50 | totalPages: null 51 | }, attributes); 52 | }, 53 | 54 | /** 55 | * @returns {String} The gap in page links, which is represented by: 56 | * 57 | */ 58 | gapMarker: function () { 59 | return ''; 60 | }, 61 | 62 | /** 63 | * @returns {Array} The links for the visible page numbers. 64 | */ 65 | windowedLinks: function () { 66 | var links = []; 67 | 68 | var prev = null; 69 | 70 | visible = this.visiblePageNumbers(); 71 | for (var i = 0, l = visible.length; i < l; i++) { 72 | if (prev && visible[i] > prev + 1) links.push(this.gapMarker()); 73 | links.push(this.pageLinkOrSpan(visible[i], [ 'pager-current' ])); 74 | prev = visible[i]; 75 | } 76 | 77 | return links; 78 | }, 79 | 80 | /** 81 | * @returns {Array} The visible page numbers according to the window options. 82 | */ 83 | visiblePageNumbers: function () { 84 | var windowFrom = this.currentPage - this.innerWindow; 85 | var windowTo = this.currentPage + this.innerWindow; 86 | 87 | // If the window is truncated on one side, make the other side longer 88 | if (windowTo > this.totalPages) { 89 | windowFrom = Math.max(0, windowFrom - (windowTo - this.totalPages)); 90 | windowTo = this.totalPages; 91 | } 92 | if (windowFrom < 1) { 93 | windowTo = Math.min(this.totalPages, windowTo + (1 - windowFrom)); 94 | windowFrom = 1; 95 | } 96 | 97 | var visible = []; 98 | 99 | // Always show the first page 100 | visible.push(1); 101 | // Don't add inner window pages twice 102 | for (var i = 2; i <= Math.min(1 + this.outerWindow, windowFrom - 1); i++) { 103 | visible.push(i); 104 | } 105 | // If the gap is just one page, close the gap 106 | if (1 + this.outerWindow == windowFrom - 2) { 107 | visible.push(windowFrom - 1); 108 | } 109 | // Don't add the first or last page twice 110 | for (var i = Math.max(2, windowFrom); i <= Math.min(windowTo, this.totalPages - 1); i++) { 111 | visible.push(i); 112 | } 113 | // If the gap is just one page, close the gap 114 | if (this.totalPages - this.outerWindow == windowTo + 2) { 115 | visible.push(windowTo + 1); 116 | } 117 | // Don't add inner window pages twice 118 | for (var i = Math.max(this.totalPages - this.outerWindow, windowTo + 1); i < this.totalPages; i++) { 119 | visible.push(i); 120 | } 121 | // Always show the last page, unless it's the first page 122 | if (this.totalPages > 1) { 123 | visible.push(this.totalPages); 124 | } 125 | 126 | return visible; 127 | }, 128 | 129 | /** 130 | * @param {Number} page A page number. 131 | * @param {String} classnames CSS classes to add to the page link. 132 | * @param {String} text The inner HTML of the page link (optional). 133 | * @returns The link or span for the given page. 134 | */ 135 | pageLinkOrSpan: function (page, classnames, text) { 136 | text = text || page; 137 | 138 | if (page && page != this.currentPage) { 139 | return $('').html(text).attr('rel', this.relValue(page)).addClass(classnames[1]).click(this.clickHandler(page)); 140 | } 141 | else { 142 | return $('').html(text).addClass(classnames.join(' ')); 143 | } 144 | }, 145 | 146 | /** 147 | * @param {Number} page A page number. 148 | * @returns {Function} The click handler for the page link. 149 | */ 150 | clickHandler: function (page) { 151 | var self = this; 152 | return function () { 153 | self.manager.store.get('start').val((page - 1) * self.perPage()); 154 | self.doRequest(); 155 | return false; 156 | } 157 | }, 158 | 159 | /** 160 | * @param {Number} page A page number. 161 | * @returns {String} The rel attribute for the page link. 162 | */ 163 | relValue: function (page) { 164 | switch (page) { 165 | case this.previousPage(): 166 | return 'prev' + (page == 1 ? 'start' : ''); 167 | case this.nextPage(): 168 | return 'next'; 169 | case 1: 170 | return 'start'; 171 | default: 172 | return ''; 173 | } 174 | }, 175 | 176 | /** 177 | * @returns {Number} The page number of the previous page or null if no previous page. 178 | */ 179 | previousPage: function () { 180 | return this.currentPage > 1 ? (this.currentPage - 1) : null; 181 | }, 182 | 183 | /** 184 | * @returns {Number} The page number of the next page or null if no next page. 185 | */ 186 | nextPage: function () { 187 | return this.currentPage < this.totalPages ? (this.currentPage + 1) : null; 188 | }, 189 | 190 | /** 191 | * An abstract hook for child implementations. 192 | * 193 | * @param {Number} perPage The number of items shown per results page. 194 | * @param {Number} offset The index in the result set of the first document to render. 195 | * @param {Number} total The total number of documents in the result set. 196 | */ 197 | renderHeader: function (perPage, offset, total) {}, 198 | 199 | /** 200 | * Render the pagination links. 201 | * 202 | * @param {Array} links The links for the visible page numbers. 203 | */ 204 | renderLinks: function (links) { 205 | if (this.totalPages) { 206 | links.unshift(this.pageLinkOrSpan(this.previousPage(), [ 'pager-disabled', 'pager-prev' ], this.prevLabel)); 207 | links.push(this.pageLinkOrSpan(this.nextPage(), [ 'pager-disabled', 'pager-next' ], this.nextLabel)); 208 | 209 | var $target = $(this.target); 210 | $target.empty(); 211 | 212 | for (var i = 0, l = links.length; i < l; i++) { 213 | var $li = $('
                                      • '); 214 | if (this.separator && i > 0) { 215 | $li.append(this.separator); 216 | } 217 | $target.append($li.append(links[i])); 218 | } 219 | } 220 | }, 221 | 222 | /** 223 | * @returns {Number} The number of results to display per page. 224 | */ 225 | perPage: function () { 226 | return parseInt(this.manager.response.responseHeader && this.manager.response.responseHeader.params && this.manager.response.responseHeader.params.rows || this.manager.store.get('rows').val() || 10); 227 | }, 228 | 229 | /** 230 | * @returns {Number} The Solr offset parameter's value. 231 | */ 232 | getOffset: function () { 233 | return parseInt(this.manager.response.responseHeader && this.manager.response.responseHeader.params && this.manager.response.responseHeader.params.start || this.manager.store.get('start').val() || 0); 234 | }, 235 | 236 | afterRequest: function () { 237 | var perPage = this.perPage(); 238 | var offset = this.getOffset(); 239 | var total = parseInt(this.manager.response.response.numFound); 240 | 241 | // Normalize the offset to a multiple of perPage. 242 | offset = offset - offset % perPage; 243 | 244 | this.currentPage = Math.ceil((offset + 1) / perPage); 245 | this.totalPages = Math.ceil(total / perPage); 246 | 247 | $(this.target).empty(); 248 | 249 | this.renderLinks(this.windowedLinks()); 250 | this.renderHeader(perPage, offset, total); 251 | } 252 | }); 253 | 254 | })(jQuery); 255 | 256 | })); 257 | -------------------------------------------------------------------------------- /core/AbstractFacetWidget.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/AbstractWidget', 'core/Parameter'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * Baseclass for all facet widgets. 12 | * 13 | * @class AbstractFacetWidget 14 | * @augments AjaxSolr.AbstractWidget 15 | */ 16 | AjaxSolr.AbstractFacetWidget = AjaxSolr.AbstractWidget.extend( 17 | /** @lends AjaxSolr.AbstractFacetWidget.prototype */ 18 | { 19 | /** 20 | * @param {Object} attributes 21 | * @param {String} attributes.field The field to facet on. 22 | * @param {Number} [attributes.start] This widget will by default set the 23 | * offset parameter to 0 on each request. 24 | * @param {Boolean} [attributes.multivalue] Set to false to force a 25 | * single "fq" parameter for this widget. Defaults to true. 26 | */ 27 | constructor: function (attributes) { 28 | AjaxSolr.AbstractFacetWidget.__super__.constructor.apply(this, arguments); 29 | AjaxSolr.extend(this, { 30 | start: 0, 31 | field: null, 32 | multivalue: true 33 | }, attributes); 34 | }, 35 | 36 | init: function () { 37 | this.initStore(); 38 | }, 39 | 40 | /** 41 | * Add facet parameters to the parameter store. 42 | */ 43 | initStore: function () { 44 | /* http://wiki.apache.org/solr/SimpleFacetParameters */ 45 | var parameters = [ 46 | 'facet.prefix', 47 | 'facet.sort', 48 | 'facet.limit', 49 | 'facet.offset', 50 | 'facet.mincount', 51 | 'facet.missing', 52 | 'facet.method', 53 | 'facet.enum.cache.minDf' 54 | ]; 55 | 56 | this.manager.store.addByValue('facet', true); 57 | 58 | // Set facet.field, facet.date or facet.range to truthy values to add 59 | // related per-field parameters to the parameter store. 60 | if (this['facet.field'] !== undefined) { 61 | this.manager.store.addByValue('facet.field', this.field); 62 | } 63 | else if (this['facet.date'] !== undefined) { 64 | this.manager.store.addByValue('facet.date', this.field); 65 | parameters = parameters.concat([ 66 | 'facet.date.start', 67 | 'facet.date.end', 68 | 'facet.date.gap', 69 | 'facet.date.hardend', 70 | 'facet.date.other', 71 | 'facet.date.include' 72 | ]); 73 | } 74 | else if (this['facet.range'] !== undefined) { 75 | this.manager.store.addByValue('facet.range', this.field); 76 | parameters = parameters.concat([ 77 | 'facet.range.start', 78 | 'facet.range.end', 79 | 'facet.range.gap', 80 | 'facet.range.hardend', 81 | 'facet.range.other', 82 | 'facet.range.include' 83 | ]); 84 | } 85 | 86 | for (var i = 0, l = parameters.length; i < l; i++) { 87 | if (this[parameters[i]] !== undefined) { 88 | this.manager.store.addByValue('f.' + this.field + '.' + parameters[i], this[parameters[i]]); 89 | } 90 | } 91 | }, 92 | 93 | /** 94 | * @returns {Boolean} Whether any filter queries have been set using this 95 | * widget's facet field. 96 | */ 97 | isEmpty: function () { 98 | return !this.manager.store.find('fq', new RegExp('^-?' + this.field + ':')); 99 | }, 100 | 101 | /** 102 | * Sets the filter query. 103 | * 104 | * @returns {Boolean} Whether the selection changed. 105 | */ 106 | set: function (value) { 107 | return this.changeSelection(function () { 108 | var a = this.manager.store.removeByValue('fq', new RegExp('^-?' + this.field + ':')), 109 | b = this.manager.store.addByValue('fq', this.fq(value)); 110 | return a || b; 111 | }); 112 | }, 113 | 114 | /** 115 | * Adds a filter query. 116 | * 117 | * @returns {Boolean} Whether a filter query was added. 118 | */ 119 | add: function (value) { 120 | return this.changeSelection(function () { 121 | return this.manager.store.addByValue('fq', this.fq(value)); 122 | }); 123 | }, 124 | 125 | /** 126 | * Removes a filter query. 127 | * 128 | * @returns {Boolean} Whether a filter query was removed. 129 | */ 130 | remove: function (value) { 131 | return this.changeSelection(function () { 132 | return this.manager.store.removeByValue('fq', this.fq(value)); 133 | }); 134 | }, 135 | 136 | /** 137 | * Removes all filter queries using the widget's facet field. 138 | * 139 | * @returns {Boolean} Whether a filter query was removed. 140 | */ 141 | clear: function () { 142 | return this.changeSelection(function () { 143 | return this.manager.store.removeByValue('fq', new RegExp('^-?' + this.field + ':')); 144 | }); 145 | }, 146 | 147 | /** 148 | * Helper for selection functions. 149 | * 150 | * @param {Function} Selection function to call. 151 | * @returns {Boolean} Whether the selection changed. 152 | */ 153 | changeSelection: function (func) { 154 | changed = func.apply(this); 155 | if (changed) { 156 | this.afterChangeSelection(); 157 | } 158 | return changed; 159 | }, 160 | 161 | /** 162 | * An abstract hook for child implementations. 163 | * 164 | *

                                        This method is executed after the filter queries change.

                                        165 | */ 166 | afterChangeSelection: function () {}, 167 | 168 | /** 169 | * One of "facet.field", "facet.date" or "facet.range" must be set on the 170 | * widget in order to determine where the facet counts are stored. 171 | * 172 | * @returns {Array} An array of objects with the properties facet and 173 | * count, e.g { facet: 'facet', count: 1 }. 174 | */ 175 | getFacetCounts: function () { 176 | var property; 177 | if (this['facet.field'] !== undefined) { 178 | property = 'facet_fields'; 179 | } 180 | else if (this['facet.date'] !== undefined) { 181 | property = 'facet_dates'; 182 | } 183 | else if (this['facet.range'] !== undefined) { 184 | property = 'facet_ranges'; 185 | } 186 | if (property !== undefined) { 187 | switch (this.manager.store.get('json.nl').val()) { 188 | case 'map': 189 | return this.getFacetCountsMap(property); 190 | case 'arrarr': 191 | return this.getFacetCountsArrarr(property); 192 | default: 193 | return this.getFacetCountsFlat(property); 194 | } 195 | } 196 | throw 'Cannot get facet counts unless one of the following properties is set to "true" on widget "' + this.id + '": "facet.field", "facet.date", or "facet.range".'; 197 | }, 198 | 199 | /** 200 | * Used if the facet counts are represented as a JSON object. 201 | * 202 | * @param {String} property "facet_fields", "facet_dates", or "facet_ranges". 203 | * @returns {Array} An array of objects with the properties facet and 204 | * count, e.g { facet: 'facet', count: 1 }. 205 | */ 206 | getFacetCountsMap: function (property) { 207 | var counts = []; 208 | for (var facet in this.manager.response.facet_counts[property][this.field]) { 209 | counts.push({ 210 | facet: facet, 211 | count: parseInt(this.manager.response.facet_counts[property][this.field][facet]) 212 | }); 213 | } 214 | return counts; 215 | }, 216 | 217 | /** 218 | * Used if the facet counts are represented as an array of two-element arrays. 219 | * 220 | * @param {String} property "facet_fields", "facet_dates", or "facet_ranges". 221 | * @returns {Array} An array of objects with the properties facet and 222 | * count, e.g { facet: 'facet', count: 1 }. 223 | */ 224 | getFacetCountsArrarr: function (property) { 225 | var counts = []; 226 | for (var i = 0, l = this.manager.response.facet_counts[property][this.field].length; i < l; i++) { 227 | counts.push({ 228 | facet: this.manager.response.facet_counts[property][this.field][i][0], 229 | count: parseInt(this.manager.response.facet_counts[property][this.field][i][1]) 230 | }); 231 | } 232 | return counts; 233 | }, 234 | 235 | /** 236 | * Used if the facet counts are represented as a flat array. 237 | * 238 | * @param {String} property "facet_fields", "facet_dates", or "facet_ranges". 239 | * @returns {Array} An array of objects with the properties facet and 240 | * count, e.g { facet: 'facet', count: 1 }. 241 | */ 242 | getFacetCountsFlat: function (property) { 243 | var counts = []; 244 | for (var i = 0, l = this.manager.response.facet_counts[property][this.field].length; i < l; i += 2) { 245 | counts.push({ 246 | facet: this.manager.response.facet_counts[property][this.field][i], 247 | count: parseInt(this.manager.response.facet_counts[property][this.field][i+1]) 248 | }); 249 | } 250 | return counts; 251 | }, 252 | 253 | /** 254 | * @param {String} value The value. 255 | * @returns {Function} Sends a request to Solr if it successfully adds a 256 | * filter query with the given value. 257 | */ 258 | clickHandler: function (value) { 259 | var self = this, meth = this.multivalue ? 'add' : 'set'; 260 | return function () { 261 | if (self[meth].call(self, value)) { 262 | self.doRequest(); 263 | } 264 | return false; 265 | } 266 | }, 267 | 268 | /** 269 | * @param {String} value The value. 270 | * @returns {Function} Sends a request to Solr if it successfully removes a 271 | * filter query with the given value. 272 | */ 273 | unclickHandler: function (value) { 274 | var self = this; 275 | return function () { 276 | if (self.remove(value)) { 277 | self.doRequest(); 278 | } 279 | return false; 280 | } 281 | }, 282 | 283 | /** 284 | * @param {String} value The facet value. 285 | * @param {Boolean} exclude Whether to exclude this fq parameter value. 286 | * @returns {String} An fq parameter value. 287 | */ 288 | fq: function (value, exclude) { 289 | return (exclude ? '-' : '') + this.field + ':' + AjaxSolr.Parameter.escapeValue(value); 290 | } 291 | }); 292 | 293 | })); 294 | -------------------------------------------------------------------------------- /core/ParameterStore.js: -------------------------------------------------------------------------------- 1 | (function (callback) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['core/Core', 'core/Parameter'], callback); 4 | } 5 | else { 6 | callback(); 7 | } 8 | }(function () { 9 | 10 | /** 11 | * The ParameterStore, as its name suggests, stores Solr parameters. Widgets 12 | * expose some of these parameters to the user. Whenever the user changes the 13 | * values of these parameters, the state of the application changes. In order to 14 | * allow the user to move back and forth between these states with the browser's 15 | * Back and Forward buttons, and to bookmark these states, each state needs to 16 | * be stored. The easiest method is to store the exposed parameters in the URL 17 | * hash (see the ParameterHashStore class). However, you may implement 18 | * your own storage method by extending this class. 19 | * 20 | *

                                        For a list of possible parameters, please consult the links below.

                                        21 | * 22 | * @see http://wiki.apache.org/solr/CoreQueryParameters 23 | * @see http://wiki.apache.org/solr/CommonQueryParameters 24 | * @see http://wiki.apache.org/solr/SimpleFacetParameters 25 | * @see http://wiki.apache.org/solr/HighlightingParameters 26 | * @see http://wiki.apache.org/solr/MoreLikeThis 27 | * @see http://wiki.apache.org/solr/SpellCheckComponent 28 | * @see http://wiki.apache.org/solr/StatsComponent 29 | * @see http://wiki.apache.org/solr/TermsComponent 30 | * @see http://wiki.apache.org/solr/TermVectorComponent 31 | * @see http://wiki.apache.org/solr/LocalParams 32 | * 33 | * @param properties A map of fields to set. Refer to the list of public fields. 34 | * @class ParameterStore 35 | */ 36 | AjaxSolr.ParameterStore = AjaxSolr.Class.extend( 37 | /** @lends AjaxSolr.ParameterStore.prototype */ 38 | { 39 | constructor: function (attributes) { 40 | /** 41 | * @param {Object} [attributes] 42 | * @param {String[]} [attributes.exposed] The names of the exposed 43 | * parameters. Any parameters that your widgets expose to the user, 44 | * directly or indirectly, should be listed here. 45 | */ 46 | AjaxSolr.extend(this, { 47 | exposed: [], 48 | // The Solr parameters. 49 | params: {}, 50 | // A reference to the parameter store's manager. 51 | manager: null 52 | }, attributes); 53 | }, 54 | 55 | /** 56 | * An abstract hook for child implementations. 57 | * 58 | *

                                        This method should do any necessary one-time initializations.

                                        59 | */ 60 | init: function () {}, 61 | 62 | /** 63 | * Some Solr parameters may be specified multiple times. It is easiest to 64 | * hard-code a list of such parameters. You may change the list by passing 65 | * { multiple: /pattern/ } as an argument to the constructor of 66 | * this class or one of its children, e.g.: 67 | * 68 | *

                                        new ParameterStore({ multiple: /pattern/ }) 69 | * 70 | * @param {String} name The name of the parameter. 71 | * @returns {Boolean} Whether the parameter may be specified multiple times. 72 | * @see http://lucene.apache.org/solr/api/org/apache/solr/handler/DisMaxRequestHandler.html 73 | */ 74 | isMultiple: function (name) { 75 | return name.match(/^(?:bf|bq|facet\.date|facet\.date\.other|facet\.date\.include|facet\.field|facet\.pivot|facet\.range|facet\.range\.other|facet\.range\.include|facet\.query|fq|group\.field|group\.func|group\.query|pf|qf)$/); 76 | }, 77 | 78 | /** 79 | * Returns a parameter. If the parameter doesn't exist, creates it. 80 | * 81 | * @param {String} name The name of the parameter. 82 | * @returns {AjaxSolr.Parameter|AjaxSolr.Parameter[]} The parameter. 83 | */ 84 | get: function (name) { 85 | if (this.params[name] === undefined) { 86 | var param = new AjaxSolr.Parameter({ name: name }); 87 | if (this.isMultiple(name)) { 88 | this.params[name] = [ param ]; 89 | } 90 | else { 91 | this.params[name] = param; 92 | } 93 | } 94 | return this.params[name]; 95 | }, 96 | 97 | /** 98 | * If the parameter may be specified multiple times, returns the values of 99 | * all identically-named parameters. If the parameter may be specified only 100 | * once, returns the value of that parameter. 101 | * 102 | * @param {String} name The name of the parameter. 103 | * @returns {String[]|Number[]} The value(s) of the parameter. 104 | */ 105 | values: function (name) { 106 | if (this.params[name] !== undefined) { 107 | if (this.isMultiple(name)) { 108 | var values = []; 109 | for (var i = 0, l = this.params[name].length; i < l; i++) { 110 | values.push(this.params[name][i].val()); 111 | } 112 | return values; 113 | } 114 | else { 115 | return [ this.params[name].val() ]; 116 | } 117 | } 118 | return []; 119 | }, 120 | 121 | /** 122 | * If the parameter may be specified multiple times, adds the given parameter 123 | * to the list of identically-named parameters, unless one already exists with 124 | * the same value. If it may be specified only once, replaces the parameter. 125 | * 126 | * @param {String} name The name of the parameter. 127 | * @param {AjaxSolr.Parameter} [param] The parameter. 128 | * @returns {AjaxSolr.Parameter|Boolean} The parameter, or false. 129 | */ 130 | add: function (name, param) { 131 | if (param === undefined) { 132 | param = new AjaxSolr.Parameter({ name: name }); 133 | } 134 | if (this.isMultiple(name)) { 135 | if (this.params[name] === undefined) { 136 | this.params[name] = [ param ]; 137 | } 138 | else { 139 | if (AjaxSolr.inArray(param.val(), this.values(name)) == -1) { 140 | this.params[name].push(param); 141 | } 142 | else { 143 | return false; 144 | } 145 | } 146 | } 147 | else { 148 | this.params[name] = param; 149 | } 150 | return param; 151 | }, 152 | 153 | /** 154 | * Deletes a parameter. 155 | * 156 | * @param {String} name The name of the parameter. 157 | * @param {Number} [index] The index of the parameter. 158 | */ 159 | remove: function (name, index) { 160 | if (index === undefined) { 161 | delete this.params[name]; 162 | } 163 | else { 164 | this.params[name].splice(index, 1); 165 | if (this.params[name].length == 0) { 166 | delete this.params[name]; 167 | } 168 | } 169 | }, 170 | 171 | /** 172 | * Finds all parameters with matching values. 173 | * 174 | * @param {String} name The name of the parameter. 175 | * @param {String|Number|String[]|Number[]|RegExp} value The value. 176 | * @returns {String|Number[]} The indices of the parameters found. 177 | */ 178 | find: function (name, value) { 179 | if (this.params[name] !== undefined) { 180 | if (this.isMultiple(name)) { 181 | var indices = []; 182 | for (var i = 0, l = this.params[name].length; i < l; i++) { 183 | if (AjaxSolr.equals(this.params[name][i].val(), value)) { 184 | indices.push(i); 185 | } 186 | } 187 | return indices.length ? indices : false; 188 | } 189 | else { 190 | if (AjaxSolr.equals(this.params[name].val(), value)) { 191 | return name; 192 | } 193 | } 194 | } 195 | return false; 196 | }, 197 | 198 | /** 199 | * If the parameter may be specified multiple times, creates a parameter using 200 | * the given name and value, and adds it to the list of identically-named 201 | * parameters, unless one already exists with the same value. If it may be 202 | * specified only once, replaces the parameter. 203 | * 204 | * @param {String} name The name of the parameter. 205 | * @param {String|Number|String[]|Number[]} value The value. 206 | * @param {Object} [locals] The parameter's local parameters. 207 | * @returns {AjaxSolr.Parameter|Boolean} The parameter, or false. 208 | */ 209 | addByValue: function (name, value, locals) { 210 | if (locals === undefined) { 211 | locals = {}; 212 | } 213 | if (this.isMultiple(name) && AjaxSolr.isArray(value)) { 214 | var ret = []; 215 | for (var i = 0, l = value.length; i < l; i++) { 216 | ret.push(this.add(name, new AjaxSolr.Parameter({ name: name, value: value[i], locals: locals }))); 217 | } 218 | return ret; 219 | } 220 | else { 221 | return this.add(name, new AjaxSolr.Parameter({ name: name, value: value, locals: locals })); 222 | } 223 | }, 224 | 225 | /** 226 | * Deletes any parameter with a matching value. 227 | * 228 | * @param {String} name The name of the parameter. 229 | * @param {String|Number|String[]|Number[]|RegExp} value The value. 230 | * @returns {String|Number[]} The indices deleted. 231 | */ 232 | removeByValue: function (name, value) { 233 | var indices = this.find(name, value); 234 | if (indices) { 235 | if (AjaxSolr.isArray(indices)) { 236 | for (var i = indices.length - 1; i >= 0; i--) { 237 | this.remove(name, indices[i]); 238 | } 239 | } 240 | else { 241 | this.remove(indices); 242 | } 243 | } 244 | return indices; 245 | }, 246 | 247 | /** 248 | * Returns the Solr parameters as a query string. 249 | * 250 | *

                                        IE6 calls the default toString() if you write store.toString() 251 | * . So, we need to choose another name for toString().

                                        252 | */ 253 | string: function () { 254 | var params = [], string; 255 | for (var name in this.params) { 256 | if (this.isMultiple(name)) { 257 | for (var i = 0, l = this.params[name].length; i < l; i++) { 258 | string = this.params[name][i].string(); 259 | if (string) { 260 | params.push(string); 261 | } 262 | } 263 | } 264 | else { 265 | string = this.params[name].string(); 266 | if (string) { 267 | params.push(string); 268 | } 269 | } 270 | } 271 | return params.join('&'); 272 | }, 273 | 274 | /** 275 | * Parses a query string into Solr parameters. 276 | * 277 | * @param {String} str The string to parse. 278 | */ 279 | parseString: function (str) { 280 | var pairs = str.split('&'); 281 | for (var i = 0, l = pairs.length; i < l; i++) { 282 | if (pairs[i]) { // ignore leading, trailing, and consecutive &'s 283 | var param = new AjaxSolr.Parameter(); 284 | param.parseString(pairs[i]); 285 | this.add(param.name, param); 286 | } 287 | } 288 | }, 289 | 290 | /** 291 | * Returns the exposed parameters as a query string. 292 | * 293 | * @returns {String} A string representation of the exposed parameters. 294 | */ 295 | exposedString: function () { 296 | var params = [], string; 297 | for (var i = 0, l = this.exposed.length; i < l; i++) { 298 | if (this.params[this.exposed[i]] !== undefined) { 299 | if (this.isMultiple(this.exposed[i])) { 300 | for (var j = 0, m = this.params[this.exposed[i]].length; j < m; j++) { 301 | string = this.params[this.exposed[i]][j].string(); 302 | if (string) { 303 | params.push(string); 304 | } 305 | } 306 | } 307 | else { 308 | string = this.params[this.exposed[i]].string(); 309 | if (string) { 310 | params.push(string); 311 | } 312 | } 313 | } 314 | } 315 | return params.join('&'); 316 | }, 317 | 318 | /** 319 | * Resets the values of the exposed parameters. 320 | */ 321 | exposedReset: function () { 322 | for (var i = 0, l = this.exposed.length; i < l; i++) { 323 | this.remove(this.exposed[i]); 324 | } 325 | }, 326 | 327 | /** 328 | * Loads the values of exposed parameters from persistent storage. It is 329 | * necessary, in most cases, to reset the values of exposed parameters before 330 | * setting the parameters to the values in storage. This is to ensure that a 331 | * parameter whose name is not present in storage is properly reset. 332 | * 333 | * @param {Boolean} [reset=true] Whether to reset the exposed parameters. 334 | * before loading new values from persistent storage. Default: true. 335 | */ 336 | load: function (reset) { 337 | if (reset === undefined) { 338 | reset = true; 339 | } 340 | if (reset) { 341 | this.exposedReset(); 342 | } 343 | this.parseString(this.storedString()); 344 | }, 345 | 346 | /** 347 | * An abstract hook for child implementations. 348 | * 349 | *

                                        Stores the values of the exposed parameters in persistent storage. This 350 | * method should usually be called before each Solr request.

                                        351 | */ 352 | save: function () {}, 353 | 354 | /** 355 | * An abstract hook for child implementations. 356 | * 357 | *

                                        Returns the string to parse from persistent storage.

                                        358 | * 359 | * @returns {String} The string from persistent storage. 360 | */ 361 | storedString: function () { 362 | return ''; 363 | } 364 | }); 365 | 366 | })); 367 | --------------------------------------------------------------------------------