javascript, etc to allow editing of directionality and edge titles. This commit results in effective, but really horrid UI-wise, editing of such things.

This commit is contained in:
Devin Howard 2013-01-27 17:21:19 -05:00
parent 0ad2392af5
commit 25fa45356f
8 changed files with 278 additions and 76 deletions

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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('<div id="edit_synapse_name"></div>');
$('#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('<div class="mapPerm"></div>');
$('#edit_synapse .mapPerm').text(mk_permission(edge));
if (userid == edge.getData('userid')) {
$('#edit_synapse').append('<div class="permActivator" />');
$('#edit_synapse .permActivator').append('<div class="editSettings" />');
$('#edit_synapse .editSettings').append('<span>Permissions:</span>');
$('#edit_synapse .editSettings').append('<span class="click-to-edit" />');
$('#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('<div class="clearfloat" />');
$('#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('<input type="checkbox" id="edit_synapse_left">');
$('#edit_synapse').append('<label class="left">&lt;</label>');
$('#edit_synapse').append('<input type="checkbox" id="edit_synapse_right">');
$('#edit_synapse').append('<label class="right">&gt;</label>');
//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 =
'<span class="best_in_place best_in_place_permission" \
id="best_in_place_topic_$_id_$_permission" \
data-url="/synapses/$_id_$" \
data-object="synapse" \
data-collection=$_permission_choices_$ \
data-attribute="permission" \
data-type="select" \
data-value="$_current_$">';
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];

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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