1 <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
3 <div class="autocomplete" style="width:400px;">
4 <div id="warning_pane" style="background: #FFFFFF; color: #CC0000;">
5 {% if incompatible == "true" %}
6 <h3>Warning: Incompatible Configuration</h3>
7 <p>Please make a different selection, as the current config conflicts with the selected pod</p>
10 <input id="user_field" name="ignore_this" class="form-control" autocomplete="off" type="text" placeholder="{{placeholder}}" value="" oninput="search(this.value)"
11 {% if disabled %} disabled {% endif %}
15 <input type="hidden" id="selector" name="{{ name }}" class="form-control" style="display: none;"
16 {% if disabled %} disabled {% endif %}
20 <ul id="drop_results"></ul>
25 <div id="added_counter" style="text-align: center; margin: 10px;"><p id="added_number" style="display: inline;">0</p><p style="display: inline;">/
26 {% if selectable_limit > -1 %} {{ selectable_limit }} {% else %} ∞ {% endif %}added</p></div>
36 list-style-type: none;
43 border: solid 1px #ddd;
50 border: 1px solid #ddd;
51 background-color: #f6f6f6;
53 text-decoration: none;
66 border: 1px solid #ddd;
69 vertical-align: middle;
75 #drop_results li a:hover{
76 background-color: #ffffff;
80 display: inline-block;
84 display: inline-block;
90 <script type="text/javascript">
92 var show_from_noentry = {{show_from_noentry|yesno:"true,false"}}; // whether to show any results before user starts typing
93 var show_x_results = {{show_x_results|default:-1}}; // how many results to show at a time, -1 shows all results
94 var results_scrollable = {{results_scrollable|yesno:"true,false"}}; // whether list should be scrollable
95 var selectable_limit = {{selectable_limit|default:-1}}; // how many selections can be made, -1 allows infinitely many
96 var placeholder = "{{placeholder|default:"begin typing"}}"; // placeholder that goes in text box
99 var items = {{items|safe}} // items to add to trie. Type is a dictionary of dictionaries with structure:
103 "id": any, identifiable on backend
104 "small_name": string, displayed first (before separator), searchable (use for e.g. username)
105 "expanded_name": string, displayed second (after separator), searchable (use for e.g. email address)
106 "string": string, not displayed, still searchable
112 {{ selectable_limit }}: changes what number displays for field
113 {{ name }}: form identifiable name, relevant for backend
114 // when submitted, form will contain field data in post with name as the key
115 {{ placeholder }}: "greyed out" contents put into search field initially to guide user as to what they're searching for
116 {{ initial }}: in search_field_init(), marked safe, an array of id's each referring to an id from items
120 var expanded_name_trie = {}
121 expanded_name_trie.isComplete = false;
122 var small_name_trie = {}
123 small_name_trie.isComplete = false;
125 string_trie.isComplete = false;
127 var added_items = [];
131 if( show_from_noentry )
137 var textfield = document.getElementById("user_field");
138 var drop = document.getElementById("drop_results");
140 textfield.disabled = "True";
141 drop.style.display = "none";
143 var btns = document.getElementsByClassName("btn-remove");
144 for( var i = 0; i < btns.length; i++ )
146 btns[i].classList.add("disabled");
150 function search_field_init() {
151 build_all_tries(items);
153 var initial = {{ initial|safe }};
155 for( var i = 0; i < initial.length; i++)
157 select_item(String(initial[i]));
159 if(initial.length == 1)
161 search(items[initial[0]]["small_name"]);
162 document.getElementById("user_field").value = items[initial[0]]["small_name"];
166 function build_all_tries(dict)
174 function add_item(item)
177 add_to_tree(item['expanded_name'], id, expanded_name_trie);
178 add_to_tree(item['small_name'], id, small_name_trie);
179 add_to_tree(item['string'], id, string_trie);
182 function add_to_tree(str, id, trie)
187 if( !inner_trie[str.charAt(0)] )
190 inner_trie[str.charAt(0)] = new_trie;
194 new_trie = inner_trie[str.charAt(0)];
197 if( str.length == 1 )
199 new_trie.isComplete = true;
200 new_trie.itemID = id;
202 inner_trie = new_trie;
203 str = str.substring(1);
207 function search(input)
209 if( input.length == 0 && !show_from_noentry){
213 else if( input.length == 0 && show_from_noentry)
215 dropdown(items); //show all items
220 var tr1 = getSubtree(input, expanded_name_trie);
222 var tr2 = getSubtree(input, small_name_trie);
224 var tr3 = getSubtree(input, string_trie);
226 var results = collate(trees);
231 function getSubtree(input, given_trie)
234 recursive function to return the trie accessed at input
237 if( input.length == 0 ){
242 var substr = input.substring(0, input.length - 1);
243 var last_char = input.charAt(input.length-1);
244 var subtrie = getSubtree(substr, given_trie);
245 if( !subtrie ) //substr not in the trie
249 var indexed_trie = subtrie[last_char];
254 function serialize(trie)
257 takes in a trie and returns a list of its item id's
262 return itemIDs; //empty, base case
264 for( var key in trie )
270 itemIDs = itemIDs.concat(serialize(trie[key]));
272 if ( trie.isComplete )
274 itemIDs.push( trie.itemID );
280 function collate(trees)
283 takes a list of tries
284 returns a list of ids of objects that are available
287 for( var i in trees )
289 var available_IDs = serialize(trees[i]);
290 for( var j=0; j<available_IDs.length; j++){
291 var itemID = available_IDs[j];
292 results[itemID] = items[itemID];
298 function dropdown(ids)
301 takes in a mapping of ids to objects in items
302 and displays them in the dropdown
304 var drop = document.getElementById("drop_results");
305 while(drop.firstChild)
307 drop.removeChild(drop.firstChild);
312 var result_entry = document.createElement("li");
313 var result_button = document.createElement("a");
315 var result_text = document.createTextNode(obj['small_name'] + " : " + obj['expanded_name']);
316 result_button.appendChild(result_text);
317 result_button.setAttribute('onclick', 'select_item("' + obj['id'] + '")');
318 result_entry.appendChild(result_button);
319 drop.appendChild(result_entry);
322 if( !drop.firstChild )
324 drop.style.display = 'none';
328 drop.style.display = 'inherit';
332 function select_item(item_id)
335 var item = items[item_id]['id'];
336 if( (selectable_limit > -1 && added_items.length < selectable_limit) || selectable_limit < 0 )
338 if( added_items.indexOf(item) == -1 )
340 added_items.push(item);
343 update_selected_list();
344 document.getElementById("user_field").focus();
347 function remove_item(item_ref)
349 item = Object.values(items)[item_ref];
350 var index = added_items.indexOf(item);
351 added_items.splice(index, 1);
353 update_selected_list()
354 document.getElementById("user_field").focus();
357 function update_selected_list()
359 document.getElementById("added_number").innerText = added_items.length;
360 selector = document.getElementById('selector');
361 selector.value = JSON.stringify(added_items);
362 added_list = document.getElementById('added_list');
364 while(selector.firstChild)
366 selector.removeChild(selector.firstChild);
368 while(added_list.firstChild)
370 added_list.removeChild(added_list.firstChild);
375 for( var key in added_items )
377 item_id = added_items[key];
378 item = items[item_id];
380 list_html += '<div class="list_entry"><p class="full_name">'
381 + item["expanded_name"]
382 + '</p><p class="small_name">, '
384 + '</p><button onclick="remove_item('
385 + Object.values(items).indexOf(item)
386 + ')" class="btn-remove btn">remove</button>';
387 list_html += '</div>';
390 added_list.innerHTML = list_html;
396 display: inline-block;
399 display: inline-block;