diff --git a/app/assets/images/screenshot_sprite.png b/app/assets/images/screenshot_sprite.png new file mode 100644 index 00000000..d3c87c5a Binary files /dev/null and b/app/assets/images/screenshot_sprite.png differ diff --git a/app/assets/javascripts/src/Metamaps.JIT.js b/app/assets/javascripts/src/Metamaps.JIT.js index faf895f2..06ed616f 100644 --- a/app/assets/javascripts/src/Metamaps.JIT.js +++ b/app/assets/javascripts/src/Metamaps.JIT.js @@ -20,7 +20,13 @@ Metamaps.JIT = { $(".zoomIn").click(self.zoomIn); $(".zoomOut").click(self.zoomOut); - $(".zoomExtents").click(self.zoomExtents); + + var zoomExtents = function (event) { + self.zoomExtents(event, Metamaps.Visualize.mGraph.canvas); + }; + $(".zoomExtents").click(zoomExtents); + + $(".takeScreenshot").click(Metamaps.Map.exportImage); self.synapseStarImage = new Image(); self.synapseStarImage.src = '/assets/synapsestar.png'; @@ -123,7 +129,11 @@ Metamaps.JIT = { var directionCat = synapse.get("category"); //label placement on edges - Metamaps.JIT.renderEdgeArrows($jit.Graph.Plot.edgeHelper, adj, synapse); + if (canvas.denySelected) { + var color = Metamaps.Settings.colors.synapses.normal; + canvas.getCtx().fillStyle = canvas.getCtx().strokeStyle = color; + } + Metamaps.JIT.renderEdgeArrows($jit.Graph.Plot.edgeHelper, adj, synapse, canvas); //check for edge label in data var desc = synapse.get("desc"); @@ -140,7 +150,7 @@ Metamaps.JIT = { } }; - if (desc != "" && showDesc) { + if (!canvas.denySelected && desc != "" && showDesc) { // '&' to '&' desc = Metamaps.Util.decodeEntities(desc); @@ -188,7 +198,7 @@ Metamaps.JIT = { drawStar(ctx, x + width, y); } } - else if (showDesc) { + else if (!canvas.denySelected && showDesc) { if (adj.getData("synapses").length > 1) { var ctx = canvas.getCtx(); var x = (pos.x + posChild.x) / 2; @@ -400,7 +410,7 @@ Metamaps.JIT = { ctx = canvas.getCtx(); // if the topic is selected draw a circle around it - if (node.selected) { + if (!canvas.denySelected && node.selected) { ctx.beginPath(); ctx.arc(pos.x, pos.y, dim + 3, 0, 2 * Math.PI, false); ctx.strokeStyle = Metamaps.Settings.colors.topics.selected; @@ -1595,12 +1605,10 @@ Metamaps.JIT = { ctx.lineTo(v2.x, v2.y); ctx.stroke(); }, // renderMidArrow - renderEdgeArrows: function (edgeHelper, adj, synapse) { + renderEdgeArrows: function (edgeHelper, adj, synapse, canvas) { var self = Metamaps.JIT; - var canvas = Metamaps.Visualize.mGraph.canvas; - var directionCat = synapse.get('category'); var direction = synapse.getDirection(); @@ -1657,8 +1665,7 @@ Metamaps.JIT = { Metamaps.Visualize.mGraph.canvas.scale(0.8,0.8); $(document).trigger(Metamaps.JIT.events.zoom, [event]); }, - centerMap: function () { - var canvas = Metamaps.Visualize.mGraph.canvas; + centerMap: function (canvas) { var offsetScale = canvas.scaleOffsetX; canvas.scale(1/offsetScale,1/offsetScale); @@ -1674,7 +1681,8 @@ Metamaps.JIT = { eX = Metamaps.Mouse.boxEndCoordinates.x, eY = Metamaps.Mouse.boxEndCoordinates.y; - Metamaps.JIT.centerMap(); + var canvas = Metamaps.Visualize.mGraph.canvas; + Metamaps.JIT.centerMap(canvas); var height = $(document).height(), width = $(document).width(); @@ -1686,8 +1694,6 @@ Metamaps.JIT = { var newRatio = Math.min(ratioX,ratioY); - var canvas = Metamaps.Visualize.mGraph.canvas; - if(canvas.scaleOffsetX *newRatio<= 5 && canvas.scaleOffsetX*newRatio >= 0.2){ canvas.scale(newRatio,newRatio); } @@ -1711,15 +1717,14 @@ Metamaps.JIT = { Metamaps.Visualize.mGraph.plot(); }, - zoomExtents: function (event) { - Metamaps.JIT.centerMap(); - var height = $(document).height(), - width = $(document).width(), + zoomExtents: function (event, canvas, denySelected) { + Metamaps.JIT.centerMap(canvas); + var height = canvas.getSize().height, + width = canvas.getSize().width, maxX, minX, maxY, minY, counter = 0; - var canvas = Metamaps.Visualize.mGraph.canvas; - if (Metamaps.Selected.Nodes.length > 0) { + if (!denySelected && Metamaps.Selected.Nodes.length > 0) { var nodes = Metamaps.Selected.Nodes; } else { diff --git a/app/assets/javascripts/src/Metamaps.Router.js b/app/assets/javascripts/src/Metamaps.Router.js index aa4fa338..466326d2 100644 --- a/app/assets/javascripts/src/Metamaps.Router.js +++ b/app/assets/javascripts/src/Metamaps.Router.js @@ -125,7 +125,7 @@ if (Metamaps.Visualize.mGraph) { Metamaps.Visualize.mGraph.graph.empty(); Metamaps.Visualize.mGraph.plot(); - Metamaps.JIT.centerMap(); + Metamaps.JIT.centerMap(Metamaps.Visualize.mGraph.canvas); } Metamaps.Famous.viz.show(); Metamaps.Topic.end(); @@ -156,7 +156,7 @@ if (Metamaps.Visualize.mGraph) { Metamaps.Visualize.mGraph.graph.empty(); Metamaps.Visualize.mGraph.plot(); - Metamaps.JIT.centerMap(); + Metamaps.JIT.centerMap(Metamaps.Visualize.mGraph.canvas); } Metamaps.Famous.viz.show(); Metamaps.Map.end(); diff --git a/app/assets/javascripts/src/Metamaps.js b/app/assets/javascripts/src/Metamaps.js index 2543f0df..7e03ea4e 100644 --- a/app/assets/javascripts/src/Metamaps.js +++ b/app/assets/javascripts/src/Metamaps.js @@ -3384,7 +3384,7 @@ Metamaps.Listeners = { case 69: //if e or E is pressed if (e.ctrlKey){ e.preventDefault(); - Metamaps.JIT.zoomExtents(); + Metamaps.JIT.zoomExtents(null, Metamaps.Visualize.mGraph.canvas); } break; case 77: //if m or M is pressed @@ -4203,6 +4203,130 @@ Metamaps.Map = { Metamaps.Map.sideLength = 1; Metamaps.Map.timeToTurn = 0; Metamaps.Map.turnCount = 0; + }, + exportImage: function() { + + var canvas = {}; + + canvas.canvas = document.createElement("canvas"); + canvas.canvas.width = 1880; // 960; + canvas.canvas.height = 1260; // 630 + + canvas.scaleOffsetX = 1; + canvas.scaleOffsetY = 1; + canvas.translateOffsetY = 0; + canvas.translateOffsetX = 0; + canvas.denySelected = true; + + canvas.getSize = function() { + if(this.size) return this.size; + var canvas = this.canvas; + return this.size = { + width: canvas.width, + height: canvas.height + }; + }; + canvas.scale = function(x, y) { + var px = this.scaleOffsetX * x, + py = this.scaleOffsetY * y; + var dx = this.translateOffsetX * (x -1) / px, + dy = this.translateOffsetY * (y -1) / py; + this.scaleOffsetX = px; + this.scaleOffsetY = py; + this.getCtx().scale(x, y); + this.translate(dx, dy); + }; + canvas.translate = function(x, y) { + var sx = this.scaleOffsetX, + sy = this.scaleOffsetY; + this.translateOffsetX += x*sx; + this.translateOffsetY += y*sy; + this.getCtx().translate(x, y); + }; + canvas.getCtx = function() { + return this.canvas.getContext("2d"); + }; + // center it + canvas.getCtx().translate(1880/2, 1260/2); + + var mGraph = Metamaps.Visualize.mGraph; + + var id = mGraph.root; + var root = mGraph.graph.getNode(id); + var T = !!root.visited; + + // pass true to avoid basing it on a selection + Metamaps.JIT.zoomExtents(null, canvas, true); + + var c = canvas.canvas, + ctx = canvas.getCtx(), + scale = canvas.scaleOffsetX; + + // draw a grey background + ctx.fillStyle = '#d8d9da'; + var xPoint = (-(c.width/scale)/2) - (canvas.translateOffsetX/scale), + yPoint = (-(c.height/scale)/2) - (canvas.translateOffsetY/scale); + ctx.fillRect(xPoint,yPoint,c.width/scale,c.height/scale); + + // draw the graph + mGraph.graph.eachNode(function(node) { + var nodeAlpha = node.getData('alpha'); + node.eachAdjacency(function(adj) { + var nodeTo = adj.nodeTo; + if(!!nodeTo.visited === T && node.drawn && nodeTo.drawn) { + mGraph.fx.plotLine(adj, canvas); + } + }); + if(node.drawn) { + mGraph.fx.plotNode(node, canvas); + } + if(!mGraph.labelsHidden) { + if(node.drawn && nodeAlpha >= 0.95) { + mGraph.labels.plotLabel(canvas, node); + } else { + mGraph.labels.hideLabel(node, false); + } + } + node.visited = !T; + }); + + var imageData = { + encoded_image: canvas.canvas.toDataURL() + }; + + console.log(imageData.encoded_image); + var map = Metamaps.Active.Map; + + var today = new Date(); + var dd = today.getDate(); + var mm = today.getMonth()+1; //January is 0! + var yyyy = today.getFullYear(); + if(dd<10) { + dd='0'+dd + } + if(mm<10) { + mm='0'+mm + } + today = mm+'/'+dd+'/'+yyyy; + + var downloadMessage = ""; + downloadMessage += "Captured map screenshot! "; + downloadMessage += "DOWNLOAD"; + Metamaps.GlobalUI.notifyUser(downloadMessage); + + $.ajax({ + type: "POST", + dataType: 'json', + url: "/maps/" + Metamaps.Active.Map.id + "/upload_screenshot", + data: imageData, + success: function (data) { + console.log('successfully uploaded map screenshot'); + }, + error: function () { + console.log('failed to save map screenshot'); + } + }); } }; diff --git a/app/assets/stylesheets/clean.css b/app/assets/stylesheets/clean.css index 5ebcb763..847ec0ed 100644 --- a/app/assets/stylesheets/clean.css +++ b/app/assets/stylesheets/clean.css @@ -763,6 +763,18 @@ background-position: 0 0; cursor:pointer; } +.takeScreenshot { + margin-bottom: 5px; + border-radius: 2px; + background-image: url(screenshot_sprite.png); + display: none; +} +.takeScreenshot:hover { + background-position: -32px 0; +} +.canEditMap .takeScreenshot { + display: block; +} .zoomExtents { margin-bottom:5px; border-radius: 2px; @@ -883,6 +895,10 @@ line-height:14px; } +.toast a { + color: #4fc059; +} + /* end toast */ /* feedback */ diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb index 0fbad24b..fbf34abc 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: [:create, :update, :destroy] + before_filter :require_user, only: [:create, :update, :screenshot, :destroy] respond_to :html, :json @@ -183,6 +183,30 @@ class MapsController < ApplicationController end end + # POST maps/:id/upload_screenshot + def screenshot + @current = current_user + @map = Map.find(params[:id]).authorize_to_edit(@current) + + if @map + png = Base64.decode64(params[:encoded_image]['data:image/png;base64,'.length .. -1]) + StringIO.open(png) do |data| + data.class.class_eval { attr_accessor :original_filename, :content_type } + data.original_filename = "map-" + @map.id.to_s + "-screenshot.png" + data.content_type = "image/png" + @map.screenshot = data + end + + if @map.save + render :json => {:message => "Successfully uploaded the map screenshot."} + else + render :json => {:message => "Failed to upload image."} + end + else + render :json => {:message => "Unauthorized to set map screenshot."} + end + end + # DELETE maps/:id def destroy @current = current_user diff --git a/app/models/map.rb b/app/models/map.rb index fcfefc81..91bc0a11 100644 --- a/app/models/map.rb +++ b/app/models/map.rb @@ -8,12 +8,12 @@ class Map < ActiveRecord::Base has_many :topics, :through => :topicmappings has_many :synapses, :through => :synapsemappings - after_touch :save_screenshot + #after_touch :save_screenshot # This method associates the attribute ":image" with a file attachment has_attached_file :screenshot, :styles => { - :thumb => ['188x126#', :png], - :full => ['940x630#', :png] + :thumb => ['188x126#', :png] + #:full => ['940x630#', :png] }, :default_url => "/assets/missing-map.png" diff --git a/app/views/layouts/_lowermapelements.html.erb b/app/views/layouts/_lowermapelements.html.erb index c69d475f..42b1820a 100644 --- a/app/views/layouts/_lowermapelements.html.erb +++ b/app/views/layouts/_lowermapelements.html.erb @@ -1,4 +1,5 @@
+
diff --git a/config/routes.rb b/config/routes.rb index 5031b431..edc19576 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,6 +30,7 @@ Metamaps::Application.routes.draw do match 'maps/topics/:id', to: 'maps#index', via: :get, as: :topicmaps resources :maps, except: [:new, :edit] match 'maps/:id/contains', to: 'maps#contains', via: :get, as: :contains + match 'maps/:id/upload_screenshot', to: 'maps#screenshot', via: :post, as: :screenshot devise_for :users, controllers: { registrations: 'users/registrations', passwords: 'users/passwords', sessions: 'devise/sessions' }, :skip => [:sessions]