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] | ||||
|         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): | ||||
|         ''' | ||||
|         Yield Tags that have no parent. | ||||
|  |  | |||
|  | @ -371,11 +371,9 @@ def get_search_core(): | |||
| def get_search_html(): | ||||
|     search_results = get_search_core() | ||||
|     search_kwargs = search_results['search_kwargs'] | ||||
|     all_tags = common.P.get_all_tag_names() | ||||
|     session = session_manager.get(request) | ||||
|     response = flask.render_template( | ||||
|         'search.html', | ||||
|         all_tags=json.dumps(all_tags), | ||||
|         next_page_url=search_results['next_page_url'], | ||||
|         prev_page_url=search_results['prev_page_url'], | ||||
|         photos=search_results['photos'], | ||||
|  |  | |||
|  | @ -1,7 +1,9 @@ | |||
| import flask; from flask import request | ||||
| import json | ||||
| 
 | ||||
| import etiquette | ||||
| 
 | ||||
| from .. import caching | ||||
| from .. import common | ||||
| from .. import decorators | ||||
| from .. import jsonify | ||||
|  | @ -48,6 +50,14 @@ def post_tag_edit(specific_tag): | |||
| 
 | ||||
| # 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('/tags') | ||||
| @session_manager.give_token | ||||
|  |  | |||
|  | @ -121,3 +121,15 @@ function html_to_element(html) | |||
|     template.innerHTML = html; | ||||
|     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/hotkeys.js"></script> | ||||
|     <script src="/static/js/photoclipboard.js"></script> | ||||
|     <script src="/static/js/tag_autocomplete.js"></script> | ||||
| 
 | ||||
| <style> | ||||
| #content_body | ||||
|  | @ -162,7 +163,7 @@ | |||
|         <h4>Tags</h4> | ||||
|         <ul id="this_tags"> | ||||
|             <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> | ||||
|             </li> | ||||
|             {% set tags = photo.get_tags()|sort_tags %} | ||||
|  | @ -434,6 +435,8 @@ function move_hoverzoom(event) | |||
|     photo_img_holder.style.backgroundPosition=(-x)+"px "+(-y)+"px"; | ||||
| } | ||||
| 
 | ||||
| tag_autocomplete.init_datalist(); | ||||
| 
 | ||||
| setTimeout( | ||||
|     /* | ||||
|     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/hotkeys.js"></script> | ||||
|     <script src="/static/js/photoclipboard.js"></script> | ||||
|     <script src="/static/js/tag_autocomplete.js"></script> | ||||
| 
 | ||||
| <style> | ||||
| form | ||||
|  | @ -171,7 +172,7 @@ form | |||
|                         </li> | ||||
|                     {% endfor %} | ||||
|                 {% 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> | ||||
|         </div> | ||||
|         {% endfor %} | ||||
|  | @ -525,10 +526,9 @@ function tags_on_this_page_hook() | |||
|     */ | ||||
|     var tagname = this.innerHTML.split(/\./); | ||||
|     tagname = tagname[tagname.length-1]; | ||||
|     var qualname = ALL_TAGS[tagname]; | ||||
|     add_searchtag( | ||||
|         input_musts, | ||||
|         qualname, | ||||
|         tagname, | ||||
|         inputted_musts, | ||||
|         "search_builder_musts_inputted" | ||||
|     ); | ||||
|  | @ -546,13 +546,9 @@ function tag_input_hook(box, inputted_list, li_class) | |||
|     if (!box.value) | ||||
|     {return;} | ||||
| 
 | ||||
|     var value = box.value.toLocaleLowerCase(); | ||||
|     value = value.split("."); | ||||
|     value = value[value.length-1]; | ||||
|     value = value.split("+")[0]; | ||||
|     value = value.replace(new RegExp(" ", 'g'), "_"); | ||||
|     value = value.replace(new RegExp("-", 'g'), "_"); | ||||
|     if (ALL_TAGS.indexOf(value) == -1) | ||||
|     var value = box.value; | ||||
|     value = tag_autocomplete.resolve(value); | ||||
|     if (value === null) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | @ -561,8 +557,7 @@ function tag_input_hook(box, inputted_list, li_class) | |||
|     box.value = ""; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| ALL_TAGS = {{all_tags|safe}}; | ||||
| tag_autocomplete.init_datalist(); | ||||
| var input_musts = document.getElementById("search_builder_musts_input"); | ||||
| var input_mays = document.getElementById("search_builder_mays_input"); | ||||
| var input_forbids = document.getElementById("search_builder_forbids_input"); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue