1 class MultipleSelectFilterWidget {
3 constructor(neighbors, items, initial) {
5 this.graph_neighbors = neighbors;
6 this.filter_items = items;
8 this.dropdown_count = 0;
10 for(let nodeId in this.filter_items) {
11 const node = this.filter_items[nodeId];
12 this.result[node.class] = {}
15 this.make_selection(initial);
18 make_selection( initial_data ){
19 if(!initial_data || jQuery.isEmptyObject(initial_data))
21 for(let item_class in initial_data) {
22 const selected_items = initial_data[item_class];
23 for( let node_id in selected_items ){
24 const node = this.filter_items[node_id];
25 const selection_data = selected_items[node_id]
26 if( selection_data.selected ) {
28 this.markAndSweep(node);
29 this.updateResult(node);
32 this.make_multiple_selection(node, selection_data);
38 make_multiple_selection(node, selection_data){
39 const prepop_data = selection_data.values;
40 for(let k in prepop_data){
41 const div = this.add_item_prepopulate(node, prepop_data[k]);
42 this.updateObjectResult(node, div.id, prepop_data[k]);
47 for(let i in this.filter_items) {
48 const node = this.filter_items[i];
49 node['marked'] = true; //mark all nodes
52 const toCheck = [root];
53 while(toCheck.length > 0){
54 const node = toCheck.pop();
56 continue; //already visited, just continue
58 node['marked'] = false; //mark as visited
59 if(node['follow'] || node == root){ //add neighbors if we want to follow this node
60 const neighbors = this.graph_neighbors[node.id];
61 for(let neighId of neighbors) {
62 const neighbor = this.filter_items[neighId];
63 toCheck.push(neighbor);
68 //now remove all nodes still marked
69 for(let i in this.filter_items){
70 const node = this.filter_items[i];
72 this.disable_node(node);
78 if(node['selected']) {
79 this.markAndSweep(node);
81 else { //TODO: make this not dumb
83 //remember the currently selected, then reset everything and reselect one at a time
84 for(let nodeId in this.filter_items) {
85 node = this.filter_items[nodeId];
86 if(node['selected']) {
91 for(let node of selected) {
93 this.markAndSweep(selected[i]);
99 const elem = document.getElementById(node['id']);
100 node['selected'] = true;
101 elem.classList.remove('disabled_node', 'cleared_node');
102 elem.classList.add('selected_node');
106 const elem = document.getElementById(node['id']);
107 node['selected'] = false;
108 node['selectable'] = true;
109 elem.classList.add('cleared_node')
110 elem.classList.remove('disabled_node', 'selected_node');
114 const elem = document.getElementById(node['id']);
115 node['selected'] = false;
116 node['selectable'] = false;
117 elem.classList.remove('cleared_node', 'selected_node');
118 elem.classList.add('disabled_node');
122 const node = this.filter_items[id];
123 if(!node['selectable'])
126 if(node['multiple']){
127 return this.processClickMultiple(node);
129 return this.processClickSingle(node);
133 processClickSingle(node){
134 node['selected'] = !node['selected']; //toggle on click
135 if(node['selected']) {
141 this.updateResult(node);
144 processClickMultiple(node){
146 const div = this.add_item_prepopulate(node, false);
148 this.updateObjectResult(node, div.id, "");
151 restrictchars(input){
152 if( input.validity.patternMismatch ){
153 input.setCustomValidity("Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed");
154 input.reportValidity();
156 input.value = input.value.replace(/([^A-Za-z0-9-_.])+/g, "");
157 this.checkunique(input);
160 checkunique(tocheck){ //TODO: use set
161 const val = tocheck.value;
162 for( let input of this.inputs ){
163 if( input.value == val && input != tocheck){
164 tocheck.setCustomValidity("All hostnames must be unique");
165 tocheck.reportValidity();
169 tocheck.setCustomValidity("");
172 make_remove_button(div, node){
173 const button = document.createElement("BUTTON");
174 button.type = "button";
175 button.appendChild(document.createTextNode("Remove"));
176 button.classList.add("btn", "btn-danger");
178 button.onclick = function(){ that.remove_dropdown(div.id, node.id); }
182 make_input(div, node, prepopulate){
183 const input = document.createElement("INPUT");
184 input.type = node.form.type;
185 input.name = node.id + node.form.name
186 input.pattern = "(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})";
187 input.title = "Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed"
188 input.placeholder = node.form.placeholder;
189 this.inputs.push(input);
191 input.onchange = function() { that.updateObjectResult(node, div.id, input.value); that.restrictchars(this); };
192 input.oninput = function() { that.restrictchars(this); };
194 input.value = prepopulate;
198 add_item_prepopulate(node, prepopulate){
199 const div = document.createElement("DIV");
200 div.id = "dropdown_" + this.dropdown_count;
201 div.classList.add("dropdown_item");
202 this.dropdown_count++;
203 const label = document.createElement("H5")
204 label.appendChild(document.createTextNode(node['name']))
205 div.appendChild(label);
206 div.appendChild(this.make_input(div, node, prepopulate));
207 div.appendChild(this.make_remove_button(div, node));
208 document.getElementById("dropdown_wrapper").appendChild(div);
212 remove_dropdown(div_id, node_id){
213 const div = document.getElementById(div_id);
214 const node = this.filter_items[node_id]
215 const parent = div.parentNode;
216 div.parentNode.removeChild(div);
217 delete this.result[node.class][node.id]['values'][div.id];
219 //checks if we have removed last item in class
220 if(jQuery.isEmptyObject(this.result[node.class][node.id]['values'])){
221 delete this.result[node.class][node.id];
227 if(!node['multiple']){
228 this.result[node.class][node.id] = {selected: node.selected, id: node.model_id}
230 delete this.result[node.class][node.id];
234 updateObjectResult(node, childKey, childValue){
235 if(!this.result[node.class][node.id])
236 this.result[node.class][node.id] = {selected: true, id: node.model_id, values: {}}
238 this.result[node.class][node.id]['values'][childKey] = childValue;
242 document.getElementById("filter_field").value = JSON.stringify(this.result);