2 .object_class_wrapper {
4 grid-template-columns: 1fr 1fr 1fr;
16 .class_grid_wrapper:last-child {
22 grid-template-columns: 1fr 1fr;
27 border: 1px solid #cccccc;
32 transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s;
33 box-shadow: 0 1px 1px rgba(0,0,0,.075);
37 border-color: #40c640;
38 box-shadow: 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(109, 243, 76, 0.6);
39 transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s;
44 background-color: #EFEFEF;
47 .disabled_node:hover {}
50 background-color: #FFFFFF;
62 border-color: lightgray;
68 grid-template-columns: 1fr 3fr 1fr;
69 justify-items: center;
72 .dropdown_item > button {
81 .dropdown_item > input {
89 grid-template-columns: 4fr 5fr;
93 <input name="filter_field" id="filter_field" type="hidden"/>
94 <div id="grid_wrapper" class="grid_wrapper">
95 {% for object_class, object_list in display_objects %}
96 <div class="class_grid_wrapper">
97 <div style="display:inline-block;margin:auto">
98 <h4>{{object_class}}</h4>
100 <div id="{{object_class}}" class="object_class_wrapper">
101 {% for obj in object_list %}
102 <div id="{{ obj.id|default:'not_provided' }}" class="grid-item">
103 <p class="grid-item-header">{{obj.name}}</p>
104 <p class="grid-item-description">{{obj.description}}</p>
105 <button type="button" class="btn btn-success grid-item-select-btn" onclick="processClick(
106 '{{obj.id}}');">{% if obj.multiple %}Add{% else %}Select{% endif %}</button>
114 <div id="dropdown_wrapper">
118 var initialized = false;
120 var graph_neighbors = {{ neighbors|safe }};
121 var filter_items = {{ filter_items|safe }};
123 var dropdown_count = 0;
125 {% if initial_value %}
127 var initial_value = {{ initial_value|safe }};
130 function make_selection( initial_data ){
132 for(var item_class in initial_data) {
133 var selected_items = initial_data[item_class];
134 for( var node_id in selected_items ){
135 var node = filter_items[node_id];
136 var selection_data = selected_items[node_id]
137 if( selection_data.selected ) {
142 if(node['multiple']){
143 make_multiple_selection(node, selection_data);
149 function make_multiple_selection(node, selection_data){
150 prepop_data = selection_data.values;
151 for(var k in prepop_data){
152 var div = add_item_prepopulate(node, prepop_data[k]);
153 updateObjectResult(node, div.id, prepop_data[k]);
157 make_selection({{initial_value|safe}});
161 function markAndSweep(root){
162 for(var i in filter_items) {
163 node = filter_items[i];
164 node['marked'] = true; //mark all nodes
168 while(toCheck.length > 0){
169 node = toCheck.pop();
170 if(!node['marked']) {
171 //already visited, just continue
174 node['marked'] = false; //mark as visited
175 if(node['follow'] || node == root){ //add neighbors if we want to follow this node
176 var neighbors = graph_neighbors[node.id];
177 for(var i in neighbors) {
178 var neighId = neighbors[i];
179 var neighbor = filter_items[neighId];
180 toCheck.push(neighbor);
185 //now remove all nodes still marked
186 for(var i in filter_items){
187 node = filter_items[i];
194 function process(node) {
195 if(node['selected']) {
198 else { //TODO: make this not dumb
200 //remember the currently selected, then reset everything and reselect one at a time
201 for(var nodeId in filter_items) {
202 node = filter_items[nodeId];
203 if(node['selected']) {
208 for(var i=0; i<selected.length; i++) {
211 markAndSweep(selected[i]);
216 function select(node) {
217 elem = document.getElementById(node['id']);
218 node['selected'] = true;
219 elem.classList.remove('cleared_node');
220 elem.classList.remove('disabled_node');
221 elem.classList.add('selected_node');
224 function clear(node) {
225 elem = document.getElementById(node['id']);
226 node['selected'] = false;
227 node['selectable'] = true;
228 elem.classList.add('cleared_node')
229 elem.classList.remove('disabled_node');
230 elem.classList.remove('selected_node');
233 function disable_node(node) {
234 elem = document.getElementById(node['id']);
235 node['selected'] = false;
236 node['selectable'] = false;
237 elem.classList.remove('cleared_node');
238 elem.classList.add('disabled_node');
239 elem.classList.remove('selected_node');
242 function processClick(id){
244 var node = filter_items[id];
245 if(!node['selectable'])
248 if(node['multiple']){
249 return processClickMultiple(node);
251 return processClickSingle(node);
255 function processClickSingle(node){
256 node['selected'] = !node['selected']; //toggle on click
258 if(node['selected']) {
267 function processClickMultiple(node){
269 var div = add_node(node);
271 updateObjectResult(node, div.id, "");
274 function add_node(node){
275 return add_item_prepopulate(node, false);
278 function restrictchars(input){
279 if( input.validity.patternMismatch ){
280 input.setCustomValidity("Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed");
281 input.reportValidity();
283 input.value = input.value.replace(/([^A-Za-z0-9-_.])+/g, "");
287 function checkunique(tocheck){
289 for( var i = 0; i < inputs.length; i++ )
291 if( inputs[i].value == val && inputs[i] != tocheck)
293 tocheck.setCustomValidity("All hostnames must be unique");
294 tocheck.reportValidity();
298 tocheck.setCustomValidity("");
301 function make_remove_button(div, node){
302 var button = document.createElement("BUTTON");
303 button.type = "button";
304 button.appendChild(document.createTextNode("Remove"));
305 button.classList.add("btn-danger");
306 button.classList.add("btn");
307 button.onclick = function(){
308 remove_dropdown(div.id, node.id);
313 function make_input(div, node, prepopulate){
314 var input = document.createElement("INPUT");
315 input.type = node.form.type;
316 input.name = node.id + node.form.name
317 input.pattern = "(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})";
318 input.title = "Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed"
319 input.placeholder = node.form.placeholder;
321 input.onchange = function() { updateObjectResult(node, div.id, input.value); restrictchars(this); };
322 input.oninput = function() { restrictchars(this); };
324 input.value = prepopulate;
328 function add_item_prepopulate(node, prepopulate){
329 var div = document.createElement("DIV");
330 div.id = "dropdown_" + dropdown_count;
331 div.classList.add("dropdown_item");
333 var label = document.createElement("H5")
334 label.appendChild(document.createTextNode(node['name']))
335 div.appendChild(label);
336 div.appendChild(make_input(div, node, prepopulate));
337 div.appendChild(make_remove_button(div, node));
338 document.getElementById("dropdown_wrapper").appendChild(div);
342 function remove_dropdown(div_id, node_id){
343 var div = document.getElementById(div_id);
344 var node = filter_items[node_id]
345 var parent = div.parentNode;
346 div.parentNode.removeChild(div);
347 delete result[node.class][node.id]['values'][div.id];
349 //checks if we have removed last item in class
350 if(jQuery.isEmptyObject(result[node.class][node.id]['values'])){
351 delete result[node.class][node.id];
355 function updateResult(node){
357 if(!node['multiple']){
358 result[node.class][node.id] = {selected: node.selected, id: node.model_id}
360 delete result[node.class][node.id];
364 function updateObjectResult(node, childKey, childValue){
366 if(!result[node.class][node.id])
367 result[node.class][node.id] = {selected: true, id: node.model_id, values: {}}
369 result[node.class][node.id]['values'][childKey] = childValue;
372 function try_init() {
373 if(initialized) return;
374 for(nodeId in filter_items) {
375 var element = document.getElementById(nodeId);
376 var node = filter_items[nodeId];
377 result[node.class] = {}