Fixing Network Models
[pharos-tools.git] / dashboard / src / templates / dashboard / multiple_select_filter_widget.html
1 <style>
2 .object_class_wrapper {
3     display: grid;
4     grid-template-columns: 1fr 1fr 1fr;
5     border: 0px;
6 }
7 .class_grid_wrapper {
8     border: 0px;
9     border-left: 1px;
10     border-right: 1px;
11     border-style: solid;
12     border-color: grey;
13     text-align: center;
14 }
15 .grid_wrapper {
16     display: grid;
17     grid-template-columns: 1fr 1fr;
18 }
19 .grid-item {
20     cursor: pointer;
21     border:2px;
22     border-style:none;
23     border-color:black;
24     border-radius: 5px;
25     margin:20px;
26     height: 200px;
27     padding: 7px;
28     box-shadow: 0px 0px 7px 0px rgba(0,0,0,0.75);
29     transition-property: box-shadow, background-color;
30     transition-duration: .2s;
31 }
32
33 .grid-item:hover {
34     box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75);
35     transition-property: box-shadow;
36     transition-duration: .2s;
37
38 }
39
40 .selected_node {
41     box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75);
42     background-color: #CCECD7;
43     transition-property: background-color;
44     transition-duration: .2s;
45 }
46
47 .disabled_node {
48     cursor: not-allowed;
49     background-color: #EFEFEF;
50     box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75);
51     transition-property: box-shadow;
52     transition-duration: .2s;
53 }
54
55 .disabled_node:hover {
56     box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75);
57 }
58
59 .cleared_node {
60     background-color: #FFFFFF;
61 }
62
63 .grid-item-header
64 {
65     font-weight: bold;
66     font-size: 20px;
67     margin-top: 10px;
68 }
69
70 </style>
71 <input name="filter_field" id="filter_field" type="hidden"/>
72 <div id="grid_wrapper" class="grid_wrapper">
73 {% for object_class, object_list in filter_objects %}
74 <div class="class_grid_wrapper">
75     <div style="display:inline-block;margin:auto">
76         <h4>{{object_class}}</h4>
77     </div>
78     <div id="{{object_class}}" class="object_class_wrapper">
79     {% for obj in object_list %}
80     <div id="object_parent">
81         <div id="{{ obj.id|default:'not_provided' }}" class="grid-item">
82             <p class="grid-item-header">{{obj.name}}</p>
83             <p class="grid-item-description">{{obj.description}}</p>
84             <button type="button" class="btn btn-success grid-item-select-btn" onclick="processClick('{{obj.id}}', {% if obj.multiple %}true{% else %}false{% endif %});">{% if obj.multiple %}Add{% else %}Select{% endif %}</button>
85         </div>
86         <input type="hidden" name="{{obj.id}}_selected" value="false"/>
87     </div>
88     {% endfor %}
89     </div>
90     </div>
91 {% endfor %}
92 </div>
93
94 <div id="dropdown_wrapper">
95 </div>
96
97 <script>
98 var initialized = false;
99 var mapping = {{ mapping|safe }};
100 var filter_items = {{ filter_items|safe }};
101 var result = {};
102 var selection = {{selection_data|default_if_none:"null"|safe}};
103 var dropdown_count = 0;
104
105 {% if selection_data %}
106 make_selection({{selection_data|safe}});
107 {% endif %}
108
109 function make_selection( selection_data ){
110     if(!initialized) {
111         filter_field_init();
112     }
113     for(var k in selection_data) {
114         selected_items = selection_data[k];
115         for( var selected_item in selected_items ){
116             var node = filter_items[selected_item];
117             if(!node['multiple']){
118                 var input_value = selected_items[selected_item];
119                 if( input_value != 'false' ) {
120                     select(node);
121                     markAndSweep(node);
122                 }
123                 var div = document.getElementById(selected_item)
124                 var inputs = div.parentNode.getElementsByTagName("input")
125                 var input = div.parentNode.getElementsByTagName("input")[0]
126                 input.value = input_value;
127                 updateResult(selected_item);
128             } else {
129                 make_multiple_selection(selected_items, selected_item);
130             }
131         }
132     }
133 }
134
135 function make_multiple_selection(data, item_class){
136     var node = filter_items[item_class];
137     select(node);
138     markAndSweep(node);
139     prepop_data = data[item_class];
140     for(var i=0; i<prepop_data.length; i++){
141         var div = add_item_prepopulate(node, prepop_data[i]);
142         updateObjectResult(div);
143     }
144 }
145
146 function markAndSweep(root){
147     for(var nodeId in filter_items) {
148         node = filter_items[nodeId];
149         node['marked'] = true; //mark all nodes
150         //clears grey background of everything
151     }
152
153     toCheck = [];
154     toCheck.push(root);
155
156     while(toCheck.length > 0){
157         node = toCheck.pop();
158         if(!node['marked']) {
159             //already visited, just continue
160             continue;
161         }
162         node['marked'] = false; //mark as visited
163         if(node['follow'] || node == root){ //add neighbors if we want to follow this node (labs)
164             var mappingId = node.id
165             var neighbors = mapping[mappingId];
166             for(var neighId in neighbors) {
167                 neighId = neighbors[neighId];
168                 var neighbor = filter_items[neighId];
169                 toCheck.push(neighbor);
170             }
171         }
172     }
173
174     //now remove all nodes still marked
175     for(var nodeId in filter_items){
176         node = filter_items[nodeId];
177         if(node['marked']){
178             disable_node(node);
179         }
180     }
181 }
182
183 function process(node) {
184     if(node['selected']) {
185         markAndSweep(node);
186     }
187     else {
188         var selected = []
189         //remember the currently selected, then reset everything and reselect one at a time
190         for(var nodeId in filter_items) {
191             node = filter_items[nodeId];
192             if(node['selected']) {
193                 selected.push(node);
194             }
195             clear(node);
196
197         }
198         for(var i=0; i<selected.length; i++) {
199             node = selected[i];
200             select(node);
201             markAndSweep(selected[i]);
202         }
203     }
204 }
205
206 function select(node) {
207     elem = document.getElementById(node['id']);
208     node['selected'] = true;
209     elem.classList.remove('cleared_node');
210     elem.classList.remove('disabled_node');
211     elem.classList.add('selected_node');
212     var input = elem.parentNode.getElementsByTagName("input")[0];
213     input.disabled = false;
214     input.value = true;
215 }
216
217 function clear(node) {
218     elem = document.getElementById(node['id']);
219     node['selected'] = false;
220     node['selectable'] = true;
221     elem.classList.add('cleared_node')
222     elem.classList.remove('disabled_node');
223     elem.classList.remove('selected_node');
224     elem.parentNode.getElementsByTagName("input")[0].disabled = true;
225 }
226
227 function disable_node(node) {
228     elem = document.getElementById(node['id']);
229     node['selected'] = false;
230     node['selectable'] = false;
231     elem.classList.remove('cleared_node');
232     elem.classList.add('disabled_node');
233     elem.classList.remove('selected_node');
234     elem.parentNode.getElementsByTagName("input")[0].disabled = true;
235 }
236
237 function processClick(id, multiple){
238     if(!initialized){
239         filter_field_init();
240     }
241     var element = document.getElementById(id);
242     var node = filter_items[id];
243     if(!node['selectable']){
244         return;
245     }
246     if(multiple){
247         return processClickMultipleObject(node);
248     }
249     node['selected'] = !node['selected']; //toggle on click
250
251     if(node['selected']) {
252         select(node);
253     }
254     else {
255         clear(node);
256     }
257     process(node);
258     updateResult(id);
259 }
260
261 function processClickMultipleObject(node){
262     select(node);
263     add_node(node);
264     process(node);
265 }
266
267 function add_node(node){
268     return add_item_prepopulate(node, {});
269 }
270
271 inputs = []
272
273 function restrictchars(input)
274 {
275     if( input.validity.patternMismatch )
276     {
277         input.setCustomValidity("Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed");
278         input.reportValidity();
279     }
280
281     input.value = input.value.replace(/([^A-Za-z0-9-_.])+/g, "");
282
283     checkunique(input);
284 }
285
286 function checkunique(tocheck)
287 {
288     val = tocheck.value;
289     for( var i = 0; i < inputs.length; i++ )
290     {
291         if( inputs[i].value == val && inputs[i] != tocheck)
292         {
293             tocheck.setCustomValidity("All hostnames must be unique");
294             tocheck.reportValidity();
295             return;
296         }
297     }
298     tocheck.setCustomValidity("");
299 }
300
301 function add_item_prepopulate(node, prepopulate){
302     inputs = [];
303     var div = document.createElement("DIV");
304     div.class = node['id'];
305     div.id = "dropdown_" + dropdown_count;
306     dropdown_count++;
307     var label = document.createElement("H5");
308     label.style['display'] = 'inline';
309     label.appendChild(document.createTextNode(node['name']));
310     div.appendChild(label);
311     for(var i=0; i<node['forms'].length; i++){
312         form = node['forms'][i];
313         var input = document.createElement("INPUT");
314         input.type = form['type'];
315         input.name = form['name'];
316         input.pattern = "(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})";
317         input.title = "Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed"
318         input.placeholder = form['placeholder'];
319         inputs.push(input);
320         input.onchange = function() { updateObjectResult(div); restrictchars(this); };
321         input.oninput = function() { restrictchars(this); };
322         if(form['name'] in prepopulate){
323             input.value = prepopulate[form['name']];
324         }
325         div.appendChild(input);
326     }
327     //add class id to dropdown object
328     var hiddenInput = document.createElement("INPUT");
329     hiddenInput.type = "hidden";
330     hiddenInput.name = "class";
331     hiddenInput.value = node['id'];
332     div.appendChild(hiddenInput);
333     button = document.createElement("BUTTON");
334     button.onclick = function(){
335         remove_dropdown(div.id);
336     }
337     button.type = "button";
338     button.appendChild(document.createTextNode("Remove"));
339     div.appendChild(button);
340     document.getElementById("dropdown_wrapper").appendChild(div);
341     updateObjectResult(div);
342     return div;
343 }
344
345 function remove_dropdown(id){
346     var div = document.getElementById(id);
347     var parent = div.parentNode;
348     div.parentNode.removeChild(div);
349     //checks if we have removed last item in class
350     var deselect_class = true;
351     var div_inputs = div.getElementsByTagName("input");
352     var div_class = div_inputs[div_inputs.length-1].value;
353     var result_class = document.getElementById(div_class).parentNode.parentNode.id;
354     delete result[result_class][div.id];
355     for(var i=0; i<parent.children.length; i++){
356         var inputs = parent.children[i].getElementsByTagName("input");
357         var object_class = "";
358         for(var k=0; k<inputs.length; k++){
359             if(inputs[k].name == "class"){
360                 object_class = inputs[k].value;
361             }
362         }
363         if(object_class == div_class){
364             deselect_class = false;
365         }
366     }
367     if(deselect_class){
368         clear(filter_items[div_class]);
369     }
370 }
371
372 function updateResult(nodeId){
373     if(!initialized){
374         filter_field_init();
375     }
376     if(!filter_items[nodeId]['multiple']){
377         var node = document.getElementById(nodeId);
378         var value = {}
379         value[nodeId] = node.parentNode.getElementsByTagName("input")[0].value;
380         result[node.parentNode.parentNode.id] = {};
381         result[node.parentNode.parentNode.id][nodeId] = value;
382     }
383 }
384
385 function updateObjectResult(parentElem){
386     node_type = document.getElementById(parentElem.class).parentNode.parentNode.id;
387     input = {};
388     inputs = parentElem.getElementsByTagName("input");
389     for(var i in inputs){
390         var e = inputs[i];
391         input[e.name] = e.value;
392     }
393     result[node_type][parentElem.id] = input;
394 }
395
396 function filter_field_init() {
397     for(nodeId in filter_items) {
398         element = document.getElementById(nodeId);
399         node = filter_items[nodeId];
400         result[element.parentNode.parentNode.id] = {}
401         }
402     initialized = true;
403 }
404
405 </script>