Merge "Improve cleanup of hosts after booking"
[pharos-tools.git] / dashboard / src / templates / dashboard / searchable_select_multiple.html
1 <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
2
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>
8         {% endif %}
9     </div>
10     <input id="user_field" name="ignore_this" class="form-control" autocomplete="off" type="text" placeholder="{{placeholder}}" value="{{initial.name}}" oninput="search(this.value)"
11     {% if disabled %} disabled {% endif %}
12     >
13
14     <input type="hidden" id="selector" name="{{ name }}" class="form-control" style="display: none;"
15     {% if disabled %} disabled {% endif %}
16     >
17     </input>
18
19     <ul id="drop_results"></ul>
20
21
22     <div id="default_entry_wrap" style="display: none;">
23         <div class="list_entry unremovable_list_entry">
24             <p id="default_text" class="full_name"></p>
25             <button class="btn-remove btn disabled">remove</button>
26         </div>
27     </div>
28
29     <div id="added_list">
30
31     </div>
32     <div id="added_counter" style="text-align: center; margin: 10px;"><p id="added_number" style="display: inline;">0</p><p style="display: inline;">/
33         {% if selectable_limit > -1 %} {{ selectable_limit }} {% else %} &infin; {% endif %}added</p></div>
34     <style>
35         #user_field {
36             font-size: 14pt;
37             width: 400px;
38             padding: 5px;
39
40         }
41
42         #drop_results{
43             list-style-type: none;
44             padding: 0;
45             margin: 0;
46             max-height: 300px;
47             min-height: 0;
48             overflow-y: scroll;
49             overflow-x: hidden;
50             border: solid 1px #ddd;
51             display: none;
52
53         }
54
55         #drop_results li a{
56             font-size: 14pt;
57             border: 1px solid #ddd;
58             background-color: #f6f6f6;
59             padding: 12px;
60             text-decoration: none;
61             display: block;
62             width: 400px;
63         }
64
65         .btn-remove {
66             float: right;
67             height: 30px;
68             margin: 4px;
69         }
70
71         .list_entry {
72             width: 400px;
73             border: 1px solid #ddd;
74             border-radius: 3px;
75             margin-top: 5px;
76             vertical-align: middle;
77             line-height: 40px;
78             height: 40px;
79             padding-left: 12px;
80         }
81
82         #drop_results li a:hover{
83             background-color: #ffffff;
84         }
85
86         .small_name {
87             display: inline-block;
88         }
89
90         .full_name {
91             display: inline-block;
92         }
93
94     </style>
95 </div>
96
97 <script type="text/javascript">
98     //flags
99     var show_from_noentry = {{show_from_noentry|default:"false"}};
100     var show_x_results = {{show_x_results|default:-1}};
101     var results_scrollable = {{results_scrollable|default:"false"}};
102     var selectable_limit = {{selectable_limit|default:-1}};
103     var field_name  = "{{name|default:"users"}}";
104     var placeholder = "{{placeholder|default:"begin typing"}}";
105     var default_entry = "{{default_entry}}";
106
107     //needed info
108     var items = {{items|safe}}
109
110     //tries
111     var expanded_name_trie = {}
112     expanded_name_trie.isComplete = false;
113     var small_name_trie = {}
114     small_name_trie.isComplete = false;
115     var string_trie = {}
116     string_trie.isComplete = false;
117
118     var added_items = [];
119
120     var added_template = {{ added_list|default:"{}" }};
121
122     if( default_entry )
123     {
124         var default_entry_div = document.getElementById("default_entry_wrap");
125         default_entry_div.style.display = "inherit";
126
127         var entry_p = document.getElementById("default_text");
128         entry_p.innerText = default_entry;
129     }
130
131     init();
132
133     if( show_from_noentry )
134     {
135         search("");
136     }
137
138     function disable() {
139         var textfield = document.getElementById("user_field");
140         var drop = document.getElementById("drop_results");
141
142         textfield.disabled = "True";
143         drop.style.display = "none";
144
145         var btns = document.getElementsByClassName("btn-remove");
146         for( var i = 0; i < btns.length; i++ )
147         {
148             btns[i].classList.add("disabled");
149         }
150     }
151
152     function init() {
153         build_all_tries(items);
154
155         var initial = {{ initial|safe }};
156
157         for( var i = 0; i < initial.length; i++)
158         {
159             select_item(String(initial[i]));
160         }
161         if(initial.length == 1)
162         {
163             search(items[initial[0]]["small_name"]);
164             document.getElementById("user_field").value = items[initial[0]]["small_name"];
165         }
166     }
167
168     function build_all_tries(dict)
169     {
170         for( var i in dict )
171         {
172             add_item(dict[i]);
173         }
174     }
175
176     function add_item(item)
177     {
178         var id = item['id'];
179         add_to_tree(item['expanded_name'], id, expanded_name_trie);
180         add_to_tree(item['small_name'], id, small_name_trie);
181         add_to_tree(item['string'], id, string_trie);
182     }
183
184     function add_to_tree(str, id, trie)
185     {
186         inner_trie = trie;
187         while( str )
188         {
189             if( !inner_trie[str.charAt(0)] )
190             {
191                 new_trie = {};
192                 inner_trie[str.charAt(0)] = new_trie;
193             }
194             else
195             {
196                 new_trie = inner_trie[str.charAt(0)];
197             }
198
199             if( str.length == 1 )
200             {
201                 new_trie.isComplete = true;
202                 new_trie.itemID = id;
203             }
204             inner_trie = new_trie;
205             str = str.substring(1);
206         }
207     }
208
209     function search(input)
210     {
211         if( input.length == 0 && !show_from_noentry){
212             dropdown([]);
213             return;
214         }
215         else if( input.length == 0 && show_from_noentry)
216         {
217             dropdown(items); //show all items
218         }
219         else
220         {
221             var trees = []
222             var tr1 = getSubtree(input, expanded_name_trie);
223             trees.push(tr1);
224             var tr2 = getSubtree(input, small_name_trie);
225             trees.push(tr2);
226             var tr3 = getSubtree(input, string_trie);
227             trees.push(tr3);
228             var results = collate(trees);
229             dropdown(results);
230         }
231     }
232
233     function getSubtree(input, given_trie)
234     {
235         /*
236         recursive function to return the trie accessed at input
237         */
238
239         if( input.length == 0 ){
240             return given_trie;
241         }
242
243         else{
244         var substr = input.substring(0, input.length - 1);
245         var last_char = input.charAt(input.length-1);
246         var subtrie = getSubtree(substr, given_trie);
247         if( !subtrie ) //substr not in the trie
248         {
249             return {};
250         }
251         var indexed_trie = subtrie[last_char];
252         return indexed_trie;
253         }
254     }
255
256     function serialize(trie)
257     {
258         /*
259         takes in a trie and returns a list of its item id's
260         */
261         var itemIDs = [];
262         if ( !trie )
263         {
264             return itemIDs; //empty, base case
265         }
266         for( var key in trie )
267         {
268             if(key.length > 1)
269             {
270                 continue;
271             }
272             itemIDs = itemIDs.concat(serialize(trie[key]));
273         }
274         if ( trie.isComplete )
275         {
276             itemIDs.push( trie.itemID );
277         }
278
279         return itemIDs;
280     }
281
282     function collate(trees)
283     {
284         /*
285         takes a list of tries
286         returns a list of ids of objects that are available
287         */
288         results = [];
289         for( var i in trees )
290         {
291             var available_IDs = serialize(trees[i]);
292             for( var j=0; j<available_IDs.length; j++){
293                 var itemID = available_IDs[j];
294                 results[itemID] = items[itemID];
295             }
296         }
297         return results;
298     }
299
300     function dropdown(ids)
301     {
302         /*
303         takes in a mapping of ids to objects in  items
304         and displays them in the dropdown
305         */
306         var drop = document.getElementById("drop_results");
307         while(drop.firstChild)
308         {
309             drop.removeChild(drop.firstChild);
310         }
311
312         for( var id in ids )
313         {
314             var result_entry = document.createElement("li");
315             var result_button = document.createElement("a");
316             var obj = items[id];
317             var result_text = document.createTextNode(obj['small_name'] + " : " + obj['expanded_name']);
318             result_button.appendChild(result_text);
319             result_button.setAttribute('onclick', 'select_item("' + obj['id'] + '")');
320             result_entry.appendChild(result_button);
321             drop.appendChild(result_entry);
322         }
323
324         if( !drop.firstChild )
325         {
326             drop.style.display = 'none';
327         }
328         else
329         {
330             drop.style.display = 'inherit';
331         }
332     }
333
334     function select_item(item_id)
335     {
336         //TODO make faster
337         var item = items[item_id];
338         if( (selectable_limit > -1 && added_items.length < selectable_limit) || selectable_limit < 0 )
339         {
340             if( added_items.indexOf(item) == -1 )
341             {
342                 added_items.push(item);
343             }
344         }
345
346         update_selected_list();
347         document.getElementById("user_field").focus();
348     }
349
350     function remove_item(item_ref)
351     {
352
353         item = Object.values(items)[item_ref];
354         var index = added_items.indexOf(item);
355         added_items.splice(index, 1);
356
357         update_selected_list()
358         document.getElementById("user_field").focus();
359     }
360
361     function edit_item(item_id){
362         var wf_type = "{{wf_type}}";
363         parent.add_edit_wf(wf_type, item_id);
364     }
365
366     function update_selected_list()
367     {
368         document.getElementById("added_number").innerText = added_items.length;
369         selector = document.getElementById('selector');
370         selector.value = JSON.stringify(added_items);
371         added_list = document.getElementById('added_list');
372
373         while(selector.firstChild)
374         {
375             selector.removeChild(selector.firstChild);
376         }
377         while(added_list.firstChild)
378         {
379             added_list.removeChild(added_list.firstChild);
380         }
381
382         list_html = "";
383
384         for( var key in added_items )
385         {
386             item = added_items[key];
387
388             list_html += '<div class="list_entry"><p class="full_name">'
389                 + item["expanded_name"]
390                 + '</p><p class="small_name">, '
391                 + item["small_name"]
392                 + '</p><button onclick="remove_item('
393                 + Object.values(items).indexOf(item)
394                 + ')" class="btn-remove btn">remove</button>';
395             {% if edit %}
396                 list_html += '<button onclick="edit_item('
397                 + item['id']
398                 + ')" class="btn-remove btn">edit</button>';
399             {% endif %}
400                 list_html += '</div>';
401         }
402
403         added_list.innerHTML = list_html;
404     }
405
406 </script>
407 <style>
408     .full_name {
409         display: inline-block;
410     }
411     .small_name {
412         display: inline-block;
413     }
414 </style>