Add tag_autocomplete.js.
Instead of embedding the entire tag list in the search.html template every single time, this script loads the tags from the new, cache-enabled endpoint /all_tags.json. Then we can use html5 datalists to create autocomplete forms on the search and photo pages.
This commit is contained in:
parent
bbf07f4401
commit
8a12a24e8e
7 changed files with 121 additions and 15 deletions
|
@ -792,6 +792,15 @@ class PDBTagMixin:
|
||||||
names = [row[0] for row in rows]
|
names = [row[0] for row in rows]
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
def get_all_synonyms(self):
|
||||||
|
'''
|
||||||
|
Return a dict mapping {synonym: mastertag} as strings.
|
||||||
|
'''
|
||||||
|
query = 'SELECT name, mastername FROM tag_synonyms'
|
||||||
|
rows = self.sql_select(query)
|
||||||
|
synonyms = {syn: tag for (syn, tag) in rows}
|
||||||
|
return synonyms
|
||||||
|
|
||||||
def get_root_tags(self):
|
def get_root_tags(self):
|
||||||
'''
|
'''
|
||||||
Yield Tags that have no parent.
|
Yield Tags that have no parent.
|
||||||
|
|
|
@ -371,11 +371,9 @@ def get_search_core():
|
||||||
def get_search_html():
|
def get_search_html():
|
||||||
search_results = get_search_core()
|
search_results = get_search_core()
|
||||||
search_kwargs = search_results['search_kwargs']
|
search_kwargs = search_results['search_kwargs']
|
||||||
all_tags = common.P.get_all_tag_names()
|
|
||||||
session = session_manager.get(request)
|
session = session_manager.get(request)
|
||||||
response = flask.render_template(
|
response = flask.render_template(
|
||||||
'search.html',
|
'search.html',
|
||||||
all_tags=json.dumps(all_tags),
|
|
||||||
next_page_url=search_results['next_page_url'],
|
next_page_url=search_results['next_page_url'],
|
||||||
prev_page_url=search_results['prev_page_url'],
|
prev_page_url=search_results['prev_page_url'],
|
||||||
photos=search_results['photos'],
|
photos=search_results['photos'],
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import flask; from flask import request
|
import flask; from flask import request
|
||||||
|
import json
|
||||||
|
|
||||||
import etiquette
|
import etiquette
|
||||||
|
|
||||||
|
from .. import caching
|
||||||
from .. import common
|
from .. import common
|
||||||
from .. import decorators
|
from .. import decorators
|
||||||
from .. import jsonify
|
from .. import jsonify
|
||||||
|
@ -48,6 +50,14 @@ def post_tag_edit(specific_tag):
|
||||||
|
|
||||||
# Tag listings #####################################################################################
|
# Tag listings #####################################################################################
|
||||||
|
|
||||||
|
@site.route('/all_tags.json')
|
||||||
|
@caching.cached_endpoint(max_age=0)
|
||||||
|
def get_all_tag_names():
|
||||||
|
all_tags = common.P.get_all_tag_names()
|
||||||
|
all_synonyms = common.P.get_all_synonyms()
|
||||||
|
response = {'tags': all_tags, 'synonyms': all_synonyms}
|
||||||
|
return json.dumps(response)
|
||||||
|
|
||||||
@site.route('/tag/<specific_tag_name>')
|
@site.route('/tag/<specific_tag_name>')
|
||||||
@site.route('/tags')
|
@site.route('/tags')
|
||||||
@session_manager.give_token
|
@session_manager.give_token
|
||||||
|
|
|
@ -121,3 +121,15 @@ function html_to_element(html)
|
||||||
template.innerHTML = html;
|
template.innerHTML = html;
|
||||||
return template.content.firstChild;
|
return template.content.firstChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalize_tagname(tagname)
|
||||||
|
{
|
||||||
|
tagname = tagname.trim();
|
||||||
|
tagname = tagname.toLocaleLowerCase();
|
||||||
|
tagname = tagname.split(".");
|
||||||
|
tagname = tagname[tagname.length-1];
|
||||||
|
tagname = tagname.split("+")[0];
|
||||||
|
tagname = tagname.replace(new RegExp(" ", 'g'), "_");
|
||||||
|
tagname = tagname.replace(new RegExp("-", 'g'), "_");
|
||||||
|
return tagname;
|
||||||
|
}
|
||||||
|
|
79
frontends/etiquette_flask/static/js/tag_autocomplete.js
Normal file
79
frontends/etiquette_flask/static/js/tag_autocomplete.js
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
var tag_autocomplete = {};
|
||||||
|
|
||||||
|
tag_autocomplete.tagset = {"tags": [], "synonyms": {}};
|
||||||
|
|
||||||
|
tag_autocomplete.DATALIST_ID = "tag_autocomplete_datalist"
|
||||||
|
tag_autocomplete.init_datalist =
|
||||||
|
function init_datalist()
|
||||||
|
{
|
||||||
|
var datalist;
|
||||||
|
datalist = document.getElementById(tag_autocomplete.DATALIST_ID);
|
||||||
|
if (!datalist)
|
||||||
|
{
|
||||||
|
var datalist = document.createElement("datalist");
|
||||||
|
datalist.id = tag_autocomplete.DATALIST_ID;
|
||||||
|
document.body.appendChild(datalist);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_all_children(datalist);
|
||||||
|
for (var index = 0; index < tag_autocomplete.tagset["tags"].length; index += 1)
|
||||||
|
{
|
||||||
|
var option = document.createElement("option");
|
||||||
|
option.value = tag_autocomplete.tagset["tags"][index];
|
||||||
|
datalist.appendChild(option);
|
||||||
|
}
|
||||||
|
for (var synonym in tag_autocomplete.tagset["synonyms"])
|
||||||
|
{
|
||||||
|
var option = document.createElement("option");
|
||||||
|
option.value = tag_autocomplete.tagset["synonyms"][synonym] + "+" + synonym;
|
||||||
|
datalist.appendChild(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_autocomplete.resolve =
|
||||||
|
function resolve(tagname)
|
||||||
|
{
|
||||||
|
tagname = normalize_tagname(tagname);
|
||||||
|
if (tag_autocomplete.tagset["tags"].indexOf(tagname) != -1)
|
||||||
|
{
|
||||||
|
return tagname;
|
||||||
|
}
|
||||||
|
if (tagname in tag_autocomplete.tagset["synonyms"])
|
||||||
|
{
|
||||||
|
return tag_autocomplete.tagset["synonyms"][tagname];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_autocomplete.update_tagset_callback =
|
||||||
|
function update_tagset_callback(response)
|
||||||
|
{
|
||||||
|
if (response["meta"]["status"] == 304)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (response["meta"]["status"] == 200)
|
||||||
|
{
|
||||||
|
tag_autocomplete.tagset = response["data"];
|
||||||
|
if (document.getElementById(tag_autocomplete.DATALIST_ID))
|
||||||
|
{
|
||||||
|
tag_autocomplete.init_datalist();
|
||||||
|
}
|
||||||
|
return tag_autocomplete.tagset;
|
||||||
|
}
|
||||||
|
console.error(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_autocomplete.update_tagset =
|
||||||
|
function update_tagset()
|
||||||
|
{
|
||||||
|
console.log("Updating known tagset.");
|
||||||
|
var url = "/all_tags.json";
|
||||||
|
get(url, tag_autocomplete.update_tagset_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function on_pageload()
|
||||||
|
{
|
||||||
|
tag_autocomplete.update_tagset();
|
||||||
|
}
|
||||||
|
document.addEventListener("DOMContentLoaded", on_pageload);
|
|
@ -10,6 +10,7 @@
|
||||||
<script src="/static/js/common.js"></script>
|
<script src="/static/js/common.js"></script>
|
||||||
<script src="/static/js/hotkeys.js"></script>
|
<script src="/static/js/hotkeys.js"></script>
|
||||||
<script src="/static/js/photoclipboard.js"></script>
|
<script src="/static/js/photoclipboard.js"></script>
|
||||||
|
<script src="/static/js/tag_autocomplete.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#content_body
|
#content_body
|
||||||
|
@ -162,7 +163,7 @@
|
||||||
<h4>Tags</h4>
|
<h4>Tags</h4>
|
||||||
<ul id="this_tags">
|
<ul id="this_tags">
|
||||||
<li>
|
<li>
|
||||||
<input id="add_tag_textbox" type="text" autofocus>
|
<input id="add_tag_textbox" type="text" list="tag_autocomplete_datalist" autofocus>
|
||||||
<button id="add_tag_button" class="green_button" onclick="submit_tag(receive_callback);">add</button>
|
<button id="add_tag_button" class="green_button" onclick="submit_tag(receive_callback);">add</button>
|
||||||
</li>
|
</li>
|
||||||
{% set tags = photo.get_tags()|sort_tags %}
|
{% set tags = photo.get_tags()|sort_tags %}
|
||||||
|
@ -434,6 +435,8 @@ function move_hoverzoom(event)
|
||||||
photo_img_holder.style.backgroundPosition=(-x)+"px "+(-y)+"px";
|
photo_img_holder.style.backgroundPosition=(-x)+"px "+(-y)+"px";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tag_autocomplete.init_datalist();
|
||||||
|
|
||||||
setTimeout(
|
setTimeout(
|
||||||
/*
|
/*
|
||||||
When the screen is in column mode, the autofocusing of the tag box snaps the
|
When the screen is in column mode, the autofocusing of the tag box snaps the
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
<script src="/static/js/common.js"></script>
|
<script src="/static/js/common.js"></script>
|
||||||
<script src="/static/js/hotkeys.js"></script>
|
<script src="/static/js/hotkeys.js"></script>
|
||||||
<script src="/static/js/photoclipboard.js"></script>
|
<script src="/static/js/photoclipboard.js"></script>
|
||||||
|
<script src="/static/js/tag_autocomplete.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
form
|
form
|
||||||
|
@ -171,7 +172,7 @@ form
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><input id="search_builder_{{tagtype}}_input" type="text"></li>
|
<li><input id="search_builder_{{tagtype}}_input" type="text" list="tag_autocomplete_datalist"></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -525,10 +526,9 @@ function tags_on_this_page_hook()
|
||||||
*/
|
*/
|
||||||
var tagname = this.innerHTML.split(/\./);
|
var tagname = this.innerHTML.split(/\./);
|
||||||
tagname = tagname[tagname.length-1];
|
tagname = tagname[tagname.length-1];
|
||||||
var qualname = ALL_TAGS[tagname];
|
|
||||||
add_searchtag(
|
add_searchtag(
|
||||||
input_musts,
|
input_musts,
|
||||||
qualname,
|
tagname,
|
||||||
inputted_musts,
|
inputted_musts,
|
||||||
"search_builder_musts_inputted"
|
"search_builder_musts_inputted"
|
||||||
);
|
);
|
||||||
|
@ -546,13 +546,9 @@ function tag_input_hook(box, inputted_list, li_class)
|
||||||
if (!box.value)
|
if (!box.value)
|
||||||
{return;}
|
{return;}
|
||||||
|
|
||||||
var value = box.value.toLocaleLowerCase();
|
var value = box.value;
|
||||||
value = value.split(".");
|
value = tag_autocomplete.resolve(value);
|
||||||
value = value[value.length-1];
|
if (value === null)
|
||||||
value = value.split("+")[0];
|
|
||||||
value = value.replace(new RegExp(" ", 'g'), "_");
|
|
||||||
value = value.replace(new RegExp("-", 'g'), "_");
|
|
||||||
if (ALL_TAGS.indexOf(value) == -1)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -561,8 +557,7 @@ function tag_input_hook(box, inputted_list, li_class)
|
||||||
box.value = "";
|
box.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tag_autocomplete.init_datalist();
|
||||||
ALL_TAGS = {{all_tags|safe}};
|
|
||||||
var input_musts = document.getElementById("search_builder_musts_input");
|
var input_musts = document.getElementById("search_builder_musts_input");
|
||||||
var input_mays = document.getElementById("search_builder_mays_input");
|
var input_mays = document.getElementById("search_builder_mays_input");
|
||||||
var input_forbids = document.getElementById("search_builder_forbids_input");
|
var input_forbids = document.getElementById("search_builder_forbids_input");
|
||||||
|
|
Loading…
Reference in a new issue