summaryrefslogtreecommitdiffstats
path: root/dashboard/src/templates
diff options
context:
space:
mode:
authorParker Berberian <pberberian@iol.unh.edu>2019-06-18 14:58:27 -0400
committerParker Berberian <pberberian@iol.unh.edu>2019-06-21 10:56:34 -0400
commit936c7ca07f96ea183cbe6c33b8af29a9ed6e0e31 (patch)
treee9f5740355c9682042c922bfc97f0f5c953320a5 /dashboard/src/templates
parent6a504fd180908365578369a6f2692e4524f31908 (diff)
Redesigns Multiple Select Filter Widget
Makes the filter widget work as it should so that it can be integrated with the rest of the Django form handling nicely. Also fixes a lot of ugly code tangential to the widget. Change-Id: Ib92db8e584f3d2162c6c43a18b75a57273bb18f5 Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
Diffstat (limited to 'dashboard/src/templates')
-rw-r--r--dashboard/src/templates/booking/quick_deploy.html121
-rw-r--r--dashboard/src/templates/dashboard/multiple_select_filter_widget.html353
-rw-r--r--dashboard/src/templates/resource/steps/define_hardware.html2
3 files changed, 181 insertions, 295 deletions
diff --git a/dashboard/src/templates/booking/quick_deploy.html b/dashboard/src/templates/booking/quick_deploy.html
index 2fbd035..8cf8481 100644
--- a/dashboard/src/templates/booking/quick_deploy.html
+++ b/dashboard/src/templates/booking/quick_deploy.html
@@ -79,77 +79,57 @@
</div>
</div>
<script type="text/javascript">
- var normalize = function(data)
- {
- //converts the top level keys in data to map to lists
- var normalized = {}
- for( var key in data ){
- normalized[key] = [];
- for( var subkey in data[key] ){
- normalized[key].push(data[key][subkey]);
- }
- }
- return normalized;
- }
- var update_page_contents = function(response)
- {
- document.open();
- document.write(response);
- document.close();
- }
- //form hamdler code
- submit_form = function()
+ function submit_form()
{
- //altered from initial prototype: form submits automatically,
- //but needs formatting for multiple select field
- var data = normalize(result);
- data = JSON.stringify(data);
- document.getElementById("filter_field").value = data;
+ //formats data for form submission
+ document.getElementById("filter_field").value = JSON.stringify(result);
}
- var sup_image_dict = {{ image_filter|safe }};
- var sup_installer_dict = {{ installer_filter|safe }};
- var sup_scenario_dict = {{ scenario_filter|safe }};
-
- function imageHider() {
- var data = normalize(result);
- var drop = document.getElementById("id_image");
+ function hide_dropdown(drop_id) {
+ var drop = document.getElementById(drop_id);
+ //select 'blank' option
for( var i=0; i < drop.length; i++ )
{
if ( drop.options[i].text == '---------' )
- {
drop.selectedIndex = i;
- }
}
+ //cross browser hide children
$('#id_image').children().hide();
-
for( var i = 0; i < drop.childNodes.length; i++ )
{
drop.childNodes[i].disabled = true; // closest we can get on safari to hiding it outright
}
+ }
+
+ function get_selected_value(key){
+ for( var attr in result[key] ){
+ if( attr in {} )
+ continue;
+ else
+ return attr;
+ }
+ return null;
+ }
+
+ var sup_image_dict = {{ image_filter|safe }};
+ var sup_installer_dict = {{ installer_filter|safe }};
+ var sup_scenario_dict = {{ scenario_filter|safe }};
+
+ function imageHider() {
+ var drop = document.getElementById("id_image");
+ hide_dropdown("id_image");
- var empty_map = {}
+ var lab_pk = get_selected_value("lab");
+ var host_pk = get_selected_value("host");
for ( var i=0; i < drop.childNodes.length; i++ )
{
var image_object = sup_image_dict[drop.childNodes[i].value];
if( image_object ) //weed out empty option
{
- var lab_pk = ""
- for( var j in data["labs"][0] )
- {
- if( j in {} ) { continue; }
- else { lab_pk = j; break; }
- }
- var host_pk = "";
- for( var j in data["hosts"][0] )
- {
- if( j in {} ) { continue; }
- else { host_pk = j; break; }
- }
if( image_object.host_profile == host_pk && image_object.lab == lab_pk )
{
drop.childNodes[i].style.display = "inherit";
@@ -180,28 +160,15 @@
document.getElementById('id_installer').addEventListener('change', scenarioHider);
function dropFilter(target, target_filter, master) {
- ob = document.getElementById(target);
+ var dropdown = document.getElementById(target);
- for(var i=0; i<ob.options.length; i++) {
- if ( ob.options[i].text == '---------' ) {
- ob.selectedIndex = i;
- }
- }
+ hide_dropdown(target);
- targ_id = "#" + target;
-
- $(targ_id).children().hide();
-
- for (var i = 0; i < document.getElementById(target).childNodes.length; i++)
- {
- document.getElementById(target).childNodes[i].disabled = true;
- }
var drop = document.getElementById(master);
var opts = target_filter[drop.options[drop.selectedIndex].value];
if (!opts) {
opts = {};
}
- var emptyMap = {}
var map = Object.create(null);
for (var i = 0; i < opts.length; i++) {
@@ -209,34 +176,14 @@
map[j] = true;
}
- for (var i = 0; i < document.getElementById(target).childNodes.length; i++) {
- if (document.getElementById(target).childNodes[i].value in opts && !(document.getElementById(target).childNodes[i].value in emptyMap) ) {
- document.getElementById(target).childNodes[i].style.display = "inherit";
- document.getElementById(target).childNodes[i].disabled = false;
+ for (var i = 0; i < dropdown.childNodes.length; i++) {
+ if (dropdown.childNodes[i].value in opts && !(dropdown.childNodes[i].value in {}) ) {
+ dropdown.childNodes[i].style.display = "inherit";
+ dropdown.childNodes[i].disabled = false;
}
}
}
</script>
<button id="quick_booking_confirm" onclick="submit_form();" class="btn btn-success">Confirm</button>
</form>
-<script>
- //context vars
- var prefill_host_selection = "{{host_select_field_prefill_data|default:""|safe}}";
- var prefill_purpose = "{{prefill_purpose|default:""|safe}}";
- var prefill_project = "{{prefill_project|default:""|safe}}";
- var prefill_hostname = "{{prefill_hostname|default:""|safe}}";
-
- //to handle prefill
- function prefill_host_select_field(data)
- {
- //
- if(data)
- {
- make_selection(data);
- }
- }
-
- //call init functions
- prefill_host_select_field(prefill_host_selection);
-</script>
{% endblock %}
diff --git a/dashboard/src/templates/dashboard/multiple_select_filter_widget.html b/dashboard/src/templates/dashboard/multiple_select_filter_widget.html
index 536fdcc..3a7e148 100644
--- a/dashboard/src/templates/dashboard/multiple_select_filter_widget.html
+++ b/dashboard/src/templates/dashboard/multiple_select_filter_widget.html
@@ -4,6 +4,7 @@
grid-template-columns: 1fr 1fr 1fr;
border: 0px;
}
+
.class_grid_wrapper {
border: 0px;
text-align: center;
@@ -20,96 +21,78 @@
display: grid;
grid-template-columns: 1fr 1fr;
}
+
.grid-item {
cursor: pointer;
- border:1px solid #cccccc;
+ border: 1px solid #cccccc;
border-radius: 5px;
- margin:20px;
+ margin: 20px;
height: 200px;
padding: 7px;
- transition-property: box-shadow, background-color;
- transition-duration: .2s;
-}
-
-.grid-item:hover {
- box-shadow: 0px 0px 7px 0px rgba(0,0,0,0.45);
- transition-property: box-shadow;
- transition-duration: .2s;
-
+ transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s;
+ box-shadow: 0 1px 1px rgba(0,0,0,.075);
}
.selected_node {
- box-shadow: 0px 0px 4px 0px rgba(0,0,0,0.45);
- background-color: #fff;
- transition-property: background-color;
- transition-duration: .2s;
+ border-color: #40c640;
+ box-shadow: 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(109, 243, 76, 0.6);
+ transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s;
}
.disabled_node {
cursor: not-allowed;
background-color: #EFEFEF;
- transition-property: box-shadow;
- transition-duration: .2s;
- border: 1px solid #ccc;
}
-.disabled_node:hover {
-}
+.disabled_node:hover {}
.cleared_node {
background-color: #FFFFFF;
}
-.grid-item-header
-{
+.grid-item-header {
font-weight: bold;
font-size: 20px;
margin-top: 10px;
}
-#dropdown_wrapper > div > h5 {
- margin: 12px;
- display: inline-block;
- vertical-align: middle;
+.dropdown_item {
+ border: 1px;
+ border-style: solid;
+ border-color: lightgray;
+ border-radius: 5px;
+ margin: 20px;
+ padding: 2px;
+ grid-column: 1;
+ display: grid;
+ grid-template-columns: 1fr 3fr 1fr;
+ justify-items: center;
}
-#dropdown_wrapper > div > button {
- padding: 7px;
+.dropdown_item > button {
margin: 2px;
- float: right;
- width: 80px;
+ justify-self: end;
}
-#dropdown_wrapper > div > input {
- padding: 7px;
- margin: 2px;
- float: right;
- width: 300px;
- width: calc(100% - 240px);
+
+.dropdown_item > h5 {
+ margin: auto;
}
-#dropdown_wrapper > div {
- border:2px;
- border-style:none;
- border-color:black;
- border-radius: 5px;
- margin:20px;
- padding: 2px;
- box-shadow: 0px 0px 7px 0px rgba(0,0,0,0.75);
- transition-property: box-shadow, background-color;
- transition-duration: .2s;
- display: inline-block;
- vertical-align: middle;
+.dropdown_item > input {
+ padding: 7px;
+ margin: 2px;
+ width: 90%;
}
#dropdown_wrapper {
display: grid;
- grid-template-columns: 3fr 5fr;
+ grid-template-columns: 4fr 5fr;
}
-
</style>
+
<input name="filter_field" id="filter_field" type="hidden"/>
<div id="grid_wrapper" class="grid_wrapper">
-{% for object_class, object_list in filter_objects %}
+{% for object_class, object_list in display_objects %}
<div class="class_grid_wrapper">
<div style="display:inline-block;margin:auto">
<h4>{{object_class}}</h4>
@@ -120,12 +103,8 @@
<p class="grid-item-header">{{obj.name}}</p>
<p class="grid-item-description">{{obj.description}}</p>
<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>
+ '{{obj.id}}');">{% if obj.multiple %}Add{% else %}Select{% endif %}</button>
</div>
- <input type="hidden" name="{{obj.id}}_selected" value="false"/>
{% endfor %}
</div>
</div>
@@ -137,62 +116,55 @@
<script>
var initialized = false;
-var mapping = {{ mapping|safe }};
+var inputs = [];
+var graph_neighbors = {{ neighbors|safe }};
var filter_items = {{ filter_items|safe }};
var result = {};
-var selection = {{selection_data|default_if_none:"null"|safe}};
var dropdown_count = 0;
-{% if selection_data %}
-make_selection({{selection_data|safe}});
-{% endif %}
+{% if initial_value %}
-function make_selection( selection_data ){
- if(!initialized) {
- filter_field_init();
- }
- for(var k in selection_data) {
- selected_items = selection_data[k];
- for( var selected_item in selected_items ){
- var node = filter_items[selected_item];
- if(!node['multiple']){
- var input_value = selected_items[selected_item];
- if( input_value != 'false' ) {
- select(node);
- markAndSweep(node);
- }
- var div = document.getElementById(selected_item)
- var inputs = div.parentNode.getElementsByTagName("input")
- var input = div.parentNode.getElementsByTagName("input")[0]
- input.value = input_value;
- updateResult(selected_item);
- } else {
- make_multiple_selection(selected_items, selected_item);
+var initial_value = {{ initial_value|safe }};
+
+
+function make_selection( initial_data ){
+ try_init();
+ for(var item_class in initial_data) {
+ var selected_items = initial_data[item_class];
+ for( var node_id in selected_items ){
+ var node = filter_items[node_id];
+ var selection_data = selected_items[node_id]
+ if( selection_data.selected ) {
+ select(node);
+ markAndSweep(node);
+ updateResult(node);
+ }
+ if(node['multiple']){
+ make_multiple_selection(node, selection_data);
}
}
}
}
-function make_multiple_selection(data, item_class){
- var node = filter_items[item_class];
- select(node);
- markAndSweep(node);
- prepop_data = data[item_class];
- for(var i=0; i<prepop_data.length; i++){
- var div = add_item_prepopulate(node, prepop_data[i]);
- updateObjectResult(div);
+function make_multiple_selection(node, selection_data){
+ prepop_data = selection_data.values;
+ for(var k in prepop_data){
+ var div = add_item_prepopulate(node, prepop_data[k]);
+ updateObjectResult(node, div.id, prepop_data[k]);
}
}
+make_selection({{initial_value|safe}});
+
+{% endif %}
+
function markAndSweep(root){
- for(var nodeId in filter_items) {
- node = filter_items[nodeId];
+ for(var i in filter_items) {
+ node = filter_items[i];
node['marked'] = true; //mark all nodes
- //clears grey background of everything
}
toCheck = [root];
-
while(toCheck.length > 0){
node = toCheck.pop();
if(!node['marked']) {
@@ -200,11 +172,10 @@ function markAndSweep(root){
continue;
}
node['marked'] = false; //mark as visited
- if(node['follow'] || node == root){ //add neighbors if we want to follow this node (labs)
- var mappingId = node.id
- var neighbors = mapping[mappingId];
- for(var neighId in neighbors) {
- neighId = neighbors[neighId];
+ if(node['follow'] || node == root){ //add neighbors if we want to follow this node
+ var neighbors = graph_neighbors[node.id];
+ for(var i in neighbors) {
+ var neighId = neighbors[i];
var neighbor = filter_items[neighId];
toCheck.push(neighbor);
}
@@ -212,8 +183,8 @@ function markAndSweep(root){
}
//now remove all nodes still marked
- for(var nodeId in filter_items){
- node = filter_items[nodeId];
+ for(var i in filter_items){
+ node = filter_items[i];
if(node['marked']){
disable_node(node);
}
@@ -224,7 +195,7 @@ function process(node) {
if(node['selected']) {
markAndSweep(node);
}
- else {
+ else { //TODO: make this not dumb
var selected = []
//remember the currently selected, then reset everything and reselect one at a time
for(var nodeId in filter_items) {
@@ -233,7 +204,6 @@ function process(node) {
selected.push(node);
}
clear(node);
-
}
for(var i=0; i<selected.length; i++) {
node = selected[i];
@@ -249,9 +219,6 @@ function select(node) {
elem.classList.remove('cleared_node');
elem.classList.remove('disabled_node');
elem.classList.add('selected_node');
- var input = elem.parentNode.getElementsByTagName("input")[0];
- input.disabled = false;
- input.value = true;
}
function clear(node) {
@@ -261,7 +228,6 @@ function clear(node) {
elem.classList.add('cleared_node')
elem.classList.remove('disabled_node');
elem.classList.remove('selected_node');
- elem.parentNode.getElementsByTagName("input")[0].disabled = true;
}
function disable_node(node) {
@@ -271,21 +237,22 @@ function disable_node(node) {
elem.classList.remove('cleared_node');
elem.classList.add('disabled_node');
elem.classList.remove('selected_node');
- elem.parentNode.getElementsByTagName("input")[0].disabled = true;
}
-function processClick(id, multiple){
- if(!initialized){
- filter_field_init();
- }
- var element = document.getElementById(id);
+function processClick(id){
+ try_init();
var node = filter_items[id];
- if(!node['selectable']){
+ if(!node['selectable'])
return;
+
+ if(node['multiple']){
+ return processClickMultiple(node);
+ } else {
+ return processClickSingle(node);
}
- if(multiple){
- return processClickMultipleObject(node);
- }
+}
+
+function processClickSingle(node){
node['selected'] = !node['selected']; //toggle on click
if(node['selected']) {
@@ -294,21 +261,20 @@ function processClick(id, multiple){
clear(node);
}
process(node);
- updateResult(id);
+ updateResult(node);
}
-function processClickMultipleObject(node){
+function processClickMultiple(node){
select(node);
- add_node(node);
+ var div = add_node(node);
process(node);
+ updateObjectResult(node, div.id, "");
}
function add_node(node){
- return add_item_prepopulate(node, {});
+ return add_item_prepopulate(node, false);
}
-inputs = []
-
function restrictchars(input){
if( input.validity.patternMismatch ){
input.setCustomValidity("Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed");
@@ -318,8 +284,7 @@ function restrictchars(input){
checkunique(input);
}
-function checkunique(tocheck)
-{
+function checkunique(tocheck){
val = tocheck.value;
for( var i = 0; i < inputs.length; i++ )
{
@@ -333,109 +298,83 @@ function checkunique(tocheck)
tocheck.setCustomValidity("");
}
-function add_item_prepopulate(node, prepopulate){
- inputs = [];
- var div = document.createElement("DIV");
- div.class = node['id'];
- div.id = "dropdown_" + dropdown_count;
- dropdown_count++;
- var label = document.createElement("H5");
- label.appendChild(document.createTextNode(node['name']));
- div.appendChild(label);
- button = document.createElement("BUTTON");
+function make_remove_button(div, node){
+ var button = document.createElement("BUTTON");
button.type = "button";
button.appendChild(document.createTextNode("Remove"));
button.classList.add("btn-danger");
button.classList.add("btn");
- div.appendChild(button);
- for(var i=0; i<node['forms'].length; i++){
- form = node['forms'][i];
- var input = document.createElement("INPUT");
- input.type = form['type'];
- input.name = form['name'];
- input.pattern = "(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})";
- input.title = "Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed"
- input.placeholder = form['placeholder'];
- inputs.push(input);
- input.onchange = function() { updateObjectResult(div); restrictchars(this); };
- input.oninput = function() { restrictchars(this); };
- if(form['name'] in prepopulate){
- input.value = prepopulate[form['name']];
- }
- div.appendChild(input);
- }
- //add class id to dropdown object
- var hiddenInput = document.createElement("INPUT");
- hiddenInput.type = "hidden";
- hiddenInput.name = "class";
- hiddenInput.value = node['id'];
- div.appendChild(hiddenInput);
button.onclick = function(){
- remove_dropdown(div.id);
+ remove_dropdown(div.id, node.id);
}
+ return button;
+}
+
+function make_input(div, node, prepopulate){
+ var input = document.createElement("INPUT");
+ input.type = node.form.type;
+ input.name = node.id + node.form.name
+ input.pattern = "(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})";
+ input.title = "Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed"
+ input.placeholder = node.form.placeholder;
+ inputs.push(input);
+ input.onchange = function() { updateObjectResult(node, div.id, input.value); restrictchars(this); };
+ input.oninput = function() { restrictchars(this); };
+ if(prepopulate)
+ input.value = prepopulate;
+ return input;
+}
+
+function add_item_prepopulate(node, prepopulate){
+ var div = document.createElement("DIV");
+ div.id = "dropdown_" + dropdown_count;
+ div.classList.add("dropdown_item");
+ dropdown_count++;
+ var label = document.createElement("H5")
+ label.appendChild(document.createTextNode(node['name']))
+ div.appendChild(label);
+ div.appendChild(make_input(div, node, prepopulate));
+ div.appendChild(make_remove_button(div, node));
document.getElementById("dropdown_wrapper").appendChild(div);
- var linebreak = document.createElement("BR");
- document.getElementById("dropdown_wrapper").appendChild(linebreak);
- updateObjectResult(div);
return div;
}
-function remove_dropdown(id){
- var div = document.getElementById(id);
+function remove_dropdown(div_id, node_id){
+ var div = document.getElementById(div_id);
+ var node = filter_items[node_id]
var parent = div.parentNode;
div.parentNode.removeChild(div);
+ delete result[node.class][node.id]['values'][div.id];
+
//checks if we have removed last item in class
- var deselect_class = true;
- var div_inputs = div.getElementsByTagName("input");
- var div_class = div_inputs[div_inputs.length-1].value;
- var result_class = document.getElementById(div_class).parentNode.parentNode.id;
- delete result[result_class][div.id];
- for(var i=0; i<parent.children.length; i++){
- var inputs = parent.children[i].getElementsByTagName("input");
- var object_class = "";
- for(var k=0; k<inputs.length; k++){
- if(inputs[k].name == "class"){
- object_class = inputs[k].value;
- }
- }
- if(object_class == div_class){
- deselect_class = false;
- }
- }
- if(deselect_class){
- clear(filter_items[div_class]);
+ if(jQuery.isEmptyObject(result[node.class][node.id]['values'])){
+ delete result[node.class][node.id];
+ clear(node);
}
}
-
-function updateResult(nodeId){
- if(!initialized){
- filter_field_init();
- }
- if(!filter_items[nodeId]['multiple']){
- var node = document.getElementById(nodeId);
- var value = {}
- value[nodeId] = node.parentNode.getElementsByTagName("input")[0].value;
- result[node.parentNode.id] = {};
- result[node.parentNode.id][nodeId] = value;
+function updateResult(node){
+ try_init();
+ if(!node['multiple']){
+ result[node.class][node.id] = {selected: node.selected, id: node.model_id}
+ if(!node.selected)
+ delete result[node.class][node.id];
}
}
-function updateObjectResult(parentElem){
- node_type = document.getElementById(parentElem.class).parentNode.id;
- input = {};
- inputs = parentElem.getElementsByTagName("input");
- for(var i in inputs){
- var e = inputs[i];
- input[e.name] = e.value;
- }
- result[node_type][parentElem.id] = input;
+function updateObjectResult(node, childKey, childValue){
+ try_init();
+ if(!result[node.class][node.id])
+ result[node.class][node.id] = {selected: true, id: node.model_id, values: {}}
+
+ result[node.class][node.id]['values'][childKey] = childValue;
}
-function filter_field_init() {
+function try_init() {
+ if(initialized) return;
for(nodeId in filter_items) {
- element = document.getElementById(nodeId);
- node = filter_items[nodeId];
- result[element.parentNode.id] = {}
+ var element = document.getElementById(nodeId);
+ var node = filter_items[nodeId];
+ result[node.class] = {}
}
initialized = true;
}
diff --git a/dashboard/src/templates/resource/steps/define_hardware.html b/dashboard/src/templates/resource/steps/define_hardware.html
index 933b4ab..9192842 100644
--- a/dashboard/src/templates/resource/steps/define_hardware.html
+++ b/dashboard/src/templates/resource/steps/define_hardware.html
@@ -26,7 +26,7 @@ var normalize = function(data){
}
return normalized;
}
-var data = normalize(result);
+var data = result;
data = JSON.stringify(data);
document.getElementById("filter_field").value = data;
var formData = $("#define_hardware_form").serialize();