We now have a nice(r) autocompletion widget that will popup the list of options directly underneath the input fild. A few changes are done in this commit that couldn't be split any further:
- Use jQuery's $.ajax() for the completion query - Use the application/json content type on the completion answer to allow jQuery to directly create an object from it instead of giving back a string (because of the text/plain content type) - Crafted more logical objects in the json answer ie all properties are at the same level instead of the default queryset serializer that put the object fields in a 'field' sub-object. - Use selectize.js for the autocompletion widget logic A slight change in behaviour is that we now don't allow "free form" submitter search, ie we need a valid completion to search with that specific person (through its primary key). I didn't remove the backend logic that allows the "free form" mechanism, but maybe we should. Signed-off-by: Damien Lespiau <damien.lesp...@intel.com> --- apps/patchwork/filters.py | 16 ++--- apps/patchwork/views/base.py | 18 +++-- templates/patchwork/filters.html | 152 +++++++++------------------------------ 3 files changed, 51 insertions(+), 135 deletions(-) diff --git a/apps/patchwork/filters.py b/apps/patchwork/filters.py index 41f4d32..db6d0a6 100644 --- a/apps/patchwork/filters.py +++ b/apps/patchwork/filters.py @@ -87,6 +87,11 @@ class SubmitterFilter(Filter): self.person = None self.person_match = None submitter_id = None + + str = str.strip() + if str == '': + return + try: submitter_id = int(str) except ValueError: @@ -128,15 +133,8 @@ class SubmitterFilter(Filter): return '' def _form(self): - name = '' - if self.person: - name = self.person.name - return mark_safe(('<input onKeyUp="submitter_field_change(this)" ' + - 'name="submitter" id="submitter_input" ' + - 'class="form-control"' + - 'value="%s">' % escape(name)) + - '<select id="submitter_select" ' + - 'disabled="true"></select>') + return mark_safe(('<input type="text" name="submitter" ' + \ + 'id="submitter_input" class="form-control">')) def key(self): if self.person: diff --git a/apps/patchwork/views/base.py b/apps/patchwork/views/base.py index fa7dd99..b9b38e3 100644 --- a/apps/patchwork/views/base.py +++ b/apps/patchwork/views/base.py @@ -17,12 +17,13 @@ # along with Patchwork; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +import json from patchwork.models import Patch, Project, Person, EmailConfirmation from django.shortcuts import render_to_response, get_object_or_404 from django.http import HttpResponse, HttpResponseRedirect, Http404 from patchwork.requestcontext import PatchworkRequestContext -from django.core import serializers, urlresolvers +from django.core import urlresolvers from django.template.loader import render_to_string from django.conf import settings from django.db.models import Q @@ -87,10 +88,9 @@ def confirm(request, key): def submitter_complete(request): search = request.GET.get('q', '') limit = request.GET.get('l', None) - response = HttpResponse(mimetype = "text/plain") if len(search) <= 3: - return response + return HttpResponse(content_type="application/json") queryset = Person.objects.filter(Q(name__icontains = search) | Q(email__icontains = search)) @@ -103,9 +103,15 @@ def submitter_complete(request): if limit is not None and limit > 0: queryset = queryset[:limit] - json_serializer = serializers.get_serializer("json")() - json_serializer.serialize(queryset, ensure_ascii=False, stream=response) - return response + data = [] + for submitter in queryset: + item = {} + item['pk'] = submitter.id + item['name'] = submitter.name + item['email'] = submitter.email + data.append(item) + + return HttpResponse(json.dumps(data), content_type="application/json") help_pages = {'': 'index.html', 'about/': 'about.html', diff --git a/templates/patchwork/filters.html b/templates/patchwork/filters.html index 5bf446f..24dd38c 100644 --- a/templates/patchwork/filters.html +++ b/templates/patchwork/filters.html @@ -18,127 +18,40 @@ function filter_click() } -function enable_selected_submitter(select, input) -{ - select.name = 'submitter'; - input.name = ''; -} -function filter_form_submit(form) -{ - var i; - - var submitter_select = document.getElementById("submitter_select"); - var submitter_input = document.getElementById("submitter_input"); - if (!submitter_select || !submitter_input) { - req = null; - return; - } - - /* submitter handling. if possible, use the select box, otherwise leave - * as-is (and so the text box is used). */ - - if (submitter_select.options.length == 0) { - /* if there's no match, just use the input */ - - } else if (submitter_select.options.length == 1) { - /* if there's only one match, request by id */ - submitter_select.selectedIndex = 0; - enable_selected_submitter(submitter_select, submitter_input); - - } else if (submitter_select.selectedIndex != -1) { - /* if the user has explicitly selected, request by id */ - enable_selected_submitter(submitter_select, submitter_input); - - } - for (i = 0; i < form.elements.length; i++) { - var e = form.elements[i]; - if (e.type == 'submit') { - continue; - } - - /* handle submitter data */ - if (e.type == 'select-one') { - if (e.name == '') { - e.disabled = true; - } - if (e.selectedIndex != -1 - && e.options[e.selectedIndex].value == '') { - e.disabled = true; +$(document).ready(function() { + $('#submitter_input').selectize({ + valueField: 'pk', + labelField: 'name', + searchField: ['name', 'email'], + maxItems: 1, + persist: false, + render: { + option: function(item, escape) { + return '<div>' + escape(item.name) + ' <' + + escape(item.email) + '>' + '</div>'; + }, + item: function(item, escape) { + return '<div>' + escape(item.name) + '</div>'; } - - continue; - } - - if (e.value == '') { - e.disabled = true; - } - } -} - -var req = null; - -function submitter_complete_response() -{ - if (req.readyState != 4) { - return - } - - var completions; - eval("completions = " + req.responseText); - - if (completions.length == 0) { - req = null; - return; - } - - var submitter_select = document.getElementById("submitter_select"); - var submitter_input = document.getElementById("submitter_input"); - if (!submitter_select || !submitter_input) { - req = null; - return; - } - - for (i = 0; i < completions.length; i++) { - name = completions[i]['fields']['name']; - if (name) { - name = completions[i]['fields']['name'] + - ' <' + completions[i]['fields']['email'] + '>'; - } else { - name = completions[i]['fields']['email']; + }, + load: function(query, callback) { + if (query.length < 4) + return callback(); + + req = $.ajax({ + url: '{% url 'patchwork.views.submitter_complete' %}?q=' + + encodeURIComponent(query) + '&l=10', + error: function() { + callback(); + }, + success: function(res) { + callback(res); + } + }); } - o = new Option(name, completions[i]['pk']); - submitter_select.options[i] = o; - } - - /* remove remaining options */ - for (; i < submitter_select.length; i++) { - submitter_select.options[i] = null; - } - - submitter_select.disabled = false; - req = null; -} - -function submitter_field_change(field) -{ - var limit = 20; - var value = field.value; - if (value.length < 4) { - return; - } - - if (req) { - return; - } - - var url = '{% url 'patchwork.views.submitter_complete' %}?q=' + value + - '&l=' + limit; - req = new XMLHttpRequest(); - req.onreadystatechange = submitter_complete_response; - req.open("GET", url, true); - req.send(''); -} + }); +}); </script> <div class="filters"> @@ -161,8 +74,7 @@ function submitter_field_change(field) {% endif %} </div> <div id="filterform" style="padding-top: 1em; display: none"> - <form class="form-horizontal" role="form" method="get" - onSubmit="return filter_form_submit(this)"> + <form class="form-horizontal" role="form" method="get"> {% for filter in filters.available_filters %} <div class="form-group"> <label class="col-sm-2 control-label">{{ filter.name }}</label> -- 1.8.3.1 _______________________________________________ Patchwork mailing list Patchwork@lists.ozlabs.org https://lists.ozlabs.org/listinfo/patchwork