UNPKG

eea-searchserver

Version:

EEA Node.js Search Server module

1,253 lines (1,127 loc) 150 kB
/* * jquery.facetview.js * * displays faceted browse results by querying a specified elasticsearch index * can read config locally or can be passed in as variable when executed * or a config variable can point to a remote config * * created by Mark MacGillivray - mark@cottagelabs.com * * http://cottagelabs.com * * There is an explanation of the options below. * */ // first define the bind with delay function from (saves loading it separately) // https://github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js function DoubleScroll(element) { var scrollbar= document.createElement('div'); scrollbar.className = "abovescrollbar"; scrollbar.appendChild(document.createElement('div')); scrollbar.style.overflow= 'auto'; scrollbar.style.overflowY= 'hidden'; scrollbar.firstChild.style.width= element.scrollWidth+'px'; scrollbar.firstChild.style.paddingTop= '1px'; scrollbar.firstChild.appendChild(document.createTextNode('\xA0')); scrollbar.onscroll= function() { element.scrollLeft= scrollbar.scrollLeft; }; element.onscroll= function() { scrollbar.scrollLeft= element.scrollLeft; }; element.parentNode.insertBefore(scrollbar, element); } (function($) { $.fn.bindWithDelay = function(type, data, fn, timeout, throttle) { var wait = null; var that = this; if ($.isFunction(data)) { throttle = timeout; timeout = fn; fn = data; data = undefined; } function cb() { var e = $.extend(true, { }, arguments[0]); var throttler = function() { wait = null; fn.apply(that, [e]); }; if (!throttle) { clearTimeout(wait); } if (!throttle || !wait) { wait = setTimeout(throttler, timeout); } } return this.bind(type, data, cb); }; })(jQuery); // add extension to jQuery with a function to get URL parameters jQuery.extend({ getUrlVars: function() { var newval; var params = {}; var hashes = window.location.href.slice( window.location.href.indexOf('?') + 1).split('&'); for (var i = 0; i < hashes.length; i++) { hash = hashes[i].split('='); if (hash.length > 1) { if (hash[1].replace(/%22/gi, '')[0] == '[' || hash[1].replace(/%22/gi, '')[0] == '{') { hash[1] = hash[1].replace(/^%22/, '').replace(/%22$/, ''); newval = JSON.parse( unescape(hash[1].replace(/%22/gi, '"'))); } else { newval = unescape(hash[1].replace(/%22/gi, '"')); } params[hash[0]] = newval; } } return params; }, getUrlVar: function(name) { return jQuery.getUrlVars()[name]; } }); // Deal with indexOf issue in <IE9 // provided by commentary in repo issue - //https://github.com/okfn/facetview/issues/18 if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(searchElement /*, fromIndex */) { 'use strict'; if (this === null) { throw new TypeError(); } var t = Object(this); var len = t.length >>> 0; if (len === 0) { return -1; } var n = 0; if (arguments.length > 1) { n = Number(arguments[1]); if (n != n) { // shortcut for verifying if it's NaN n = 0; } else if (n !== 0 && n !== Infinity && n !== -Infinity) { n = (n > 0 || -1) * Math.floor(Math.abs(n)); } } if (n >= len) { return -1; } var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); for (; k < len; k++) { if (k in t && t[k] === searchElement) { return k; } } return -1; }; } /* EXPLAINING THE FACETVIEW OPTIONS Facetview options can be set on instantiation. The list below details which options are available. Options can also be set and retrieved externally via $.fn.facetview.options. Query values can also be read from the query parameters of the current page, or provided in the "source" option for initial search. Also, whilst facetview is executing a query, it will "show" any element with the "notify-loading" class. So that class can be applied to any element on a page that can be used to signify loading is taking place. Once facetview has executed a query, the querystring used is available under "options.querystring". And the result object as retrieved directly from the index is available under "options.rawdata". searchbox_class --------------- This should only be set if embedded_search is set to false, and if an alternative search box on the page should be used as the source of search terms. If so, this should be set to the class name (including preceding .) of the text input that should be used as the source of the search terms. It is only a class instead of an ID so that it can be applied to fields that may already have an ID - it should really identify a unique box on the page for entering search terms for this instance of facetview. So an ID could actually also be used - just precede with # instead of . This makes it possible to embed a search box anywhere on a page and have it be used as the source of simple search parameters for the facetview. Only the last text box with this clas will be used. embedded_search --------------- Default to true, in which case full search term functionality is created and displayed on the page. If this is false, the search term text box and options will be hidden, so that new search terms cannot be provided by the user. It is possible to set an alternative search term input box on the page instead, by setting this to false and also setting a searchbox_class value to identify the basic source of search terms, in which case such a box must be manually created elsewhere on the page. searchbox_shade --------------- The background colour to apply to the search box sharesave_link -------------- Default to true, in which case the searchbox - if drawn by facetview - will be appended with a button that shows the full current search parameters as a URL. config_file ----------- Specify as a URL from which to pull a JSON config file specifying these options. facets ------ A list of facet objects which should be created as filter options on the page. As per elasticsearch facets settings, plus "display" as a display name for the facet, instead of field name. If these should be nested, define them with full scope e.g. nestedobj.nestedfield. extra_facets ------------ An object of named extra facet objects that should be submitted and executed on each query. These will NOT be used to generate filters on the page, but the result object can be queried for their content for other purposes. searchbox_fieldselect --------------------- A list of objects specifying fields to which search terms should be restricted. Each object should have a "display" value for displaying as the name of the option, and a "field" option specifying the field to restrict the search to. search_sortby ---------------- A list of objects describing sort option dropdowns. Each object requires a "display" value, and "field" value upon which to sort results. NOTE sort fields must be unique on the ES index, NOT lists. Otherwise it will fail silently. Choose wisely. enable_rangeselect ------------------ RANGES NEED SOME WORK AFTER RECENT UPDATE, KEEP DISABLED FOR NOW Enable or disable the ability to select a range of filter values include_facets_in_querystring ----------------------------- Default to false. Whether or not to include full facet settings in the querystring when it is requested for display. This makes it easier to get the querystring for other purposes, but does not change the query that is sent to the index. result_display -------------- A display template for search results. It is a list of lists. Each list specifies a line. Within each list, specify the contents of the line using objects to describe them. Each content piece should pertain to a particular "field" of the result set, and should specify what to show "pre" and "post" the given field display_images -------------- Default to true, in which case any image found in a given result object will be displayed to the left in the result object output. description ----------- Just an option to provide a human-friendly description of the functionality of the instantiated facetview. Like "search my shop". Will be displayed on the page. search_url ---------- The URL at the index to which searches should be submitted in order to retrieve JSON results. datatype -------- The datatype that should be used when submitting a search to the index - e.g. JSON for local, JSONP for remote. initialsearch ------------- Default to true, in which case a search-all will be submitted to the index on page load. Set to false to wait for user input before issuing the first search. fields ------ A list of which fields the index should return in result objects (by default elasticsearch returns them all). partial_fields -------------- A definition of which fields to return, as per elasticsearch docs http://www.elasticsearch.org/guide/reference/api/search/fields.html nested ------ A list of keys for which the content should be considered nested for query and facet purposes. NOTE this requires that such keys be referenced with their full scope e.g. nestedobj.nestedfield. Only works on top-level keys so far. default_url_params ------------------ Any query parameters that the index search URL needs by default. freetext_submit_delay --------------------- When search terms are typed in the search box, they are automatically submitted to the index. This field specifies in milliseconds how long to wait before sending another query - e.g. waiting for the user to finish typing a word. q - Specify a query value to start with when the page is loaded. Will be submitted as the initial search value if initialsearch is enabled. Will also be set as the value of the searchbox on page load. predefined_filters ------------------ Facet / query values to apply to all searches. Give each one a reference key, then in each object define it as per an elasticsearch query for appending to the bool must. If these filters should be applied at the nested level, then prefix the name with the relevant nesting prefix. e.g. if the nested object is called stats, call the filter stats.MYFILTER. filter ------- JSON document describing an `elasticsearch filter <http://www.elasticsearch.org/guide/reference/api/search/filter/>`_ paging ------ An object defining the paging settings: from ---- Which result number to start displaying results from size ---- How many results to get and display per "page" of results pager_on_top ------------ Default to false, in which case the pager - e.g. result count and prev / next page buttons - only appear at the bottom of the search results. Set to true to show the pager at the top of the search results as well. pager_slider ------------ If this is set to true, then the paging options will be a left and right arrow at the bottom, with the count in between, but a bit bigger and more slider-y than the standard one. Works well for displaying featured content, for example. sort ---- A list of objects defining how to sort the results, as per elasticsearch sorting. searchwrap_start searchwrap_end ---------------- HTML values in which to wrap the full result set, to style them into the page they are being injected into. resultwrap_start resultwrap_end ---------------- HTML values in which to wrap each result object result_box_colours ------------------ A list of background colours that will be randomly assigned to each result object that has the "result_box" class. To use this, specify the colours in this list and ensure that the "result_display" option uses the "result_box" class to wrap the result objects. fadein ------ Define a fade-in delay in milliseconds so that whenever a new list of results is displays, it uses the fade-in effect. post_search_callback -------------------- This can define or reference a function that will be executed any time new search results are retrieved and presented on the page. post_init_callback ------------------ This can define or reference a function that will be executed any time new facetview object is being created pushstate --------- Updates the URL string with the current query when the user changes the search terms linkify ------- Makes any URLs in the result contents into clickable links default_operator ---------------- Sets the default operator in text search strings - elasticsearch uses OR by default, but can also be AND default_freetext_fuzzify ------------------------ If this exists and is not false, it should be either * or ~. If it is * then * will be prepended and appended to each string in the freetext search term, and if it is ~ then ~ will be appended to each string in the freetext search term. If * or ~ or : are already in the freetext search term, it will be assumed the user is already trying to do a complex search term so no action will be taken. NOTE these changes are not replicated into the freetext search box - the end user will not know they are happening. add_undefined ------------- Adds a new value to each set, 'undefined', coresponding to the facet response 'missing'. For each property, 'undefined' will cover all objects that do not have a value for it. static_filter ------------- A static filter with predefined values that can be included in the search. oneorless --------- An option for static filters saying that the user can either select a value, or not select anything. Therefore, when a new value is selected, the previous one, if it exists, will be disabled. hierarchy --------- When this exists and it is not false, it defines a controled vocabulary for the facet values. The values are classified into categories, sub-categories... with an unlimited number of possible children. All the categories are possible facets but they are not obtained from the data. permanent_filters ----------------- When this is set to true, the main filters (the defined facet values) will remain visible even if there is only one possible value. facet_display_options --------------------- When this parameter is not an empty list, it defines a list of settings for displaying facet values. Possible values: checkbox - when included, a checkbox is displayed in front of the options sort - when included, one can sort the facet values The checkbox option is only possible for one layer trees enable_wildcard_search ---------------------- When this parameter is set to true, wildcards in the query string will be parsed. querystr_filtered_chars ----------------------- Add a string with the chars that should be filtered out when performing the search. Use this when you want to escape wildcards added by the user. no_results_message ------------------ Custom message to display when there are no results found for the search. The default one is "Not found..." */ function sortNumber(a,b){ return a-b; } // now the facetview function (function($) { $.fn.facetview = function(options) { // a big default value (pulled into options below) // demonstrates how to specify an output style based on the fields that //can be found in the result object where a specified field is not //found, the pre and post for it are just ignored var resdisplay = [ [ { 'field': 'author.name' }, { 'pre': '(', 'field': 'year', 'post': ')' } ], [ { 'pre': '<strong>', 'field': 'title', 'post': '</strong>' } ], [ { 'field': 'howpublished' }, { 'pre': 'in <em>', 'field': 'journal.name', 'post': '</em>,' }, { 'pre': '<em>', 'field': 'booktitle', 'post': '</em>,' }, { 'pre': 'vol. ', 'field': 'volume', 'post': ',' }, { 'pre': 'p. ', 'field': 'pages' }, { 'field': 'publisher' } ], [ { 'field': 'link.url' } ] ]; // specify the defaults var defaults = { 'config_file': false, 'embedded_search': true, 'searchbox_class': '', 'searchbox_fieldselect': [], 'searchbox_shade': '#ececec', 'search_sortby': [], 'save_link': true, 'sharesave_link': false, 'description': '', 'facets': [], 'extra_facets': {}, 'enable_rangeselect': false, 'include_facets_in_querystring': false, 'result_display': resdisplay, 'display_images': true, 'search_url': '', 'datatype': 'jsonp', 'initialsearch': true, 'fields': false, 'partial_fields': false, 'nested': [], 'default_url_params': {}, 'freetext_submit_delay': '500', 'q': '', 'sort': [], 'predefined_filters': {}, 'paging': { 'from': 0, 'size': 10 }, 'pager_on_top': false, 'pager_slider': false, 'searchwrap_start': '<table class="table table-striped ' + 'table-bordered" id="facetview_results">', 'searchwrap_end': '</table>', 'resultwrap_start': '<tr><td>', 'resultwrap_end': '</td></tr>', 'result_box_colours': [], 'fadein': 800, 'post_search_callback': false, 'post_init_callback': false, 'pushstate': true, 'linkify': true, 'default_operator': 'OR', 'default_freetext_fuzzify': false, 'static_filters': [], 'hierarchy': false, 'permanent_filters': false, 'query_filter': false, 'facet_display_options' : [], 'enable_wildcard_search' : true, 'querystr_filtered_chars' : '', 'no_results_message' : false, "default_sort":{}, "resizable": true }; if (defaults.sharesave_link) { $("<div>") .addClass("facet-share") .insertBefore(".facet-view-simple"); $("<a>") .addClass("facetview-share") .text("Share this search") .appendTo(".facet-share"); $("<i>") .addClass("share-icon") .prependTo(".facetview-share"); } // and add in any overrides from the call // these options are also overridable by URL parameters // facetview options are declared as a function so they are available // externally // (see bottom of this file) var provided_options = $.extend(defaults, options); var url_options = $.getUrlVars(); $.fn.facetview.options = $.extend(provided_options, url_options); var options = $.fn.facetview.options; // if hierarchy is missing create a default one if (!options.hierarchy) { options.hierarchy = {}; for (var i = 0; i < options.facets.length; i++) { options.hierarchy[options.facets[i].field] = []; } } window.embed = url_options.embed; if (url_options.source) { var from = url_options.source.from; options.paging.from = !from ? options.paging.from : from; var size = url_options.source.size; options.paging.size = !size ? options.paging.size : size; var sort = url_options.source.sort; options.sort = !sort ? options.sort : sort; } // =============================================== // functions to do with filters // =============================================== // show the filter values var showfiltervals = function(event) { event.preventDefault(); if ($(this).hasClass('facetview_open')) { $(this).children('i').removeClass('icon-minus'); $(this).children('i').addClass('icon-plus'); $(this).removeClass('facetview_open'); $('[id="facetview_' + $(this).attr('rel') + '"]', obj) .children().find('.facetview_filtervalue').hide(); $(this).parent().parent() .siblings('.facetview_filtervalue_hierarchic').hide(); $(this).parent().parent() .siblings('.facetview_filterdiv_hierarchic').hide(); $(this).siblings('.facetview_filteroptions').hide(); } else { $(this).children('i').removeClass('icon-plus'); $(this).children('i').addClass('icon-minus'); $(this).addClass('facetview_open'); $('[id="facetview_' + $(this).attr('rel') + '"]', obj) .children().find('.facetview_filtervalue').show(); $(this).parent().parent() .siblings('.facetview_filtervalue_hierarchic').show(); $(this).parent().parent() .siblings('.facetview_filterdiv_hierarchic').show(); $(this).siblings('.facetview_filteroptions').show(); var ml_button = $(this).parent() .children('.facetview_filteroptions') .children('.facetview_moreless'); if (ml_button.text() == 'Less') { ml_button.trigger('click'); } else { ml_button.trigger('click'); ml_button.trigger('click'); } } }; // show the filter values - the tree version var showfiltervalues = function(event) { event.preventDefault(); event.target = $(event.target).closest(".facetview_filter").find(".facetview_showtree"); var these = $(this); if (these.hasClass('facetview_open')) { these.removeClass('facetview_open'); these.siblings().hide(); // these.siblings('.facetview_tree').jstree('open_all'); } else { these.addClass('facetview_open'); if ($.inArray($(event.target).attr("id"), options.rangefacets) !== -1){ these.parent().find(".facetview_rangecontainer").show(); facetrange(event); return; } if ($.inArray($(event.target).attr("id"), options.geofacets) !== -1){ these.parent().find(".facetview_geocontainer").show(); facetgeo(event); return; } var siblings = these.siblings(); these.siblings('.facetview_tree').jstree('close_all'); siblings.show(); var or_button = these.siblings('.facetview_filter_options') .find('.facetview_or').attr('rel'); if (or_button === 'AND') { var children = siblings.children().children(); var c_len = children.length; for (var childID = 0; childID < c_len; childID++) { var child = $(children[childID]); if (child.text().indexOf('(0)') > -1) { child.hide(); } } } //find out if the list is ckecbox type var checkbox = false; var length = options.facets.length; var title = these.attr('title'); var facet_rel = title; //in the case of a checkbox list, disable the checked option for (var i = 0; i < length; i++) { var item = options.facets[i]; if ('field' in item && (item.field === title || item.display === title)) { display_opt = item.facet_display_options; facet_rel = item.field; for (var opt in display_opt) { if (display_opt[opt] === 'checkbox') { checkbox = true; break; } } break; } } if (checkbox) { /* if the checkbox display option is set, checked values should be as many as the ones in facetview_filterselected */ var selected = $('.facetview_filterselected[rel="' + facet_rel + '"]'); var len = selected.length; var checked = these.siblings('.jstree') .find('.jstree-clicked').length; if (checked !== len) { for (i = 0; i <= len; i++) { var option = selected[i]; var rel = $(option).attr('rel'); var href = $(option).attr('href'); var box = $('li[rel="' + rel + '"][title="' + href + '"]'); box.find('.jstree-checkbox').trigger('click'); } } } //adjust the tree height var nodes = $('.jstree-node'); var lineHeight = nodes.height(); var id = 0; while (!lineHeight) { lineHeight = $(nodes[id]).height(); id += 1; } var tree = these.siblings('.jstree'); var ulTree = tree.children('.jstree-container-ul'); var ulHeight = ulTree.height(); ulTree.addClass('facetview_tree_container'); tree.height(Math.min(ulHeight, 10 * lineHeight) + 'px'); } }; //recursive function that returns the json in a hierarchy var getJson = function(value, property, rel) { var count = ''; count = ' (0)'; var jsonval = []; if (typeof value === 'string') { jsonval.push( { 'text': value + count, 'li_attr' : { 'rel' : property, 'class' : 'facetview_filterchoice leaf', 'title' : value } } ); return jsonval; } if (value instanceof Array) { for (var element in value) { jsonval = jsonval.concat(getJson(value[element], property)); } return jsonval; } for (var element in value) { var children = value[element]; if (children.length > 0) { jsonval.push({ 'text': element + count, 'state': { 'opened' : true, 'selected' : false }, 'li_attr' : { 'rel' : property, 'class' : 'facetview_filterchoice', 'title' : element }, 'children': getJson(children, property) }); } else { jsonval = jsonval.concat(getJson(element, property)); } } return jsonval; }; // function to switch filters to OR instead of AND var orfilters = function(event) { event.preventDefault(); var that = $(this); var id = 'facetview_group_' + that.attr('href').replace(/\./gi, '_').replace(/\:/gi, '_'); if (that.attr('rel') === 'AND') { that.attr('rel', 'OR'); that.text('OR'); that.css({'color': '#333'}); toc = $('[id="' + id + '"]') .children('.rel-between').text('OR'); $('.facetview_filterselected[rel="' + that.attr('href') + '"]', obj) .addClass('facetview_logic_or'); } else { that.attr('rel', 'AND'); that.text('AND'); $('[id="' + id + '"]').children('.rel-between').text('AND'); that.css({'color': '#aaa'}); $('.facetview_filterselected[rel="' + that.attr('href') + '"]', obj) .removeClass('facetview_logic_or'); } dosearch(); }; function createtreefromdata(tree, ord, opt, values) { var facet = tree.attr("rel"); var tmp_values = values; if (jQuery.inArray(facet, options.rangefacets) !== -1){ if (values.length > 1){ tmp_values = [] tmp_values.push(values[0]); tmp_values.push(values[values.length - 1]); } } if (!opt) { opt = []; } tree.jstree({ 'plugins' : opt, 'core' : { 'animation': 0, 'data' : tmp_values, 'check_callback' : true, 'themes' : { 'name' : 'default', 'icons' : false, 'dots': true } }, 'checkbox' : { 'whole_node' : true, 'keep_selected_style' : false, 'tie_selection': false }, 'sort' : function(a, b) { var a_text = this.get_node(a).text; var b_text = this.get_node(b).text; if (ord === 'term') { return a_text > b_text ? 1 : -1; } else if (ord === 'reverse_term') { return a_text > b_text ? -1 : 1; } else { var a_size = a_text.substring( a_text.lastIndexOf('(') + 1, a_text.lastIndexOf(')')); a_size = parseInt(a_size); var b_size = b_text.substring( b_text.lastIndexOf('(') + 1, b_text.lastIndexOf(')')); b_size = parseInt(b_size); if (ord === 'count') { return a_size - b_size; } else { return b_size - a_size; } } } }) .bind('select_node.jstree', function(event, data) { var attributes = data.node.li_attr; if (attributes.class.indexOf('leaf') > -1) { clickfilterchoice(false, attributes.rel, attributes.title, false); dosearch(); } else { var children = data.node.children_d; var branch = $('#' + data.node.id); tree.jstree('open_all', branch); children.map(function(childID) { var child = $('#' + childID); if (child.hasClass('jstree-leaf')) { clickfilterchoice(false, child.attr('rel'), child.attr('title'), false); } }); dosearch(); } }) .bind('deselect_node.jstree', function(event, data) { var attributes = data.node.li_attr; $('.facetview_filterselected[rel="' + attributes.rel + '"][href="' + attributes.title + '"]').trigger('click'); //reset var parent_tree = $(this); var lineHeight = parent_tree.find('.jstree-leaf').height(); parent_tree.height(10 * lineHeight + 'px'); }) .on('open_node.jstree', function(event, data) { var children = data.node.children; var len = children.length; for (var idx = 0; idx < len; idx++) { var child = $('#' + children[idx]); if (child.children('a.jstree-anchor') .text().indexOf('(0)') != -1) { child.hide(); } } }); } // function to perform for sorting of filters var sortfilters = function(event) { event.preventDefault(); var sortwhat = $(this).attr('href'); var tree = $('.facetview_tree[rel="' + sortwhat + '"]'); var which = 0; var length = options.facets.length; for (var i = 0; i < length; i++) { var item = options.facets[i]; if ('field' in item) { if (item.field === sortwhat) { which = i; } } } // iterate to next sort type on click. order is term, rterm, //count, rcount if ($(this).hasClass('facetview_term')) { options.facets[which].order = 'reverse_term'; $(this).html('a-z <i class="icon-arrow-up"></i>'); $(this).removeClass('facetview_term') .addClass('facetview_rterm'); } else if ($(this).hasClass('facetview_rterm')) { options.facets[which].order = 'count'; $(this).html('count <i class="icon-arrow-down"></i>'); $(this).removeClass('facetview_rterm') .addClass('facetview_count'); } else if ($(this).hasClass('facetview_count')) { options.facets[which].order = 'reverse_count'; $(this).html('count <i class="icon-arrow-up"></i>'); $(this).removeClass('facetview_count') .addClass('facetview_rcount'); } else if ($(this).hasClass('facetview_rcount')) { options.facets[which].order = 'term'; $(this).html('a-z <i class="icon-arrow-down"></i>'); $(this).removeClass('facetview_rcount') .addClass('facetview_term'); } var thejson = tree.jstree(true).get_json('#'); tree.jstree('destroy'); createtreefromdata(tree, options.facets[which].order, options.facets[which].facet_display_options, thejson); correctFacetRenderer(); }; // insert a geo facet once selected var dofacetgeo = function(rel, dothesearch) { if (dothesearch === undefined){ dothesearch = true; } $('a.facetview_filterselected[rel="' + rel + '"]').remove(); $('#facetview_georesults_' + rel, obj).remove(); $('#facetview_group_' + rel, obj).remove(); // TODO: get the values var type = $('#facetview_geoplaceholder_' + rel, obj).find(".facetview_geo_type_"+rel).attr("value"); var facet_display_value = ""; var data = {}; if (type === "distance"){ var lat = $('#facetview_geoplaceholder_' + rel + ' .facetview_latval_' + rel, obj).attr("value"); var lon = $('#facetview_geoplaceholder_' + rel + ' .facetview_lonval_' + rel, obj).attr("value"); var dist = $('#facetview_geoplaceholder_' + rel + ' .facetview_distval_' + rel, obj).attr("value"); facet_display_value = "(" + lat + ", " + lon + ") - " + dist; data = {type:type, lat:lat, lon:lon, dist:dist}; } else { var lat1 = $('#facetview_geoplaceholder_' + rel + ' .facetview_latval1_' + rel, obj).attr("value"); var lon1 = $('#facetview_geoplaceholder_' + rel + ' .facetview_lonval1_' + rel, obj).attr("value"); var lat2 = $('#facetview_geoplaceholder_' + rel + ' .facetview_latval2_' + rel, obj).attr("value"); var lon2 = $('#facetview_geoplaceholder_' + rel + ' .facetview_lonval2_' + rel, obj).attr("value"); facet_display_value = "(" + lat1 + ", " + lon1 + ") - (" + lat2 + ", " + lon2 + ")"; data = {type:type, lat1:lat1, lon1:lon1, lat2:lat2, lon2:lon2}; } var href = $(this).attr('href'); var newobj = [ '<div style="display:none;" class="btn-group"', 'id="facetview_georesults_', rel, '"> ', '<a class="facetview_filterselected facetview_facetgeo facetview_logic_or fores ', 'facetview_clear btn btn-info"', 'facettype="geo"', ' rel="', rel, '" alt="remove" title="remove" href="', href, '">', ' <i class="icon-white icon-remove"></i></a></div>' ].join(''); $('#facetview_selectedfilters', obj).append(newobj); $('#facetview_georesults_' + rel + " .facetview_filterselected").data("geo_data", data); options.paging.from = 0; var relclean = rel.replace(/\./gi, '_').replace(/\:/gi, '_'); var myobj = '<div class="facetview_selection"> <a ' + 'class="facetview_filterselected facetview_logic_or facetview_facetgeo facetview_clear btn'; myobj = [myobj, '" ', 'facettype="geo"', ' rel="', rel, '" alt="remove" title="remove" href="', href, '">', ' <i class="icon-white icon-remove" ', 'style="margin-top:1px;"></i></a>', facet_display_value, '</div>' ].join(''); if ($('div[id="facetview_group_' + relclean + '"]', obj).length) { myobj = '<a class="btn btn-small rel-between" rel="' + href + '" style="color:#aaa">' + op_text + '</a>' + myobj; $('div[id="facetview_group_' + relclean + '"]', obj) .append(myobj); } else { var pobj = [ '<div id="facetview_group_', relclean, '" class="btn-group facetview_selected">', myobj, '</div>' ].join(''); if ($('div.facetview_selected').length) { pobj = '<div class="facet-rel-between"> <a class="btn ' + 'btn-small facet_operator"> AND</a></div>' + pobj; } } $('#facetview_selected_filters', obj).append(pobj); if ($('.current-filters:hidden')) { $('.current-filters').show(); } $('.facetview_filterselected[facettype="geo"]', obj).unbind('click', clearfacetgeo); $('.facetview_filterselected[facettype="geo"]', obj).bind('click', clearfacetgeo); if (dothesearch){ dosearch(); } }; // insert a facet range once selected // TODO: UPDATE var dofacetrange = function(rel) { $('a.facetview_filterselected[rel="' + rel + '"]').remove(); $('#facetview_rangeresults_' + rel, obj).remove(); $('#facetview_group_' + rel, obj).remove(); var values = $('#facetview_slider_' + rel).slider('values'); var min = $('#facetview_slider_' + rel).slider('option', 'min'); var max = $('#facetview_slider_' + rel).slider('option', 'max'); if (min === values[0] && max === values[1]){ dosearch(); return; } var range = $('#facetview_rangechoices_' + rel, obj).html(); var href = $(this).attr('href'); var newobj = [ '<div style="display:none;" class="btn-group"', 'id="facetview_rangeresults_', rel, '"> ', '<a class="facetview_filterselected facetview_facetrange ', 'facetview_clear btn btn-info"', 'facettype="range"', ' rel="', rel, '" alt="remove" title="remove" href="', href, '">', range, ' <i class="icon-white icon-remove"></i></a></div>' ].join(''); $('#facetview_selectedfilters', obj).append(newobj); options.paging.from = 0; var relclean = rel.replace(/\./gi, '_').replace(/\:/gi, '_'); var myobj = '<div class="facetview_selection"> <a ' + 'class="facetview_filterselected facetview_clear btn'; var lowval = $("#facetview_rangechoices_" + rel + " .facetview_lowrangeval_" + rel).html(); var highval = $("#facetview_rangechoices_" + rel + " .facetview_highrangeval_" + rel).html(); var operation = $('.facetview_or[href="' + rel + '"]', obj); var op_text = 'AND'; myobj = [myobj, '" ', 'facettype="range"', ' rel="', rel, '" alt="remove" title="remove" href="', href, '">', ' <i class="icon-white icon-remove" ', 'style="margin-top:1px;"></i></a>', lowval, ' - ', highval, '</div>' ].join(''); if ($('div[id="facetview_group_' + relclean + '"]', obj).length) { myobj = '<a class="btn btn-small rel-between" rel="' + href + '" style="color:#aaa">' + op_text + '</a>' + myobj; $('div[id="facetview_group_' + relclean + '"]', obj) .append(myobj); } else { var pobj = [ '<div id="facetview_group_', relclean, '" class="btn-group facetview_selected">', myobj, '</div>' ].join(''); if ($('div.facetview_selected').length) { pobj = '<div class="facet-rel-between"> <a class="btn ' + 'btn-small facet_operator"> AND</a></div>' + pobj; } } $('#facetview_selected_filters', obj).append(pobj); if ($('.current-filters:hidden')) { $('.current-filters').show(); } $('.facetview_filterselected[facettype="range"]', obj).unbind('click', clearfacetrange); $('.facetview_filterselected[facettype="range"]', obj).bind('click', clearfacetrange); dosearch(); }; // clear a facet geo var clearfacetgeo = function(event) { event.preventDefault(); var rel = $(this).attr('rel'); $('a.facetview_filterselected[rel="' + rel + '"]').remove(); $('#facetview_georesults_' + rel, obj).remove(); $('#facetview_group_' + rel, obj).remove(); $('#facetview_geoplaceholder_' + rel + ' .facetview_latval_' + rel, obj).attr("value", ""); $('#facetview_geoplaceholder_' + rel + ' .facetview_lonval_' + rel, obj).attr("value", ""); $('#facetview_geoplaceholder_' + rel + ' .facetview_distval_' + rel, obj).attr("value", ""); $('#facetview_geoplaceholder_' + rel + ' .facetview_latval1_' + rel, obj).attr("value", ""); $('#facetview_geoplaceholder_' + rel + ' .facetview_lonval1_' + rel, obj).attr("value", ""); $('#facetview_geoplaceholder_' + rel + ' .facetview_latval2_' + rel, obj).attr("value", ""); $('#facetview_geoplaceholder_' + rel + ' .facetview_lonval2_' + rel, obj).attr("value", ""); dosearch(); }; // clear a facet range var clearfacetrange = function(event) { event.preventDefault(); var rel = $(this).attr('rel'); var min = $('#facetview_slider_' + rel).slider('option', 'min'); var max = $('#facetview_slider_' + rel).slider('option', 'max'); delete options.facets[parseFloat(rel)].default_values; $('#facetview_slider_' + rel).slider('values',[min, max]); $('a.facetview_filterselected[rel="' + rel + '"]').remove(); $('#facetview_rangeresults_' + rel, obj).remove(); $('#facetview_group_' + rel, obj).remove(); dosearch(); }; var createfacetgeo = function (options){ var rel = options.rel; if ($("[id='facetview_geoplaceholder_" + rel + "']").length > 0) { return; } var defaults = $('a.facetview_filterselected[rel="' + rel + '"].fores').data("geo_data"); var geoselect = [ '<div id="facetview_geoplaceholder_', rel, '" class="facetview_geocontainer clearfix"> ', '<div class="clearfix">', '<div id="geo-facet-tabs_' + rel +'">', '<input class="facetview_geo_type_'+rel+'" type="text" style="display:none">', '<ul>', '<li><a href="#geo-distance-tab" class="geo-facet-type">Distance</a></li>', '<li><a href="#geo-bounding-box-tab" class="geo-facet-type">Bounding Box</a></li>', '</ul>', '<div id="geo-distance-tab">', '<span>latitude</span><input class="facetview_latval_'+rel+'" type="text"><br/>', '<span>longitude</span><input class="facetview_lonval_'+rel+'" type="text"><br/>', '<span>distance(km)</span><input class="facetview_distval_'+rel+'" type="text"><br/>', '</div>', '<div id="geo-bounding-box-tab">', '<span style="font-weight:bold; width:100px;">Top Left</span><br/>', '<div style="clear:both"><!-- --></div>', '<span style="padding-left:10px">latitude</span><input class="facetview_latval1_'+rel+'" type="text"><br/>', '<span style="padding-left:10px">longitude</span><input class="facetview_lonval1_'+rel+'" type="text"><br/>', '<span style="font-weight:bold; width:100px;">Bottom Right</span><br/>', '<div style="clear:both"><!-- --></div>', '<span style="padding-left:10px">latitude</span><input class="facetview_latval2_'+rel+'" type="text"><br/>', '<span style="padding-left:10px">longitude</span><input class="facetview_lonval2_'+rel+'" type="text"><br/>', '</div>', '</div>', '</div></div>' ].join(''); $("[id='" + options.id + "']").after(geoselect); var activetab = 0; if (defaults !== undefined){ var placeholder = $("#facetview_geoplaceholder_" + rel); placeholder.find(".facetview_geo_type_" + rel).attr("value", defaults.type) if (defaults.type === "distance"){ placeholder.find('.facetview_latval_'