diff --git a/Gemfile.lock b/Gemfile.lock index d768a413..cc33f0b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -128,7 +128,7 @@ DEPENDENCIES formtastic formula jbuilder - jquery-rails + jquery-rails (= 2.1.2) json pg rails (= 3.2.11) diff --git a/app/assets/javascripts/Jit/graphsettings-event-handlers.js b/app/assets/javascripts/Jit/graphsettings-event-handlers.js index 6ec0a25e..f1f86843 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 bc0e4d5c..e53d5b8b 100644 --- a/app/assets/javascripts/Jit/graphsettings.js +++ b/app/assets/javascripts/Jit/graphsettings.js @@ -74,6 +74,10 @@ function graphSettings(type) { //Add also a click handler to nodes onClick: function (node, eventInfo, e) { if (e.target.id != "infovis-canvas") return false; + + //hide synapse editing dialog + $('#edit_synapse').hide(); + //clicking on a node, or clicking on blank part of canvas? if (node.nodeFrom) { selectEdgeOnClickHandler(node, e); @@ -161,8 +165,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); @@ -250,6 +253,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") { + 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': { @@ -260,20 +285,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") { - this.edgeHelper.line.render({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, canvas); - renderMidArrows({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 13, true, canvas); - renderMidArrows({ 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"); @@ -341,6 +353,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 3370b402..616c609b 100644 --- a/app/assets/javascripts/Jit/onCreateLabelHandler.js +++ b/app/assets/javascripts/Jit/onCreateLabelHandler.js @@ -321,20 +321,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); @@ -342,16 +340,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 c96eecfa..ecbb55e5 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,216 @@ 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) { + //disabled for now since permissions are complicated + //TODO: figure out why permissions are "0" in edge objects + //instead of being "commons" or "public", etc + //add_perms_form(edge); + add_direction_form(edge); + add_name_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').html(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; //devin + 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 = [right.id, left.id]; + } else if (leftChecked && !rightChecked) { + dirCat = 'from-to'; + dir = [left.id, right.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(data) { + updateEdgeDisplay(edge, dir, dirCat); + } + }); + }); +}//add_direction_form + +function updateEdgeDisplay(edge, dir, dirCat) { + edge.setData('category', dirCat); + edge.setData('direction', dir); + + //render mid arrow + //renderEdgeArrows(Mconsole.fx.edgeHelper, edge); + Mconsole.plot(); +} + +function best_in_place_perms(edge) { + var output = + '$_perm_$'; + + 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')); + output = output.replace(/\$_perm_\$/g, mk_permission(edge)); + 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 9ed5830c..88d5bac1 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -689,3 +689,13 @@ text-align:center; .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 9817c8f0..dede0afe 100644 --- a/app/assets/stylesheets/base.css +++ b/app/assets/stylesheets/base.css @@ -122,6 +122,10 @@ margin-top:2px; } +#edit_synapse .best_in_place_desc { + width: auto; +} + .best_in_place_desc textarea{ width:150px; display:block; @@ -176,7 +180,7 @@ } .editSettings span { - float:left; + display: inline-block; } .permActivator { @@ -200,3 +204,35 @@ width:16px; text-align:center; } + +#edit_synapse .mapPerm, +#edit_synapse .permActivator { + position: static; + display: inline-block; + color: #000; + margin: 2px; +} + +#edit_synapse .mapPerm { + margin-left: 1em; +} + +#edit_synapse .permActivator { + position: absolute; + margin-top: -0.5em; +} + +#edit_synapse { + background: url(bg.png); + border-radius: 0.5em; + padding: 5px; + color: #000; +} + +#edit_synapse_name { + margin-left: 0.4em; +} + +#edit_synapse .click-to-edit { + margin-left: 0.3em; +} diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 813a398c..0b4eb3ad 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -22,10 +22,10 @@ class SessionsController < ApplicationController @user = User.new - @topic = Topic.find(260) + @topic = Topic.exists?(260) - if @map - respond_with(@user, @session, location: topic_url(@topic)) do |format| + if @topic + respond_with(@user, @session, location: topic_url(260)) do |format| end else respond_with(@user, @session, location: root_url) do |format| diff --git a/app/controllers/synapses_controller.rb b/app/controllers/synapses_controller.rb index 18c52140..94f459d8 100644 --- a/app/controllers/synapses_controller.rb +++ b/app/controllers/synapses_controller.rb @@ -106,41 +106,42 @@ 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 # PUT synapses/:id def update - @current = current_user + @current = current_user @synapse = Synapse.find(params[:id]).authorize_to_edit(@current) - if @synapse + if @synapse if params[:synapse] @synapse.desc = params[:synapse][:desc] if params[:synapse][:desc] - @synapse.category = params[:synapse][:category] if params[:synapse][:category] - @synapse.permission = params[:synapse][:permission] if params[:synapse][:permission] + @synapse.category = params[:synapse][:category] if params[:synapse][:category] + @synapse.permission = params[:synapse][:permission] if params[:synapse][:permission] end if params[:node1_id] and params[:node1_id][:node1] - @synapse.topic1 = Topic.find(params[:node1_id][:node1]) + @synapse.topic1 = Topic.find(params[:node1_id][:node1]) end - if params[:node1_id] and params[:node1_id][:node1] - @synapse.topic2 = Topic.find(params[:node2_id][:node2]) + if params[:node2_id] and params[:node2_id][:node2] + @synapse.topic2 = Topic.find(params[:node2_id][:node2]) end - @synapse.save + @synapse.save end - respond_with @synapse - #respond_with(@user, location: synapse_url(@synapse)) do |format| - #end + respond_to do |format| + format.js + format.json { respond_with(@synapse) } + end end # POST mappings/:map_id/:synapse_id/removefrommap diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 4a9dc625..7b5a00ec 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -42,16 +42,15 @@ class UsersController < ApplicationController @user = User.create(params[:user]) - #generate a random 8 letter/digit code that they can use to invite people - @user.code = rand(36**8).to_s(36) + #generate a random 8 letter/digit code that they can use to invite people + @user.code = rand(36**8).to_s(36) @user.save - # direct them straight to the metamaps manual map. - - @map = Map.first(:conditions => [ "id = ?", 5]) + # direct them straight to the metamaps manual topic 'Click Me' + @topic = Topic.exists?(260) - if @map - respond_with(@user, location: map_url(@map)) do |format| + if @topic + respond_with(@user, location: topic_url(260)) do |format| end else respond_with(@user, location: root_url) do |format| diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 05b3b5d9..3ffc097d 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -49,7 +49,9 @@
  • About
  • Request Invite
  • <%= link_to "Explore Maps", maps_url %>
  • -
  • <%= link_to "Tour", "/topics/260" %>
  • + <% if Topic.exists?(260) %> +
  • <%= link_to "Tour", "/topics/260" %>
  • + <% end %>
  • <%= link_to "Login", new_session_path, id: "Login" %>
  • <% end %> diff --git a/app/views/synapses/update.js.erb b/app/views/synapses/update.js.erb new file mode 100644 index 00000000..b8f9dd59 --- /dev/null +++ b/app/views/synapses/update.js.erb @@ -0,0 +1,4 @@ +/* + * @file + * Javascript called when you update a synapse + */