From 25fa45356f37995f6c5db3c07e5a76d4cfa16b4a Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Sun, 27 Jan 2013 17:21:19 -0500 Subject: [PATCH] javascript, etc to allow editing of directionality and edge titles. This commit results in effective, but really horrid UI-wise, editing of such things. --- .../Jit/graphsettings-event-handlers.js | 1 + .../javascripts/Jit/graphsettings-model.js | 6 + app/assets/javascripts/Jit/graphsettings.js | 41 +-- .../javascripts/Jit/onCreateLabelHandler.js | 22 +- .../Jit/select-edit-delete-nodes-and-edges.js | 237 +++++++++++++++--- app/assets/stylesheets/application.css | 10 + app/assets/stylesheets/base.css | 23 +- app/controllers/synapses_controller.rb | 14 +- 8 files changed, 278 insertions(+), 76 deletions(-) diff --git a/app/assets/javascripts/Jit/graphsettings-event-handlers.js b/app/assets/javascripts/Jit/graphsettings-event-handlers.js index 27a6c088..de2ba4bc 100644 --- a/app/assets/javascripts/Jit/graphsettings-event-handlers.js +++ b/app/assets/javascripts/Jit/graphsettings-event-handlers.js @@ -4,6 +4,7 @@ function selectEdgeOnClickHandler(adj, e) { //editing overrides everything else if (e.altKey) { + //in select-edit-delete-nodes-and-edges.js editEdge(adj, e); return; } diff --git a/app/assets/javascripts/Jit/graphsettings-model.js b/app/assets/javascripts/Jit/graphsettings-model.js index 3b234530..c67de017 100644 --- a/app/assets/javascripts/Jit/graphsettings-model.js +++ b/app/assets/javascripts/Jit/graphsettings-model.js @@ -10,3 +10,9 @@ MetamapsModel.selectedEdges = new Array(); MetamapsModel.lastCanvasClick = 0; MetamapsModel.DOUBLE_CLICK_TOLERANCE = 300; MetamapsModel.edgeHoveringOver = false; +MetamapsModel.edgePermTimer1 = null; +MetamapsModel.edgePermTimer2 = null; +MetamapsModel.edgePermSliding = false; +MetamapsModel.topicPermTimer1 = null; +MetamapsModel.topicPermTimer2 = null; +MetamapsModel.topicPermSliding = false; diff --git a/app/assets/javascripts/Jit/graphsettings.js b/app/assets/javascripts/Jit/graphsettings.js index 6c3eb3bc..387ac3e8 100644 --- a/app/assets/javascripts/Jit/graphsettings.js +++ b/app/assets/javascripts/Jit/graphsettings.js @@ -175,8 +175,7 @@ var renderMidArrow = function(from, to, dim, swap, canvas){ // move midpoint by half the "length" of the arrow so the arrow is centered on the midpoint var arrowPoint = new $jit.Complex((vect.x / 0.7) + midPoint.x, (vect.y / 0.7) + midPoint.y); // compute the tail intersection point with the edge line - var intermediatePoint = new $jit.Complex(arrowPoint.x - vect.x, -arrowPoint.y - vect.y); + var intermediatePoint = new $jit.Complex(arrowPoint.x - vect.x, arrowPoint.y - vect.y); // vector perpendicular to vect var normal = new $jit.Complex(-vect.y / 2, vect.x / 2); var v1 = intermediatePoint.add(normal); @@ -232,6 +231,28 @@ var nodeSettings = { } } + var renderEdgeArrows = function(edgeHelper, adj) { + var canvas = Mconsole.canvas; + var directionCat = adj.getData('category'); + var direction = adj.getData('direction'); + var pos = adj.nodeFrom.pos.getc(true); + var posChild = adj.nodeTo.pos.getc(true); + + //plot arrow edge + if (directionCat == "none") { + //this.edgeHelper.line.render({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, canvas); + } + else if (directionCat == "both") { + renderMidArrow({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 13, true, canvas); + renderMidArrow({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 13, false, canvas); + } + else if (directionCat == "from-to") { + var direction = adj.data.$direction; + var inv = (direction && direction.length > 1 && direction[0] != adj.nodeFrom.id); + renderMidArrow({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 13, inv, canvas); + } + }//renderEdgeArrow + // defining custom edges var edgeSettings = { 'customEdge': { @@ -242,19 +263,7 @@ var nodeSettings = { var directionCat = adj.getData("category"); //label placement on edges - //plot arrow edge - if (directionCat == "none") { - this.edgeHelper.line.render({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, canvas); - } - else if (directionCat == "both") { - renderMidArrow({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 13, true, canvas); - renderMidArrow({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 13, false, canvas); - } - else if (directionCat == "from-to") { - var direction = adj.data.$direction; - var inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id); - renderMidArrow({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 13, inv, canvas); - } + renderEdgeArrows(this.edgeHelper, adj); //check for edge label in data var desc = adj.getData("desc"); @@ -322,6 +331,8 @@ function onMouseMoveHandler(node, eventInfo, e) { onMouseLeave(MetamapsModel.edgeHoveringOver) onMouseEnter(edge); } + + //could be false MetamapsModel.edgeHoveringOver = edge; } diff --git a/app/assets/javascripts/Jit/onCreateLabelHandler.js b/app/assets/javascripts/Jit/onCreateLabelHandler.js index 6fc4dd53..95232723 100644 --- a/app/assets/javascripts/Jit/onCreateLabelHandler.js +++ b/app/assets/javascripts/Jit/onCreateLabelHandler.js @@ -329,20 +329,18 @@ function bindCallbacks(showCard, nameContainer, node) { $(showCard).find('.go-link').attr('href', link); }); - var sliding2 = false; - var lT1,lT2; $(showCard).find(".permActivator").bind('mouseover', function () { - clearTimeout(lT2); + clearTimeout(MetamapsModel.topicPermTimer2); that = this; - lT1 = setTimeout(function() { - if (! sliding2) { - sliding2 = true; + MetamapsModel.topicPermTimer1 = setTimeout(function() { + if (! MetamapsModel.topicPermSliding) { + MetamapsModel.topicPermSliding = true; $(that).animate({ width: '203px', height: '37px' }, 300, function() { - sliding2 = false; + MetamapsModel.topicPermSliding = false; }); } }, 300); @@ -350,16 +348,16 @@ function bindCallbacks(showCard, nameContainer, node) { $(showCard).find(".permActivator").bind('mouseout', function () { - clearTimeout(lT1); + clearTimeout(MetamapsModel.topicPermTimer1); that = this; - lT2 = setTimeout(function() { - if (! sliding2) { - sliding2 = true; + MetamapsModel.topicPermTimer2 = setTimeout(function() { + if (! MetamapsModel.topicPermSliding) { + MetamapsModel.topicPermSliding = true; $(that).animate({ height: '16px', width: '16px' }, 300, function() { - sliding2 = false; + MetamapsModel.topicPermSliding = false; }); } },800); diff --git a/app/assets/javascripts/Jit/select-edit-delete-nodes-and-edges.js b/app/assets/javascripts/Jit/select-edit-delete-nodes-and-edges.js index 7de4977c..9c420e57 100644 --- a/app/assets/javascripts/Jit/select-edit-delete-nodes-and-edges.js +++ b/app/assets/javascripts/Jit/select-edit-delete-nodes-and-edges.js @@ -1,56 +1,211 @@ function editEdge(edge, e) { if (authorizeToEdit(edge)) { - //reset so we don't interfere with other edges - $('#edit_synapse').remove(); + //reset so we don't interfere with other edges + $('#edit_synapse').remove(); - deselectEdge(edge); //so the label is missing while editing - var perm = document.createElement('div'); - perm.className = 'permission canEdit'; - var edit_div = document.createElement('div'); - edit_div.setAttribute('id', 'edit_synapse'); - perm.appendChild(edit_div); - $('.main .wrapper').append(perm); - $('#edit_synapse').attr('class', 'best_in_place best_in_place_desc'); - $('#edit_synapse').attr('data-object', 'synapse'); - $('#edit_synapse').attr('data-attribute', 'desc'); - $('#edit_synapse').attr('data-type', 'input'); - //TODO how to get blank data-nil - $('#edit_synapse').attr('data-nil', ' '); - $('#edit_synapse').attr('data-url', '/synapses/' + edge.getData("id")); - $('#edit_synapse').html(edge.getData("desc")); + //so label is missing while editing + deselectEdge(edge); - $('#edit_synapse').css('position', 'absolute'); - $('#edit_synapse').css('left', e.clientX); - $('#edit_synapse').css('top', e.clientY); + //create the wrapper around the form elements, including permissions + //classes to make best_in_place happy + var edit_div = document.createElement('div'); + edit_div.setAttribute('id', 'edit_synapse'); + edit_div.className = 'permission canEdit'; + $('.main .wrapper').append(edit_div); - $('#edit_synapse').bind("ajax:success", function() { - var desc = $(this).html(); - edge.setData("desc", desc); - selectEdge(edge); - Mconsole.plot(); - $('#edit_synapse').remove(); - }); + populateEditEdgeForm(edge); - $('#edit_synapse').focusout(function() { - //in case they cancel - $('#edit_synapse').hide(); - }); - - //css stuff above moves it, this activates it - $('#edit_synapse').click(); - $('#edit_synapse form').submit(function() { - //hide it once form submits. - //If you don't do this, and data is unchanged, it'll show up on canvas - $('#edit_synapse').hide(); - }); - $('#edit_synapse input').focus(); - $('#edit_synapse').show(); + //drop it in the right spot, activate it + $('#edit_synapse').css('position', 'absolute'); + $('#edit_synapse').css('left', e.clientX); + $('#edit_synapse').css('top', e.clientY); + //$('#edit_synapse_name').click(); //required in case name is empty + //$('#edit_synapse_name input').focus(); + $('#edit_synapse').show(); } else if ((! authorizeToEdit(edge)) && userid) { alert("You don't have the permissions to edit this synapse."); } } +function populateEditEdgeForm(edge) { + add_name_form(edge); + add_perms_form(edge); + add_direction_form(edge); +} +function add_name_form(edge) { + //name editing form + $('#edit_synapse').append('
'); + $('#edit_synapse_name').attr('class', 'best_in_place best_in_place_desc'); + $('#edit_synapse_name').attr('data-object', 'synapse'); + $('#edit_synapse_name').attr('data-attribute', 'desc'); + $('#edit_synapse_name').attr('data-type', 'input'); + //TODO how to get blank data-nil + $('#edit_synapse_name').attr('data-nil', ' '); + $('#edit_synapse_name').attr('data-url', '/synapses/' + edge.getData("id")); + $('#edit_synapse_name').html(edge.getData("desc")); + + $('#edit_synapse_name').bind("ajax:success", function() { + var desc = $(this).html(); + edge.setData("desc", desc); + selectEdge(edge); + Mconsole.plot(); + }); +} +function add_perms_form(edge) { + //permissions - if owner, also allow permission editing + $('#edit_synapse').append('
'); + $('#edit_synapse .mapPerm').text(mk_permission(edge)); + if (userid == edge.getData('userid')) { + $('#edit_synapse').append('
'); + $('#edit_synapse .permActivator').append('
'); + $('#edit_synapse .editSettings').append('Permissions:'); + $('#edit_synapse .editSettings').append(''); + $('#edit_synapse .click-to-edit').attr('title', 'Click to Edit'); + $('#edit_synapse .click-to-edit').append(best_in_place_perms(edge)); + $('#edit_synapse .editSettings').append('
'); + $('#edit_synapse .permActivator').bind('mouseover', function() { + clearTimeout(MetamapsModel.edgePermTimer2); + that = this; + MetamapsModel.edgePermTimer1 = setTimeout(function() { + if (! MetamapsModel.edgePermSliding) { + MetamapsModel.edgePermSliding = true; + $(that).animate({ + width: '203px', + height: '37px' + }, 300, function() { + MetamapsModel.edgePermSliding = false; + }); + } + }, 300); + }); + $('#edit_synapse .permActivator').bind('mouseout', function () { + return; + clearTimeout(MetamapsModel.edgePermTimer1); + that = this; + MetamapsModel.edgePermTimer2 = setTimeout(function() { + if (! MetamapsModel.edgePermSliding) { + MetamapsModel.edgePermSliding = true; + $(that).animate({ + height: '16px', + width: '16px' + }, 300, function() { + MetamapsModel.edgePermSliding = false; + }); + } + },800); + } + ); + } +}//add_perms_form + +function add_direction_form(edge) { + //directionality checkboxes + $('#edit_synapse').append(''); + $('#edit_synapse').append(''); + $('#edit_synapse').append(''); + $('#edit_synapse').append(''); + + //determine which node is to the left and the right + //if directly in a line, top is left + if (edge.nodeFrom.pos.x < edge.nodeTo.pos.x || + edge.nodeFrom.pos.x == edge.nodeTo.pos.x && + edge.nodeFrom.pos.y < edge.nodeTo.pos.y) { + var left = edge.nodeTo; + var right = edge.nodeFrom; + } else { + var left = edge.nodeFrom; + var right = edge.nodeTo; + } + + /* + * One node is actually on the left onscreen. Call it left, & the other right. + * If category is from-to, and that node is first, check the 'right' checkbox. + * Else check the 'left' checkbox since the arrow is incoming. + */ + + var directionCat = edge.getData('category'); //both, none, from-to + if (directionCat == 'from-to') { + var from_to = edge.getData('direction'); + if (from_to[0] == left.id) { + //check left checkbox + $('#edit_synapse_left').prop('checked', true); + } else { + //check right checkbox + $('#edit_synapse_right').prop('checked', true); + } + } else if (directionCat == 'both') { + //check both checkboxes + $('#edit_synapse_left').prop('checked', true); + $('#edit_synapse_right').prop('checked', true); + } + $('edit_synapse_left, #edit_synapse_right').click(function() { + var leftChecked = $('#edit_synapse_left').is(':checked'); + var rightChecked = $('#edit_synapse_right').is(':checked'); + + var dir = edge.getData('direction'); + var dirCat = 'none'; + if (leftChecked && rightChecked) { + dirCat = 'both'; + } else if (!leftChecked && rightChecked) { + dirCat = 'from-to'; + dir = [left.id, right.id]; + } else if (leftChecked && !rightChecked) { + dirCat = 'from-to'; + dir = [right.id, left.id]; + } + $.ajax({ + 'type': 'PUT', + 'url': '/synapses/' + edge.getData('id'), + 'data': { + synapse: { + category:dirCat + }, + node1_id: { + node1: dir[0] + }, + node2_id: { + node2: dir[1] + } + }, + 'success': function() { + updateEdgeDisplay(edge, dir, dirCat); + } + }); + }); +}//add_direction_form + +function updateEdgeDisplay(edge, dir, dirCat) { + edge.setData('category', dirCat); + edge.setData('direction', dir); + + //render mid arrow + renderEdgeArrows(null, edge); +} + +function best_in_place_perms(edge) { + var output = + ''; + + var permission_choices = "'["; + permission_choices += '["commons","commons"],'; + permission_choices += '["public","public"],'; + permission_choices += '["private","private"]'; + permission_choices += "]'"; + + output = output.replace(/\$_permission_choices_\$/g, permission_choices); + output = output.replace(/\$_id_\$/g, edge.getData('id')); + output = output.replace(/\$_current_$\$/g, edge.getData('permission')); + return output; +}//best_in_place_perms + function deselectAllEdges() { for (var i = 0; i < MetamapsModel.selectedEdges.length; i += 1) { var edge = MetamapsModel.selectedEdges[i]; diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 6adbf863..a59c0bee 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -646,3 +646,13 @@ input[type="submit"] { .contact a { color: #36bbe8; } + +#edit_synapse label, +#edit_synapse_left, +#edit_synapse_right { + display: inline-block; +} + +#edit_synapse label.left { + margin-right: 0.5em; +} diff --git a/app/assets/stylesheets/base.css b/app/assets/stylesheets/base.css index 4d8c2c0a..d57f216f 100644 --- a/app/assets/stylesheets/base.css +++ b/app/assets/stylesheets/base.css @@ -103,6 +103,10 @@ margin-top:2px; } +#edit_synapse .best_in_place_desc { + width: auto; +} + .best_in_place_desc textarea{ width:150px; display:block; @@ -157,7 +161,7 @@ } .editSettings span { - float:left; + display: inline-block; } .permActivator { @@ -181,3 +185,20 @@ width:16px; text-align:center; } + +#edit_synapse .mapPerm, +#edit_synapse .permActivator { + position: static; + display: inline-block; + color: #000; + margin: 2px; +} + +#edit_synapse { + background-color: #aaa; + border-radius: 0.5em; +} + +#edit_synapse_name { + margin-left: 0.4em; +} diff --git a/app/controllers/synapses_controller.rb b/app/controllers/synapses_controller.rb index c329bc25..1f1e1c61 100644 --- a/app/controllers/synapses_controller.rb +++ b/app/controllers/synapses_controller.rb @@ -106,14 +106,14 @@ class SynapsesController < ApplicationController # GET synapses/:id/edit def edit - @current = current_user + @current = current_user @synapse = Synapse.find(params[:id]).authorize_to_edit(@current) - if @synapse - @topics = Topic.visibleToUser(@current, nil) - elsif not @synapse - redirect_to root_url and return - end + if @synapse + @topics = Topic.visibleToUser(@current, nil) + elsif not @synapse + redirect_to root_url and return + end respond_with(@synapse, @topics) end @@ -132,7 +132,7 @@ class SynapsesController < ApplicationController if params[:node1_id] and params[:node1_id][:node1] @synapse.topic1 = Topic.find(params[:node1_id][:node1]) end - if params[:node1_id] and params[:node1_id][:node1] + if params[:node2_id] and params[:node2_id][:node2] @synapse.topic2 = Topic.find(params[:node2_id][:node2]) end @synapse.save