c08fbe5a036e06b2430d55514514832db0dc5a55
[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     var initial_log = {{ initial|safe }};
120
121     var added_template = {{ added_list|default:"{}" }};
122
123     if( default_entry )
124     {
125         var default_entry_div = document.getElementById("default_entry_wrap");
126         default_entry_div.style.display = "inherit";
127
128         var entry_p = document.getElementById("default_text");
129         entry_p.innerText = default_entry;
130     }
131
132     search_field_init();
133
134     if( show_from_noentry )
135     {
136         search("");
137     }
138
139     function disable() {
140         var textfield = document.getElementById("user_field");
141         var drop = document.getElementById("drop_results");
142
143         textfield.disabled = "True";
144         drop.style.display = "none";
145
146         var btns = document.getElementsByClassName("btn-remove");
147         for( var i = 0; i < btns.length; i++ )
148         {
149             btns[i].classList.add("disabled");
150         }
151     }
152
153     function search_field_init() {
154         build_all_tries(items);
155
156         var initial = {{ initial|safe }};
157
158         for( var i = 0; i < initial.length; i++)
159         {
160             select_item(String(initial[i]));
161         }
162         if(initial.length == 1)
163         {
164             search(items[initial[0]]["small_name"]);
165             document.getElementById("user_field").value = items[initial[0]]["small_name"];
166         }
167     }
168
169     function build_all_tries(dict)
170     {
171         for( var i in dict )
172         {
173             add_item(dict[i]);
174         }
175     }
176
177     function add_item(item)
178     {
179         var id = item['id'];
180         add_to_tree(item['expanded_name'], id, expanded_name_trie);
181         add_to_tree(item['small_name'], id, small_name_trie);
182         add_to_tree(item['string'], id, string_trie);
183     }
184
185     function add_to_tree(str, id, trie)
186     {
187         inner_trie = trie;
188         while( str )
189         {
190             if( !inner_trie[str.charAt(0)] )
191             {
192                 new_trie = {};
193                 inner_trie[str.charAt(0)] = new_trie;
194             }
195             else
196             {
197                 new_trie = inner_trie[str.charAt(0)];
198             }
199
200             if( str.length == 1 )
201             {
202                 new_trie.isComplete = true;
203                 new_trie.itemID = id;
204             }
205             inner_trie = new_trie;
206             str = str.substring(1);
207         }
208     }
209
210     function search(input)
211     {
212         if( input.length == 0 && !show_from_noentry){
213             dropdown([]);
214             return;
215         }
216         else if( input.length == 0 && show_from_noentry)
217         {
218             dropdown(items); //show all items
219         }
220         else
221         {
222             var trees = []
223             var tr1 = getSubtree(input, expanded_name_trie);
224             trees.push(tr1);
225             var tr2 = getSubtree(input, small_name_trie);
226             trees.push(tr2);
227             var tr3 = getSubtree(input, string_trie);
228             trees.push(tr3);
229             var results = collate(trees);
230             dropdown(results);
231         }
232     }
233
234     function getSubtree(input, given_trie)
235     {
236         /*
237         recursive function to return the trie accessed at input
238         */
239
240         if( input.length == 0 ){
241             return given_trie;
242         }
243
244         else{
245         var substr = input.substring(0, input.length - 1);
246         var last_char = input.charAt(input.length-1);
247         var subtrie = getSubtree(substr, given_trie);
248         if( !subtrie ) //substr not in the trie
249         {
250             return {};
251         }
252         var indexed_trie = subtrie[last_char];
253         return indexed_trie;
254         }
255     }
256
257     function serialize(trie)
258     {
259         /*
260         takes in a trie and returns a list of its item id's
261         */
262         var itemIDs = [];
263         if ( !trie )
264         {
265             return itemIDs; //empty, base case
266         }
267         for( var key in trie )
268         {
269             if(key.length > 1)
270             {
271                 continue;
272             }
273             itemIDs = itemIDs.concat(serialize(trie[key]));
274         }
275         if ( trie.isComplete )
276         {
277             itemIDs.push( trie.itemID );
278         }
279
280         return itemIDs;
281     }
282
283     function collate(trees)
284     {
285         /*
286         takes a list of tries
287         returns a list of ids of objects that are available
288         */
289         results = [];
290         for( var i in trees )
291         {
292             var available_IDs = serialize(trees[i]);
293             for( var j=0; j<available_IDs.length; j++){
294                 var itemID = available_IDs[j];
295                 results[itemID] = items[itemID];
296             }
297         }
298         return results;
299     }
300
301     function dropdown(ids)
302     {
303         /*
304         takes in a mapping of ids to objects in  items
305         and displays them in the dropdown
306         */
307         var drop = document.getElementById("drop_results");
308         while(drop.firstChild)
309         {
310             drop.removeChild(drop.firstChild);
311         }
312
313         for( var id in ids )
314         {
315             var result_entry = document.createElement("li");
316             var result_button = document.createElement("a");
317             var obj = items[id];
318             var result_text = document.createTextNode(obj['small_name'] + " : " + obj['expanded_name']);
319             result_button.appendChild(result_text);
320             result_button.setAttribute('onclick', 'select_item("' + obj['id'] + '")');
321             result_entry.appendChild(result_button);
322             drop.appendChild(result_entry);
323         }
324
325         if( !drop.firstChild )
326         {
327             drop.style.display = 'none';
328         }
329         else
330         {
331             drop.style.display = 'inherit';
332         }
333     }
334
335     function select_item(item_id)
336     {
337         //TODO make faster
338         var item = items[item_id];
339         if( (selectable_limit > -1 && added_items.length < selectable_limit) || selectable_limit < 0 )
340         {
341             if( added_items.indexOf(item) == -1 )
342             {
343                 added_items.push(item);
344             }
345         }
346         update_selected_list();
347         document.getElementById("user_field").focus();
348     }
349
350     function remove_item(item_ref)
351     {
352         item = Object.values(items)[item_ref];
353         var index = added_items.indexOf(item);
354         added_items.splice(index, 1);
355
356         update_selected_list()
357         document.getElementById("user_field").focus();
358     }
359
360     function edit_item(item_id){
361         var wf_type = "{{wf_type}}";
362         parent.add_edit_wf(wf_type, item_id);
363     }
364
365     function update_selected_list()
366     {
367         document.getElementById("added_number").innerText = added_items.length;
368         selector = document.getElementById('selector');
369         selector.value = JSON.stringify(added_items);
370         added_list = document.getElementById('added_list');
371
372         while(selector.firstChild)
373         {
374             selector.removeChild(selector.firstChild);
375         }
376         while(added_list.firstChild)
377         {
378             added_list.removeChild(added_list.firstChild);
379         }
380
381         list_html = "";
382
383         for( var key in added_items )
384         {
385             item = added_items[key];
386
387             list_html += '<div class="list_entry"><p class="full_name">'
388                 + item["expanded_name"]
389                 + '</p><p class="small_name">, '
390                 + item["small_name"]
391                 + '</p><button onclick="remove_item('
392                 + Object.values(items).indexOf(item)
393                 + ')" class="btn-remove btn">remove</button>';
394             {% if edit %}
395                 list_html += '<button onclick="edit_item('
396                 + item['id']
397                 + ')" class="btn-remove btn">edit</button>';
398             {% endif %}
399                 list_html += '</div>';
400         }
401
402         added_list.innerHTML = list_html;
403     }
404
405 </script>
406 <style>
407     .full_name {
408         display: inline-block;
409     }
410     .small_name {
411         display: inline-block;
412     }
413 </style>