Skip to content

Commit 876e366

Browse files
committed
added instant search to the tags page
1 parent 0459ee4 commit 876e366

File tree

10 files changed

+202
-48
lines changed

10 files changed

+202
-48
lines changed

Diff for: askbot/doc/source/changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Changes in Askbot
33

44
Development version
55
-------------------
6+
* Added instant search to the tags page
67
* Added a placeholder template for the custom javascript on the question page
78
* Allowed to disable the big "ask" button.
89
* Some support for the media compression (Tyler Mandry)

Diff for: askbot/media/js/live_search.js

+142-1
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ TagWarningBox.prototype.showWarning = function(){
267267
*/
268268
var InputToolTip = function() {
269269
WrappedElement.call(this);
270+
this._promptText = gettext('search or ask your question');
270271
};
271272
inherits(InputToolTip, WrappedElement);
272273

@@ -284,6 +285,10 @@ InputToolTip.prototype.dim = function() {
284285
this._element.addClass('dimmed');
285286
};
286287

288+
InputToolTip.prototype.setPromptText = function(text) {
289+
this._promptText = text;
290+
};
291+
287292
InputToolTip.prototype.setClickHandler = function(handler) {
288293
this._clickHandler = handler;
289294
};
@@ -292,7 +297,7 @@ InputToolTip.prototype.createDom = function() {
292297
var element = this.makeElement('div');
293298
this._element = element;
294299
element.addClass('input-tool-tip');
295-
element.html(gettext('search or ask your question'));
300+
element.html(this._promptText);
296301
this.decorate(element);
297302
};
298303

@@ -882,3 +887,139 @@ FullTextSearch.prototype.decorate = function(element) {
882887

883888
$("form#searchForm").submit(me.makeFormSubmitHandler());
884889
};
890+
891+
/**
892+
* @constructor
893+
*/
894+
var TagSearch = function() {
895+
WrappedElement.call(this);
896+
this._isRunning = false;
897+
};
898+
inherits(TagSearch, WrappedElement);
899+
900+
TagSearch.prototype.getQuery = function() {
901+
return $.trim(this._element.val());
902+
};
903+
904+
TagSearch.prototype.setQuery = function(val) {
905+
this._element.val(val);
906+
};
907+
908+
TagSearch.prototype.getSort = function() {
909+
//todo: read it off the page
910+
var link = $('.tabBar a.on');
911+
if (link.length === 1) {
912+
var sort = link.attr('id').replace('sort_', '');
913+
if (sort === 'name' || sort === 'used') {
914+
return sort;
915+
}
916+
}
917+
return 'name';
918+
};
919+
920+
TagSearch.prototype.getIsRunning = function() {
921+
return this._isRunning;
922+
};
923+
924+
TagSearch.prototype.setIsRunning = function(val) {
925+
this._isRunning = val;
926+
};
927+
928+
TagSearch.prototype.renderResult = function(html) {
929+
this._contentBox.html(html);
930+
};
931+
932+
TagSearch.prototype.runSearch = function() {
933+
var data = {
934+
'query': this.getQuery(),
935+
'sort': this.getSort(),
936+
'page': '1'
937+
};
938+
var me = this;
939+
$.ajax({
940+
dataType: 'json',
941+
data: data,
942+
cache: false,
943+
url: askbot['urls']['tags'],
944+
success: function(data) {
945+
if (data['success']) {
946+
me.renderResult(data['html']);
947+
me.setIsRunning(false);
948+
}
949+
},
950+
error: function() { me.setIsRunning(false); }
951+
});
952+
me.setIsRunning(true);
953+
};
954+
955+
TagSearch.prototype.getToolTip = function() {
956+
return this._toolTip;
957+
};
958+
959+
TagSearch.prototype.makeKeyUpHandler = function() {
960+
var me = this;
961+
return function(evt) {
962+
var keyCode = getKeyCode(evt);
963+
if (me.getIsRunning() === false) {
964+
me.runSearch();
965+
}
966+
};
967+
};
968+
969+
TagSearch.prototype.makeKeyDownHandler = function() {
970+
var me = this;
971+
var xButton = this._xButton;
972+
return function(evt) {
973+
var query = me.getQuery();
974+
var keyCode = getKeyCode(evt);
975+
var toolTip = me.getToolTip();
976+
if (keyCode === 27) {//escape
977+
me.setQuery('');
978+
toolTip.show();
979+
xButton.hide();
980+
return;
981+
}
982+
if (keyCode === 8 || keyCode === 48) {//del or backspace
983+
if (query.length === 1) {
984+
toolTip.show();
985+
xButton.hide();
986+
}
987+
} else {
988+
toolTip.hide();
989+
xButton.show();
990+
}
991+
};
992+
};
993+
994+
TagSearch.prototype.reset = function() {
995+
if (this.getIsRunning() === false) {
996+
this.setQuery('');
997+
this._toolTip.show();
998+
this._xButton.hide();
999+
this.runSearch();
1000+
this._element.focus();
1001+
}
1002+
};
1003+
1004+
TagSearch.prototype.decorate = function(element) {
1005+
this._element = element;
1006+
this._contentBox = $('#ContentLeft');
1007+
this._xButton = $('input[name=reset_query]');
1008+
element.keyup(this.makeKeyUpHandler());
1009+
element.keydown(this.makeKeyDownHandler());
1010+
1011+
var me = this;
1012+
this._xButton.click(function(){ me.reset() });
1013+
1014+
var toolTip = new InputToolTip();
1015+
toolTip.setPromptText(askbot['data']['tagSearchPromptText']);
1016+
toolTip.setClickHandler(function() {
1017+
element.focus();
1018+
});
1019+
element.after(toolTip.getElement());
1020+
//below is called after getElement, b/c element must be defined
1021+
if (this.getQuery() !== '') {
1022+
toolTip.hide();//hide if search query is not empty
1023+
}
1024+
this._toolTip = toolTip;
1025+
};

Diff for: askbot/media/style/style.css

-1
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,6 @@ input[type="submit"].searchBtn {
547547
z-index: 10001;
548548
}
549549
.groups-page input[type="submit"].searchBtn,
550-
.tags-page input[type="submit"].searchBtn,
551550
.badges-pages input[type="submit"].searchBtn,
552551
.user-profile-page input[type="submit"].searchBtn,
553552
.meta input[type="submit"].searchBtn,

Diff for: askbot/media/style/style.less

-1
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,6 @@ input[type="submit"].searchBtn {
592592
}
593593

594594
.groups-page, /* todo: clean up - should not need this */
595-
.tags-page,
596595
.badges-pages,
597596
.user-profile-page,
598597
.meta,

Diff for: askbot/templates/meta/bottom_scripts.html

+3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
search.setAskButtonEnabled(false);
8585
}
8686
search.decorate(searchInput);
87+
} else if (activeTab === 'tags') {
88+
var search = new TagSearch();
89+
search.decorate(searchInput);
8790
}
8891

8992
if (askbot['data']['userIsAdminOrMod']) {

Diff for: askbot/templates/tags.html

+3-36
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,8 @@
11
{% extends "two_column_body.html" %}
2-
{% import "macros.html" as macros %}
32
<!-- tags.html -->
43
{% block title %}{% spaceless %}{% trans %}Tags{% endtrans %}{% endspaceless %}{% endblock %}
54
{% block content %}
6-
<!-- Tabs -->
7-
{% include "tags/header.html" %}
8-
{% if tag_list_type == 'list' %}
9-
{% if not tags.object_list %}
10-
<span>{% trans %}Nothing found{% endtrans %}</span>
11-
{% endif %}
12-
{% if tags.object_list %}
13-
<div class='clearfix'></div>
14-
<ul class='tags'>
15-
{% for tag in tags.object_list %}
16-
<li>
17-
{{ macros.tag_widget(
18-
tag=tag.name,
19-
html_tag='div',
20-
truncate_long_tag=True,
21-
extra_content='<span class="tag-number">&#215; ' ~
22-
tag.used_count|intcomma ~ '</span>'
23-
)
24-
}}
25-
</li>
26-
{% endfor %}
27-
</ul>
28-
<div class="clean"></div>
29-
<div class="pager">
30-
{{macros.paginator(paginator_context)}}
31-
</div>
32-
{% endif %}
33-
{% else %}
34-
<div class="clearfix"></div>
35-
{% if not tags %}
36-
<span>{% trans %}Nothing found{% endtrans %}</span>
37-
{% endif %}
38-
{{ macros.tag_cloud(tags=tags, font_sizes=font_size, search_state=search_state) }}
39-
{% endif %}
40-
5+
{% include "tags/content.html" %}
416
{% endblock %}
427
{% block endjs %}
438
<script type="text/javascript">
@@ -49,7 +14,9 @@
4914
Hilite.elementid = "searchtags";
5015
Hilite.debug_referrer = location.href;
5116
});
17+
askbot['data']['tagSearchPromptText'] = "{% trans %}search for tags{% endtrans %}";
5218
/*]]>*/
5319
</script>
20+
{% include "tags/custom_javascript.httml" ignore missing %}
5421
{% endblock %}
5522
<!-- end tags.html -->

Diff for: askbot/templates/tags/content.html

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{% import "macros.html" as macros %}
2+
{% include "tags/header.html" %}
3+
{% if tag_list_type == 'list' %}
4+
{% if not tags.object_list %}
5+
<span>{% trans %}Nothing found{% endtrans %}</span>
6+
{% endif %}
7+
{% if tags.object_list %}
8+
<div class='clearfix'></div>
9+
<ul class='tags'>
10+
{% for tag in tags.object_list %}
11+
<li>
12+
{{ macros.tag_widget(
13+
tag=tag.name,
14+
html_tag='div',
15+
truncate_long_tag=True,
16+
extra_content='<span class="tag-number">&#215; ' ~
17+
tag.used_count|intcomma ~ '</span>'
18+
)
19+
}}
20+
</li>
21+
{% endfor %}
22+
</ul>
23+
<div class="clean"></div>
24+
<div class="pager">
25+
{{macros.paginator(paginator_context)}}
26+
</div>
27+
{% endif %}
28+
{% else %}
29+
<div class="clearfix"></div>
30+
{% if not tags %}
31+
<span>{% trans %}Nothing found{% endtrans %}</span>
32+
{% endif %}
33+
{{ macros.tag_cloud(tags=tags, font_sizes=font_size, search_state=search_state) }}
34+
{% endif %}

Diff for: askbot/templates/tags/header.html

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<div id="content-header">
2+
{% set tag_query = stag|escape %}
23
{% if page_title %}
34
<h1 class="section-title">{{ page_title }}</h1>
45
{% else %}
56
{% if stag %}
6-
<h1 class="section-title">{% trans %}Tags, matching "{{ stag }}"{% endtrans %}</h1>
7+
<h1 class="section-title">{% trans %}Tags, matching "{{ tag_query }}"{% endtrans %}</h1>
78
{% else %}
89
<h1 class="section-title">{% trans %}Tags{% endtrans %}</h1>
910
{% endif %}
@@ -13,13 +14,13 @@ <h1 class="section-title">{% trans %}Tags{% endtrans %}</h1>
1314
<span class="label">{% trans %}Sort by &raquo;{% endtrans %}</span>
1415
<a
1516
id="sort_name"
16-
href="{% url tags %}?sort=name"
17+
href="{% url tags %}?sort=name{% if tag_query %}&query={{ tag_query }}{% endif %}"
1718
{% if tab_id == 'name' %}class="on"{% endif %}
1819
title="{% trans %}sorted alphabetically{% endtrans %}"
1920
><span>{% trans %}by name{% endtrans %}</span></a>
2021
<a
2122
id="sort_used"
22-
href="{% url tags %}?sort=used"
23+
href="{% url tags %}?sort=used{% if tag_query %}&query={{ tag_query }}{% endif %}"
2324
{% if tab_id == 'used' %}class="on"{% endif %}
2425
title="{% trans %}sorted by frequency of tag use{% endtrans %}"
2526
><span>{% trans %}by popularity{% endtrans %}</span></a>

Diff for: askbot/templates/widgets/search_bar.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77
{# url action depends on which tab is active #}
88
{% if active_tab == "tags" %}
99
<input type="hidden" name="t" value="tag"/>
10+
{% set query=stag %}
1011
{% elif active_tab == "users" %}
1112
<input type="hidden" name="t" value="user"/>
1213
{% endif %}
1314
<input
1415
class="searchInput"
1516
type="text"
1617
autocomplete="off"
17-
value="{{ query|default_if_none('') }}"
18+
value="{{ query|default_if_none('')|escape }}"
1819
name="query"
1920
id="keywords"
2021
/>

Diff for: askbot/views/readers.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,10 @@ def questions(request, **kwargs):
245245
def tags(request):#view showing a listing of available tags - plain list
246246

247247
#1) Get parameters. This normally belongs to form cleaning.
248-
sortby = request.GET.get('sort', 'used')
248+
post_data = request.GET
249+
sortby = post_data.get('sort', 'used')
249250
try:
250-
page = int(request.GET.get('page', '1'))
251+
page = int(post_data.get('page', '1'))
251252
except ValueError:
252253
page = 1
253254

@@ -256,13 +257,13 @@ def tags(request):#view showing a listing of available tags - plain list
256257
else:
257258
order_by = '-used_count'
258259

259-
query = request.GET.get('query', '').strip()
260+
query = post_data.get('query', '').strip()
260261
tag_list_type = askbot_settings.TAG_LIST_FORMAT
261262

262263
#2) Get query set for the tags.
263264
query_params = {'deleted': False}
264265
if query != '':
265-
query_params['name__icontains': query]
266+
query_params['name__icontains'] = query
266267

267268
tags_qs = Tag.objects.filter(**query_params).exclude(used_count=0)
268269

@@ -307,7 +308,14 @@ def tags(request):#view showing a listing of available tags - plain list
307308

308309
data['tags'] = tags
309310

310-
return render(request, 'tags.html', data)
311+
if request.is_ajax():
312+
template = get_template('tags/content.html')
313+
template_context = RequestContext(request, data)
314+
json_data = {'success': True, 'html': template.render(template_context)}
315+
json_string = simplejson.dumps(json_data)
316+
return HttpResponse(json_string, mimetype='application/json')
317+
else:
318+
return render(request, 'tags.html', data)
311319

312320
@csrf.csrf_protect
313321
def question(request, id):#refactor - long subroutine. display question body, answers and comments

0 commit comments

Comments
 (0)