diff options
Diffstat (limited to 'chimere')
| -rw-r--r-- | chimere/search_indexes.py | 2 | ||||
| -rw-r--r-- | chimere/static/chimere/js/jquery.chimere.js | 4 | ||||
| -rw-r--r-- | chimere/static/chimere/js/search-autocomplete.js | 78 | ||||
| -rw-r--r-- | chimere/static/chimere/js/search.js | 2 | ||||
| -rw-r--r-- | chimere/templates/chimere/main_map.html | 3 | ||||
| -rw-r--r-- | chimere/templates/search/search.html | 12 | ||||
| -rw-r--r-- | chimere/urls.py | 2 | ||||
| -rw-r--r-- | chimere/views.py | 12 | 
8 files changed, 109 insertions, 6 deletions
| diff --git a/chimere/search_indexes.py b/chimere/search_indexes.py index 7f45b06..6b57450 100644 --- a/chimere/search_indexes.py +++ b/chimere/search_indexes.py @@ -25,6 +25,8 @@ from chimere import models  class GeographicItemIndex(indexes.SearchIndex):      text = indexes.CharField(document=True, use_template=True)      categories = indexes.MultiValueField() +    # for autocomplete +    content_auto = indexes.EdgeNgramField(model_attr='name')      def index_queryset(self, using=None):          return self.get_model().objects.filter(status='A') diff --git a/chimere/static/chimere/js/jquery.chimere.js b/chimere/static/chimere/js/jquery.chimere.js index d04e1ae..23165fa 100644 --- a/chimere/static/chimere/js/jquery.chimere.js +++ b/chimere/static/chimere/js/jquery.chimere.js @@ -732,8 +732,8 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {                      var id = this.id.substr(this.id.lastIndexOf("_")+1);                      helpers.zoom_to_subcategories([id]);                  }); -                $(".toggle_category").parent().bind("click", function (e) { -                    var item = $(this).children('.toggle_category'); +                $(".toggle_category").bind("click", function (e) { +                    var item = $(this);                      var id = item.attr('id').substr(item.attr('id').lastIndexOf("_")+1);                      methods.toggle_category(id);                  }); diff --git a/chimere/static/chimere/js/search-autocomplete.js b/chimere/static/chimere/js/search-autocomplete.js new file mode 100644 index 0000000..79faab1 --- /dev/null +++ b/chimere/static/chimere/js/search-autocomplete.js @@ -0,0 +1,78 @@ + +var no_result_message = "No results found."; + +var Autocomplete = function(options) { +  this.form_selector = options.form_selector; +  this.url = options.url || '/search/autocomplete/'; +  this.delay = parseInt(options.delay || 300); +  this.minimum_length = parseInt(options.minimum_length || 3); +  this.form_elem = null; +  this.query_box = null; +} + +Autocomplete.prototype.setup = function() { +  var self = this; + +  this.form_elem = $(this.form_selector); +  this.query_box = this.form_elem.find('input[name=q]'); + +  // watch the input box. +  this.query_box.on('keyup', function() { +    var query = self.query_box.val(); +    if (query){ +        $('#haystack-search').removeAttr("disabled"); +    } else { +        $('#haystack-search').attr('disabled', 'disabled'); +    } + +    if(query.length < self.minimum_length) { +      return false; +    } + +    self.fetch(query); +  }) + +  // on selecting a result, populate the search field. +  this.form_elem.on('click', '.ac-result', function(ev) { +    self.query_box.val($(this).text()); +    $('.ac-results').remove(); +    return false; +  }) +} + +Autocomplete.prototype.fetch = function(query) { +  var self = this ; + +  $.ajax({ +    url: this.url, +    data: { 'q': query }, +    success: function(data) { +      self.show_results(data); +    } +  }) +} + +Autocomplete.prototype.show_results = function(data) { +  // Remove any existing results. +  $('.ac-results').remove(); + +  var results = data.results || [] +  var results_wrapper = $('<div class="ac-results"></div>'); +  var base_elem = $('<div class="result-wrapper"><a href="#" class="ac-result"></a></div>'); + +  if(results.length > 0) { +    for(var res_offset in results) { +      var elem = base_elem.clone(); +      // don't use .html(...) here, as it opens to XSS. +      elem.find('.ac-result').text(results[res_offset]); +      results_wrapper.append(elem); +    } +  } +  else { +    var elem = base_elem.clone(); +    elem.text(no_result_message); +    results_wrapper.append(elem); +  } + +  this.query_box.after(results_wrapper) +} diff --git a/chimere/static/chimere/js/search.js b/chimere/static/chimere/js/search.js index 779a32d..6f46f3f 100644 --- a/chimere/static/chimere/js/search.js +++ b/chimere/static/chimere/js/search.js @@ -8,12 +8,14 @@ function load_search_box(){  function haystack_search(evt, page){      search_result = new Array();      $('#categories').find('#ul_categories > li > input').attr("checked", false); +    if (!$('#id_q').val()) return false;      var c_url = search_url + "?q=" + $('#id_q').val();      if (page){          c_url += '&page=' + page;      }      $.get(c_url).done(function( data ) { +        $('.ac-results').remove();          $('#search-result').html(data).show('slow');      });      return false; diff --git a/chimere/templates/chimere/main_map.html b/chimere/templates/chimere/main_map.html index 5f01fdb..a3dec9a 100644 --- a/chimere/templates/chimere/main_map.html +++ b/chimere/templates/chimere/main_map.html @@ -6,7 +6,8 @@  {% head_jme %}  <script src="{{ STATIC_URL }}chimere/js/jquery.chimere.js" type="text/javascript"></script>  {% if has_search %} -<script src="{{ STATIC_URL }}chimere/js/search.js" type="text/javascript"></script>{% endif %} +<script src="{{ STATIC_URL }}chimere/js/search.js" type="text/javascript"></script> +<script src="{{ STATIC_URL }}chimere/js/search-autocomplete.js" type="text/javascript"></script>{% endif %}  {{ block.super }}  {% endblock %}  {% block message_edit %}{% endblock %} diff --git a/chimere/templates/search/search.html b/chimere/templates/search/search.html index 28b2b3a..4405381 100644 --- a/chimere/templates/search/search.html +++ b/chimere/templates/search/search.html @@ -1,4 +1,4 @@ -{% load url from future %}{% load i18n%} +{% load url from future %}{% load i18n %}  {% if query %}  <script type='text/javascript'>  var new_ids = [{% for result in page.object_list %}{{result.object.pk}}{% if not forloop.last %}, {% endif %}{% endfor %}]; @@ -31,17 +31,23 @@ for (idx=0 ; idx < new_ids.length ; idx++){  {% endif %}  {% else %} -<form id='search-form'> -    {{ form.q }} <button name='haystack-search' id='haystack-search' type='button'>{% trans "Search" %}</button> +<form id='search-form' class='autocomplete-me'> +    <input type="text" id="id_q" name="q" autocomplete="off"/> +    <button name='haystack-search' id='haystack-search' type='button' disabled='disabled' class="btn btn-default">{% trans "Search" %}</button>  </form>  <div id='search-result'></div>  <script type='text/javascript'> +no_result_message = "{% trans 'No results found.' %}";  $(function(){      $('#haystack-search').click(          function(evt){              $("#main-map").chimere("razMap");              haystack_search(evt);      }); +    window.autocomplete = new Autocomplete({ +        form_selector: '.autocomplete-me' +    }); +    window.autocomplete.setup();  });  </script>  {% endif %} diff --git a/chimere/urls.py b/chimere/urls.py index 2ed88aa..f67662b 100644 --- a/chimere/urls.py +++ b/chimere/urls.py @@ -79,6 +79,8 @@ if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') \              template='search/search.html',              form_class=SearchForm          ), name='haystack_search'), +        url(r'^search/autocomplete/$', 'autocomplete', +            name='autocomplete-search')      )      #urlpatterns += [url(r'^search/', include('haystack.urls')),] diff --git a/chimere/views.py b/chimere/views.py index 5999af7..0afb6fa 100644 --- a/chimere/views.py +++ b/chimere/views.py @@ -938,8 +938,20 @@ def rss(request, area_name=''):  from django.core.paginator import Paginator, InvalidPage  SearchView = None +autocomplete = None  if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') \     and settings.CHIMERE_SEARCH_ENGINE:      from haystack.views import SearchView as HaystackSearchView +    from haystack.query import SearchQuerySet      class SearchView(HaystackSearchView):          pass +    def autocomplete(request): +        sqs = SearchQuerySet().autocomplete( +                                content_auto=request.GET.get('q', ''))[:5] +        suggestions = [result.object.name for result in sqs] +        # make sure it returns a JSON object, not a bare list. +        # otherwise, it could be vulnerable to an XSS attack. +        the_data = json.dumps({ +            'results': suggestions +        }) +        return HttpResponse(the_data, content_type='application/json') | 
