diff --git a/app/assets/javascripts/Jit/Map/metamapMAP.js b/app/assets/javascripts/Jit/Map/metamapMAP.js
new file mode 100644
index 00000000..42a7c046
--- /dev/null
+++ b/app/assets/javascripts/Jit/Map/metamapMAP.js
@@ -0,0 +1,342 @@
+var labelType, useGradients, nativeTextSupport, animate, json, map;
+
+(function() {
+ var ua = navigator.userAgent,
+ iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i),
+ typeOfCanvas = typeof HTMLCanvasElement,
+ nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'),
+ textSupport = nativeCanvasSupport
+ && (typeof document.createElement('canvas').getContext('2d').fillText == 'function');
+ //I'm setting this based on the fact that ExCanvas provides text support for IE
+ //and that as of today iPhone/iPad current text support is lame
+ labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML';
+ nativeTextSupport = labelType == 'Native';
+ useGradients = nativeCanvasSupport;
+ animate = !(iStuff || !nativeCanvasSupport);
+})();
+
+var imgArray = new Object();
+
+imgArray['Group'] = new Image(); imgArray['Group'].src = '/assets/group.png';
+imgArray['Person'] = new Image(); imgArray['Person'].src = '/assets/person.png';
+imgArray['Bizarre'] = new Image(); imgArray['Bizarre'].src = '/assets/bizarre.png';
+imgArray['Catalyst'] = new Image(); imgArray['Catalyst'].src = '/assets/catalyst.png';
+imgArray['Closed'] = new Image(); imgArray['Closed'].src = '/assets/closed.png';
+imgArray['Experience'] = new Image(); imgArray['Experience'].src = '/assets/experience.png';
+imgArray['Future Dev'] = new Image(); imgArray['Future Dev'].src = '/assets/futuredev.png';
+imgArray['Idea'] = new Image(); imgArray['Idea'].src = '/assets/idea.png';
+imgArray['Implication'] = new Image(); imgArray['Implication'].src = '/assets/implication.png';
+imgArray['Insight'] = new Image(); imgArray['Insight'].src = '/assets/insight.png';
+imgArray['Intention'] = new Image(); imgArray['Intention'].src = '/assets/intention.png';
+imgArray['Knowledge'] = new Image(); imgArray['Knowledge'].src = '/assets/knowledge.png';
+imgArray['Location'] = new Image(); imgArray['Location'].src = '/assets/location.png';
+imgArray['Open Issue'] = new Image(); imgArray['Open Issue'].src = '/assets/openissue.png';
+imgArray['Opinion'] = new Image(); imgArray['Opinion'].src = '/assets/opinion.png';
+imgArray['Opportunity'] = new Image(); imgArray['Opportunity'].src = '/assets/opportunity.png';
+imgArray['Platform'] = new Image(); imgArray['Platform'].src = '/assets/platform.png';
+imgArray['Problem'] = new Image(); imgArray['Problem'].src = '/assets/problem.png';
+imgArray['Question'] = new Image(); imgArray['Question'].src = '/assets/question.png';
+imgArray['Reference'] = new Image(); imgArray['Reference'].src = '/assets/reference.png';
+imgArray['Requirement'] = new Image(); imgArray['Requirement'].src = '/assets/requirement.png';
+imgArray['Resource'] = new Image(); imgArray['Resource'].src = '/assets/resource.png';
+imgArray['Role'] = new Image(); imgArray['Role'].src = '/assets/role.png';
+imgArray['Task'] = new Image(); imgArray['Task'].src = '/assets/task.png';
+imgArray['Tool'] = new Image(); imgArray['Tool'].src = '/assets/tool.png';
+imgArray['Trajectory'] = new Image(); imgArray['Trajectory'].src = '/assets/trajectory.png';
+imgArray['Action'] = new Image(); imgArray['Action'].src = '/assets/action.png';
+imgArray['Activity'] = new Image(); imgArray['Activity'].src = '/assets/activity.png';
+
+function initMAP(){
+ // init custom node type
+ $jit.ForceDirected.Plot.NodeTypes.implement({
+ 'customNode': {
+ 'render': function (node, canvas) {
+ var pos = node.pos.getc(true),
+ dim = node.getData('dim'),
+ cat = node.getData('itemcatname'),
+ ctx = canvas.getCtx();
+ ctx.drawImage(imgArray[cat], pos.x - dim, pos.y - dim, dim*2, dim*2);
+
+ },
+ 'contains': function(node, pos) {
+ var npos = node.pos.getc(true),
+ dim = node.getData('dim');
+ return this.nodeHelper.circle.contains(npos, pos, dim);
+ }
+ }
+ });
+ //implement an edge type
+ $jit.ForceDirected.Plot.EdgeTypes.implement({
+ 'customEdge': {
+ 'render': function(adj, canvas) {
+ //get nodes cartesian coordinates
+ var pos = adj.nodeFrom.pos.getc(true);
+ var posChild = adj.nodeTo.pos.getc(true);
+
+ 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.arrow.render({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 40, false, canvas);
+ this.edgeHelper.arrow.render({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 40, true, canvas);
+ }
+ else if (directionCat == "from-to") {
+ var direction = adj.data.$direction;
+ var inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
+ this.edgeHelper.arrow.render({ x: pos.x, y: pos.y }, { x: posChild.x, y: posChild.y }, 40, inv, canvas);
+ }
+
+ //check for edge label in data
+ var desc = adj.getData("desc") + ' (' + adj.getData("userid") + ',' + adj.getData("id") + ')';
+ var showDesc = adj.getData("showDesc");
+ if( desc != "" && showDesc ) {
+ //now adjust the label placement
+ var radius = canvas.getSize();
+ var x = parseInt((pos.x + posChild.x - (desc.length * 5)) /2);
+ var y = parseInt((pos.y + posChild.y) /2);
+ canvas.getCtx().fillStyle = '#000';
+ canvas.getCtx().font = 'bold 14px arial';
+ //canvas.getCtx().fillText(desc, x, y);
+ }
+ }
+ }
+ });
+ // end
+ // init ForceDirected
+ map = new $jit.ForceDirected({
+ //id of the visualization container
+ injectInto: 'infovis',
+ //Enable zooming and panning
+ //by scrolling and DnD
+ Navigation: {
+ enable: true,
+ type: 'HTML',
+ //Enable panning events only if we're dragging the empty
+ //canvas (and not a node).
+ panning: 'avoid nodes',
+ zooming: 10 //zoom speed. higher is more sensible
+ },
+ // Change node and edge styles such as
+ // color and width.
+ // These properties are also set per node
+ // with dollar prefixed data-properties in the
+ // JSON structure.
+ Node: {
+ overridable: true,
+ color: '#2D6A5D',
+ type: 'customNode',
+ dim: 25
+ },
+ Edge: {
+ overridable: true,
+ color: '#222222',
+ type: 'customEdge',
+ lineWidth: 1
+ },
+ //Native canvas text styling
+ Label: {
+ type: 'HTML', //Native or HTML
+ size: 20,
+ //style: 'bold'
+ },
+ //Add Tips
+ Tips: {
+ enable: true,
+ onShow: function(tip, node) {
+ //count connections
+ var count = 0;
+ node.eachAdjacency(function() { count++; });
+ //display node info in tooltip
+ tip.innerHTML = "
" + node.name + "
"
+ + "connections: " + count + "
";
+ }
+ },
+ // Add node events
+ Events: {
+ enable: true,
+ type: 'HTML',
+ //Change cursor style when hovering a node
+ onMouseEnter: function() {
+ //fd.canvas.getElement().style.cursor = 'move';
+ },
+ onMouseLeave: function() {
+ //fd.canvas.getElement().style.cursor = '';
+ },
+ //Update node positions when dragged
+ onDragMove: function(node, eventInfo, e) {
+ var pos = eventInfo.getPos();
+ node.pos.setc(pos.x, pos.y);
+ map.plot();
+ },
+ //Implement the same handler for touchscreens
+ onTouchMove: function(node, eventInfo, e) {
+ $jit.util.event.stop(e); //stop default touchmove event
+ this.onDragMove(node, eventInfo, e);
+ },
+ //Add also a click handler to nodes
+ onClick: function(node) {
+ if(!node) return;
+ //set final styles
+ map.graph.eachNode(function(n) {
+ if(n.id != node.id) delete n.selected;
+ n.setData('dim', 25, 'end');
+ n.eachAdjacency(function(adj) {
+ adj.setDataset('end', {
+ lineWidth: 0.5,
+ color: '#222222'
+ });
+ adj.setData('showDesc', false, 'current');
+ });
+ });
+ if(!node.selected) {
+ node.selected = true;
+ node.setData('dim', 35, 'end');
+ node.eachAdjacency(function(adj) {
+ adj.setDataset('end', {
+ lineWidth: 3,
+ color: '#FFF'
+ });
+ adj.setData('showDesc', true, 'current');
+ });
+ } else {
+ delete node.selected;
+ }
+ //trigger animation to final styles
+ map.fx.animate({
+ modes: ['node-property:dim',
+ 'edge-property:lineWidth:color'],
+ duration: 500
+ });
+ // Build the right column relations list.
+ // This is done by traversing the clicked node connections.
+ var html =
+ '' + node.getData("itemcatname") + '
' +
+ '' +
+ '' +
+ '' + node.getData('link') + '';
+
+ //append connections information
+ $jit.id('showcard').innerHTML = '';
+ $jit.id('item_' + node.id).innerHTML = html;
+ $("#showcard .scroll").mCustomScrollbar();
+ }
+ },
+ //Number of iterations for the FD algorithm
+ iterations: 200,
+ //Edge length
+ levelDistance: 200,
+ // Add text to the labels. This method is only triggered
+ // on label creation and only for DOM labels (not native canvas ones).
+ onCreateLabel: function(domElement, node){
+ // Create a 'name' and 'close' buttons and add them
+ // to the main node label
+ var nameContainer = document.createElement('span'),
+ style = nameContainer.style;
+ nameContainer.className = 'name';
+ nameContainer.innerHTML = '' + node.name + '
';
+ domElement.appendChild(nameContainer);
+ style.fontSize = "0.9em";
+ style.color = "#222222";
+ //Toggle a node selection when clicking
+ //its name. This is done by animating some
+ //node styles like its dimension and the color
+ //and lineWidth of its adjacencies.
+ nameContainer.onclick = function() {
+ //set final styles
+ map.graph.eachNode(function(n) {
+ if(n.id != node.id) delete n.selected;
+ n.setData('dim', 25, 'end');
+ n.eachAdjacency(function(adj) {
+ adj.setDataset('end', {
+ lineWidth: 0.4,
+ color: '#222222'
+ });
+ adj.setData('showDesc', false, 'current');
+ });
+ });
+ if(!node.selected) {
+ node.selected = true;
+ node.setData('dim', 35, 'end');
+ node.eachAdjacency(function(adj) {
+ adj.setDataset('end', {
+ lineWidth: 3,
+ color: '#FFF'
+ });
+ adj.setData('showDesc', true, 'current');
+ });
+ } else {
+ delete node.selected;
+ }
+ //trigger animation to final styles
+ map.fx.animate({
+ modes: ['node-property:dim',
+ 'edge-property:lineWidth:color'],
+ duration: 500
+ });
+ // Build the right column relations list.
+ // This is done by traversing the clicked node connections.
+ var html =
+ '' + node.getData("itemcatname") + '
' +
+ '' +
+ '' +
+ '' + node.getData('link') + '';
+
+ //append connections information
+ $jit.id('showcard').innerHTML = '';
+ $jit.id('item_' + node.id).innerHTML = html;
+ $("#showcard .scroll").mCustomScrollbar();
+ };
+ },
+ // Change node styles when DOM labels are placed
+ // or moved.
+ onPlaceLabel: function(domElement, node){
+ var style = domElement.style;
+ var left = parseInt(style.left);
+ var top = parseInt(style.top);
+ var w = domElement.offsetWidth;
+ var dim = node.getData('dim');
+ style.left = (left - w / 2) + 'px';
+ style.top = (top + dim) + 'px';
+ style.display = '';
+ }
+ });
+ // load JSON data.
+ map.loadJSON(json);
+ // compute positions incrementally and animate.
+ map.graph.eachNode(function(n) {
+ var pos = n.getPos();
+ pos.setc(0, 0);
+ var newPos = new $jit.Complex();
+ newPos.x = n.data.$xloc;
+ newPos.y = n.data.$yloc;
+ n.setPos(newPos, 'end');
+ });
+
+ $(document).ready(function() {
+ var coor = "";
+ $("#saveLayout").click(function(event) {
+ event.preventDefault();
+ coor = "";
+ map.graph.eachNode(function(n) {
+ coor = coor + n.data.$mappingid + '/' + n.pos.x + '/' + n.pos.y + ',';
+ });
+ coor = coor.slice(0, -1);
+ $('#map_coordinates').val(coor);
+ $('#saveMapLayout').submit();
+ });
+
+ map.animate({
+ modes: ['linear'],
+ transition: $jit.Trans.Quad.easeInOut,
+ duration: 2500
+ });
+ });
+ // end
+}
diff --git a/app/assets/javascripts/Jit/RGraph/metamapRG.js b/app/assets/javascripts/Jit/RGraph/metamapRG.js
index 115e21fd..723267c3 100644
--- a/app/assets/javascripts/Jit/RGraph/metamapRG.js
+++ b/app/assets/javascripts/Jit/RGraph/metamapRG.js
@@ -113,7 +113,8 @@ function initRG(){
//concentric circles.
background: {
CanvasStyles: {
- strokeStyle: '#555'
+ strokeStyle: '#333',
+ lineWidth: 1.5
}
},
//Enable zooming and panning
diff --git a/app/assets/javascripts/maps.js.coffee b/app/assets/javascripts/maps.js.coffee
index 76156794..2a5d8419 100644
--- a/app/assets/javascripts/maps.js.coffee
+++ b/app/assets/javascripts/maps.js.coffee
@@ -1,3 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
\ No newline at end of file
diff --git a/app/assets/stylesheets/maps.css.scss b/app/assets/stylesheets/maps.css.scss
index 9e4f4015..3fdbcb09 100644
--- a/app/assets/stylesheets/maps.css.scss
+++ b/app/assets/stylesheets/maps.css.scss
@@ -2,6 +2,12 @@
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
+.saveMapLayout {
+ margin:10px auto 5px auto;
+ width:87px;
+}
+
+
.map { display:block; float:left; position:relative; width:175px; height:300px; padding:10px 5px 10px 35px; background: url('bg.png'); border-radius:15px; margin:30px 0 30px 50px; color:#000; }
.map .delete {position: absolute;
diff --git a/app/controllers/items_controller.rb b/app/controllers/items_controller.rb
index fe2f679f..0c45a215 100644
--- a/app/controllers/items_controller.rb
+++ b/app/controllers/items_controller.rb
@@ -69,18 +69,21 @@ class ItemsController < ApplicationController
@item.save
end
+ @mapping = nil
if params[:item][:map]
@mapping = Mapping.new()
@mapping.category = "Item"
@mapping.user = @user
@mapping.map = Map.find(params[:item][:map])
@mapping.item = @item
+ @mapping.xloc = 0
+ @mapping.yloc = 0
@mapping.save
end
respond_to do |format|
format.html { respond_with(@user, location: user_item_url(@user, @item)) }
- format.js { respond_with(@item) }
+ format.js { respond_with(@item, @mapping) }
end
end
diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb
index 507dc47d..61aab663 100644
--- a/app/controllers/maps_controller.rb
+++ b/app/controllers/maps_controller.rb
@@ -1,6 +1,6 @@
class MapsController < ApplicationController
- before_filter :require_user, only: [:new, :create, :edit, :update]
+ before_filter :require_user, only: [:new, :create, :edit, :update, :savelayout]
respond_to :html, :js, :json
@@ -94,6 +94,25 @@ class MapsController < ApplicationController
end
+ # PUT /users/:user_id/maps/:id/savelayout
+ def savelayout
+ @user = User.find(params[:user_id])
+
+ @map = @user.maps.find(params[:id])
+
+ if params[:map][:coordinates]
+ @all = params[:map][:coordinates]
+ @all = @all.split(',')
+ @all.each do |item|
+ item = item.split('/')
+ @mapping = Mapping.find(item[0])
+ @mapping.xloc = item[1]
+ @mapping.yloc = item[2]
+ @mapping.save
+ end
+ end
+ end
+
# DELETE /users/:user_id/maps/:id
def destroy
@user = User.find(params[:user_id])
diff --git a/app/models/item.rb b/app/models/item.rb
index 95098467..425470b0 100644
--- a/app/models/item.rb
+++ b/app/models/item.rb
@@ -32,6 +32,11 @@ belongs_to :item_category
return result
end
+ ##### MAKE SEARCHABLE ######
+ def self.search(query)
+ self.where("(UPPER(name) LIKE UPPER(:query)", query: "%#{query}%").order(:name)
+ end
+
###### JSON ######
def self_as_json
Jbuilder.encode do |json|
diff --git a/app/models/map.rb b/app/models/map.rb
index 52c0f9c8..4ab76ba7 100644
--- a/app/models/map.rb
+++ b/app/models/map.rb
@@ -45,6 +45,10 @@ end
@itemdata['$itemcatname'] = item.item_category.name
@itemdata['$userid'] = item.user.id
@itemdata['$username'] = item.user.name
+ @mapping = Mapping.find_by_item_id_and_map_id(item.id,self.id)
+ @itemdata['$xloc'] = @mapping.xloc
+ @itemdata['$yloc'] = @mapping.yloc
+ @itemdata['$mappingid'] = @mapping.id
json.data @itemdata
json.id item.id
json.name item.name
diff --git a/app/views/items/create.js.erb b/app/views/items/create.js.erb
index d3284b3c..e5af4392 100644
--- a/app/views/items/create.js.erb
+++ b/app/views/items/create.js.erb
@@ -36,6 +36,23 @@ if (map2 != null) {
duration: 400
});
}
+ if (map != null) {
+ map.graph.addNode(newnode);
+ var temp = map.graph.getNode('<%= @item.id %>');
+ temp.setData('dim', 1, 'start');
+ temp.setData('dim', 40, 'end');
+ temp.setData('xloc',0);
+ temp.setData('yloc',0);
+ temp.setData('mappingid', '<%= @mapping.id %>');
+ temp.setPos(new $jit.Complex(0, 0), 'current');
+ temp.setPos(new $jit.Complex(0, 0), 'start');
+ temp.setPos(new $jit.Complex(0, 0), 'end');
+ map.fx.plotNode(temp, map.canvas);
+ map.fx.animate({
+ modes: ['node-property:dim'],
+ duration: 400
+ });
+ }
else {
json = newnode;
initFD();
diff --git a/app/views/maps/savelayout.js.erb b/app/views/maps/savelayout.js.erb
new file mode 100644
index 00000000..b6981d62
--- /dev/null
+++ b/app/views/maps/savelayout.js.erb
@@ -0,0 +1,2 @@
+$('.focusleft').append('Saved!
');
+$('#saveNotify').delay(1500).fadeOut('fast', function(){$('#saveNotify').remove()});
\ No newline at end of file
diff --git a/app/views/maps/show.html.erb b/app/views/maps/show.html.erb
index 4582fc31..f3a29e92 100644
--- a/app/views/maps/show.html.erb
+++ b/app/views/maps/show.html.erb
@@ -2,6 +2,12 @@
# of Topics: <%= @map.items.count %>
# of Synapses: <%= @map.synapses.count %>
+ <% if (@map.permission == "commons" && authenticated?) || @map.user == user %>
+ <%= form_for @map, :url => savelayout_path(@user,@map), :html => { :class => "saveMapLayout", :id => "saveMapLayout"}, remote: true do |form| %>
+ <%= form.hidden_field "coordinates", :value => "" %>
+ <%= form.submit "Save Layout", class: "saveLayout", id: "saveLayout" %>
+ <% end %>
+ <% end %>
<%= @map.name %> <% if (@map.permission == "commons" && authenticated?) || @map.user == user %><%= link_to "[edit]", edit_user_map_path(@user, @map) %><% end %>
@@ -28,10 +34,10 @@
json = <%= @mapjson %>;
if (json.length > 0) {
$(document).ready(function() {
- initFD();
+ initMAP();
});
}
<%= render :partial => 'newtopic' %>
-<%= render :partial => 'newsynapse' %>
+<%= render :partial => 'newsynapse' %>
\ No newline at end of file
diff --git a/config/database.yml b/config/database.yml
index 74caa69a..6705ca8f 100644
--- a/config/database.yml
+++ b/config/database.yml
@@ -1,32 +1,21 @@
development:
min_messages: WARNING
adapter: postgresql
- host: ec2-54-243-188-58.compute-1.amazonaws.com
- port: 5442
+ host: 127.0.0.1
+ port: 5432
encoding: unicode
- database: da81fvorvn5pp9
+ database: metamap002_development
pool: 5
- username: yolhqgtlivenrk
- password: "oi_EAZwbXboYjqB_9tWPh2x5YS"
-
-test:
- min_messages: WARNING
- adapter: postgresql
- host: ec2-54-243-188-58.compute-1.amazonaws.com
- port: 5442
- encoding: unicode
- database: da81fvorvn5pp9
- pool: 5
- username: yolhqgtlivenrk
- password: "oi_EAZwbXboYjqB_9tWPh2x5YS"
+ username: postgres
+ password: "3112"
production:
min_messages: WARNING
adapter: postgresql
- host: ec2-54-243-188-58.compute-1.amazonaws.com
- port: 5442
+ host: ec2-107-21-107-194.compute-1.amazonaws.com
+ port: 5432
encoding: unicode
- database: da81fvorvn5pp9
+ database: ddcrn2lgphjk9k
pool: 5
- username: yolhqgtlivenrk
- password: "oi_EAZwbXboYjqB_9tWPh2x5YS"
+ username: qqpckkahytovwv
+ password: "njeP6cMA8EjM9ukHk9s3ReOdy7"
diff --git a/config/routes.rb b/config/routes.rb
index 8d465aeb..ddb701cf 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -7,6 +7,8 @@ ISSAD::Application.routes.draw do
match 'invite', to: 'main#invite', via: :get, as: :invite
+ match 'users/:user_id/maps/:id/savelayout', to: 'maps#savelayout', via: :put, as: :savelayout
+
resource :session
resources :users do