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