diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 784395de..b25950b0 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -25,6 +25,7 @@
//= require ./src/views/room
//= require ./src/JIT
//= require ./src/Metamaps
+//= require ./src/Metamaps.Map
//= require ./src/Metamaps.Account
//= require ./src/Metamaps.Mapper
//= require ./src/Metamaps.Admin
diff --git a/app/assets/javascripts/src/Metamaps.Map.js.erb b/app/assets/javascripts/src/Metamaps.Map.js.erb
new file mode 100644
index 00000000..0207d081
--- /dev/null
+++ b/app/assets/javascripts/src/Metamaps.Map.js.erb
@@ -0,0 +1,667 @@
+/* global Metamaps, $ */
+
+/*
+ * Metamaps.Map.js.erb
+ *
+ * Dependencies:
+ * Metamaps.Create
+ * Metamaps.Filter
+ * Metamaps.JIT
+ * Metamaps.Loading
+ * Metamaps.Maps
+ * Metamaps.Realtime
+ * Metamaps.Router
+ * Metamaps.Selected
+ * Metamaps.SynapseCard
+ * Metamaps.TopicCard
+ * Metamaps.Visualize
+ * - Metamaps.Active
+ * - Metamaps.Backbone
+ * - Metamaps.GlobalUI
+ * - Metamaps.Mappers
+ * - Metamaps.Mappings
+ * - Metamaps.Messages
+ * - Metamaps.Synapses
+ * - Metamaps.Topics
+ *
+ * Major sub-modules:
+ * - Metamaps.Map.CheatSheet
+ * - Metamaps.Map.InfoBox
+ */
+
+Metamaps.Map = {
+ events: {
+ editedByActiveMapper: 'Metamaps:Map:events:editedByActiveMapper'
+ },
+ nextX: 0,
+ nextY: 0,
+ sideLength: 1,
+ turnCount: 0,
+ nextXshift: 1,
+ nextYshift: 0,
+ timeToTurn: 0,
+ init: function () {
+ var self = Metamaps.Map
+
+ // prevent right clicks on the main canvas, so as to not get in the way of our right clicks
+ $('#center-container').bind('contextmenu', function (e) {
+ return false
+ })
+
+ $('.sidebarFork').click(function () {
+ self.fork()
+ })
+
+ Metamaps.GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html()
+
+ self.InfoBox.init()
+ self.CheatSheet.init()
+
+ $(document).on(Metamaps.Map.events.editedByActiveMapper, self.editedByActiveMapper)
+ },
+ launch: function (id) {
+ var bb = Metamaps.Backbone
+ var start = function (data) {
+ Metamaps.Active.Map = new bb.Map(data.map)
+ Metamaps.Mappers = new bb.MapperCollection(data.mappers)
+ Metamaps.Topics = new bb.TopicCollection(data.topics)
+ Metamaps.Synapses = new bb.SynapseCollection(data.synapses)
+ Metamaps.Mappings = new bb.MappingCollection(data.mappings)
+ Metamaps.Messages = data.messages
+ Metamaps.Backbone.attachCollectionEvents()
+
+ var map = Metamaps.Active.Map
+ var mapper = Metamaps.Active.Mapper
+
+ // add class to .wrapper for specifying whether you can edit the map
+ if (map.authorizeToEdit(mapper)) {
+ $('.wrapper').addClass('canEditMap')
+ }
+
+ // add class to .wrapper for specifying if the map can
+ // be collaborated on
+ if (map.get('permission') === 'commons') {
+ $('.wrapper').addClass('commonsMap')
+ }
+
+ // set filter mapper H3 text
+ $('#filter_by_mapper h3').html('MAPPERS')
+
+ // build and render the visualization
+ Metamaps.Visualize.type = 'ForceDirected'
+ Metamaps.JIT.prepareVizData()
+
+ // update filters
+ Metamaps.Filter.reset()
+
+ // reset selected arrays
+ Metamaps.Selected.reset()
+
+ // set the proper mapinfobox content
+ Metamaps.Map.InfoBox.load()
+
+ // these three update the actual filter box with the right list items
+ Metamaps.Filter.checkMetacodes()
+ Metamaps.Filter.checkSynapses()
+ Metamaps.Filter.checkMappers()
+
+ Metamaps.Realtime.startActiveMap()
+ Metamaps.Loading.hide()
+ }
+
+ $.ajax({
+ url: '/maps/' + id + '/contains.json',
+ success: start
+ })
+ },
+ end: function () {
+ if (Metamaps.Active.Map) {
+ $('.wrapper').removeClass('canEditMap commonsMap')
+ Metamaps.Map.resetSpiral()
+
+ $('.rightclickmenu').remove()
+ Metamaps.TopicCard.hideCard()
+ Metamaps.SynapseCard.hideCard()
+ Metamaps.Create.newTopic.hide()
+ Metamaps.Create.newSynapse.hide()
+ Metamaps.Filter.close()
+ Metamaps.Map.InfoBox.close()
+ Metamaps.Realtime.endActiveMap()
+ }
+ },
+ fork: function () {
+ Metamaps.GlobalUI.openLightbox('forkmap')
+
+ var nodes_data = '',
+ synapses_data = ''
+ var nodes_array = []
+ var synapses_array = []
+ // collect the unfiltered topics
+ Metamaps.Visualize.mGraph.graph.eachNode(function (n) {
+ // if the opacity is less than 1 then it's filtered
+ if (n.getData('alpha') === 1) {
+ var id = n.getData('topic').id
+ nodes_array.push(id)
+ var x, y
+ if (n.pos.x && n.pos.y) {
+ x = n.pos.x
+ y = n.pos.y
+ } else {
+ var x = Math.cos(n.pos.theta) * n.pos.rho
+ var y = Math.sin(n.pos.theta) * n.pos.rho
+ }
+ nodes_data += id + '/' + x + '/' + y + ','
+ }
+ })
+ // collect the unfiltered synapses
+ Metamaps.Synapses.each(function (synapse) {
+ var desc = synapse.get('desc')
+
+ var descNotFiltered = Metamaps.Filter.visible.synapses.indexOf(desc) > -1
+ // make sure that both topics are being added, otherwise, it
+ // doesn't make sense to add the synapse
+ var topicsNotFiltered = nodes_array.indexOf(synapse.get('node1_id')) > -1
+ topicsNotFiltered = topicsNotFiltered && nodes_array.indexOf(synapse.get('node2_id')) > -1
+ if (descNotFiltered && topicsNotFiltered) {
+ synapses_array.push(synapse.id)
+ }
+ })
+
+ synapses_data = synapses_array.join()
+ nodes_data = nodes_data.slice(0, -1)
+
+ Metamaps.GlobalUI.CreateMap.topicsToMap = nodes_data
+ Metamaps.GlobalUI.CreateMap.synapsesToMap = synapses_data
+ },
+ leavePrivateMap: function () {
+ var map = Metamaps.Active.Map
+ Metamaps.Maps.Active.remove(map)
+ Metamaps.Maps.Featured.remove(map)
+ Metamaps.Router.home()
+ Metamaps.GlobalUI.notifyUser('Sorry! That map has been changed to Private.')
+ },
+ commonsToPublic: function () {
+ Metamaps.Realtime.turnOff(true); // true is for 'silence'
+ Metamaps.GlobalUI.notifyUser('Map was changed to Public. Editing is disabled.')
+ Metamaps.Active.Map.trigger('changeByOther')
+ },
+ publicToCommons: function () {
+ var confirmString = 'This map permission has been changed to Commons! '
+ confirmString += 'Do you want to reload and enable realtime collaboration?'
+ var c = confirm(confirmString)
+ if (c) {
+ Metamaps.Router.maps(Metamaps.Active.Map.id)
+ }
+ },
+ editedByActiveMapper: function () {
+ if (Metamaps.Active.Mapper) {
+ Metamaps.Mappers.add(Metamaps.Active.Mapper)
+ }
+ },
+ getNextCoord: function () {
+ var self = Metamaps.Map
+ var nextX = self.nextX
+ var nextY = self.nextY
+
+ var DISTANCE_BETWEEN = 120
+
+ self.nextX = self.nextX + DISTANCE_BETWEEN * self.nextXshift
+ self.nextY = self.nextY + DISTANCE_BETWEEN * self.nextYshift
+
+ self.timeToTurn += 1
+ // if true, it's time to turn
+ if (self.timeToTurn === self.sideLength) {
+ self.turnCount += 1
+ // if true, it's time to increase side length
+ if (self.turnCount % 2 === 0) {
+ self.sideLength += 1
+ }
+ self.timeToTurn = 0
+
+ // going right? turn down
+ if (self.nextXshift == 1 && self.nextYshift == 0) {
+ self.nextXshift = 0
+ self.nextYshift = 1
+ }
+ // going down? turn left
+ else if (self.nextXshift == 0 && self.nextYshift == 1) {
+ self.nextXshift = -1
+ self.nextYshift = 0
+ }
+ // going left? turn up
+ else if (self.nextXshift == -1 && self.nextYshift == 0) {
+ self.nextXshift = 0
+ self.nextYshift = -1
+ }
+ // going up? turn right
+ else if (self.nextXshift == 0 && self.nextYshift == -1) {
+ self.nextXshift = 1
+ self.nextYshift = 0
+ }
+ }
+
+ return {
+ x: nextX,
+ y: nextY
+ }
+ },
+ resetSpiral: function () {
+ Metamaps.Map.nextX = 0
+ Metamaps.Map.nextY = 0
+ Metamaps.Map.nextXshift = 1
+ Metamaps.Map.nextYshift = 0
+ 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()
+ }
+
+ 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 mapName = map.get('name').split(' ').join([separator = '-'])
+ 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')
+ }
+ })
+ }
+}
+
+/*
+ *
+ * CHEATSHEET
+ *
+ */
+Metamaps.Map.CheatSheet = {
+ init: function () {
+ // tab the cheatsheet
+ $('#cheatSheet').tabs()
+ $('#quickReference').tabs().addClass('ui-tabs-vertical ui-helper-clearfix')
+ $('#quickReference .ui-tabs-nav li').removeClass('ui-corner-top').addClass('ui-corner-left')
+
+ // id = the id of a vimeo video
+ var switchVideo = function (element, id) {
+ $('.tutorialItem').removeClass('active')
+ $(element).addClass('active')
+ $('#tutorialVideo').attr('src', '//player.vimeo.com/video/' + id)
+ }
+
+ $('#gettingStarted').click(function () {
+ // switchVideo(this,'88334167')
+ })
+ $('#upYourSkillz').click(function () {
+ // switchVideo(this,'100118167')
+ })
+ $('#advancedMapping').click(function () {
+ // switchVideo(this,'88334167')
+ })
+ }
+}; // end Metamaps.Map.CheatSheet
+
+/*
+ *
+ * INFOBOX
+ *
+ */
+Metamaps.Map.InfoBox = {
+ isOpen: false,
+ changing: false,
+ selectingPermission: false,
+ changePermissionText: "
As the creator, you can change the permission of this map, but the permissions of the topics and synapses on it must be changed independently.
",
+ nameHTML: '{{name}}',
+ descHTML: '{{desc}}',
+ init: function () {
+ var self = Metamaps.Map.InfoBox
+
+ $('.mapInfoIcon').click(self.toggleBox)
+ $('.mapInfoBox').click(function (event) {
+ event.stopPropagation()
+ })
+ $('body').click(self.close)
+
+ self.attachEventListeners()
+
+ self.generateBoxHTML = Hogan.compile($('#mapInfoBoxTemplate').html())
+ },
+ toggleBox: function (event) {
+ var self = Metamaps.Map.InfoBox
+
+ if (self.isOpen) self.close()
+ else self.open()
+
+ event.stopPropagation()
+ },
+ open: function () {
+ var self = Metamaps.Map.InfoBox
+ $('.mapInfoIcon div').addClass('hide')
+ if (!self.isOpen && !self.changing) {
+ self.changing = true
+ $('.mapInfoBox').fadeIn(200, function () {
+ self.changing = false
+ self.isOpen = true
+ })
+ }
+ },
+ close: function () {
+ var self = Metamaps.Map.InfoBox
+
+ $('.mapInfoIcon div').removeClass('hide')
+ if (!self.changing) {
+ self.changing = true
+ $('.mapInfoBox').fadeOut(200, function () {
+ self.changing = false
+ self.isOpen = false
+ self.hidePermissionSelect()
+ $('.mapContributors .tip').hide()
+ })
+ }
+ },
+ load: function () {
+ var self = Metamaps.Map.InfoBox
+
+ var map = Metamaps.Active.Map
+
+ var obj = map.pick('permission', 'contributor_count', 'topic_count', 'synapse_count')
+
+ var isCreator = map.authorizePermissionChange(Metamaps.Active.Mapper)
+ var canEdit = map.authorizeToEdit(Metamaps.Active.Mapper)
+ var shareable = map.get('permission') !== 'private'
+
+ obj['name'] = canEdit ? Hogan.compile(self.nameHTML).render({id: map.id, name: map.get('name')}) : map.get('name')
+ obj['desc'] = canEdit ? Hogan.compile(self.descHTML).render({id: map.id, desc: map.get('desc')}) : map.get('desc')
+ obj['map_creator_tip'] = isCreator ? self.changePermissionText : ''
+ obj['contributors_class'] = Metamaps.Mappers.length > 1 ? 'multiple' : ''
+ obj['contributors_class'] += Metamaps.Mappers.length === 2 ? ' mTwo' : ''
+ obj['contributor_image'] = Metamaps.Mappers.length > 0 ? Metamaps.Mappers.models[0].get('image') : "<%= asset_path('user.png') %>"
+ obj['contributor_list'] = self.createContributorList()
+ obj['user_name'] = isCreator ? 'You' : map.get('user_name')
+ obj['created_at'] = map.get('created_at_clean')
+ obj['updated_at'] = map.get('updated_at_clean')
+
+ var classes = isCreator ? 'yourMap' : ''
+ classes += canEdit ? ' canEdit' : ''
+ classes += shareable ? ' shareable' : ''
+ $('.mapInfoBox').removeClass('shareable yourMap canEdit')
+ .addClass(classes)
+ .html(self.generateBoxHTML.render(obj))
+
+ self.attachEventListeners()
+ },
+ attachEventListeners: function () {
+ var self = Metamaps.Map.InfoBox
+
+ $('.mapInfoBox.canEdit .best_in_place').best_in_place()
+
+ // because anyone who can edit the map can change the map title
+ var bipName = $('.mapInfoBox .best_in_place_name')
+ bipName.unbind('best_in_place:activate').bind('best_in_place:activate', function () {
+ var $el = bipName.find('textarea')
+ var el = $el[0]
+
+ $el.attr('maxlength', '140')
+
+ $('.mapInfoName').append('')
+
+ var callback = function (data) {
+ $('.nameCounter.forMap').html(data.all + '/140')
+ }
+ Countable.live(el, callback)
+ })
+ bipName.unbind('best_in_place:deactivate').bind('best_in_place:deactivate', function () {
+ $('.nameCounter.forMap').remove()
+ })
+
+ $('.mapInfoName .best_in_place_name').unbind('ajax:success').bind('ajax:success', function () {
+ var name = $(this).html()
+ Metamaps.Active.Map.set('name', name)
+ Metamaps.Active.Map.trigger('saved')
+ })
+
+ $('.mapInfoDesc .best_in_place_desc').unbind('ajax:success').bind('ajax:success', function () {
+ var desc = $(this).html()
+ Metamaps.Active.Map.set('desc', desc)
+ Metamaps.Active.Map.trigger('saved')
+ })
+
+ $('.yourMap .mapPermission').unbind().click(self.onPermissionClick)
+ // .yourMap in the unbind/bind is just a namespace for the events
+ // not a reference to the class .yourMap on the .mapInfoBox
+ $('.mapInfoBox.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect)
+
+ $('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap)
+
+ $('.mapContributors span, #mapContribs').unbind().click(function (event) {
+ $('.mapContributors .tip').toggle()
+ event.stopPropagation()
+ })
+ $('.mapContributors .tip').unbind().click(function (event) {
+ event.stopPropagation()
+ })
+ $('.mapContributors .tip li a').click(Metamaps.Router.intercept)
+
+ $('.mapInfoBox').unbind('.hideTip').bind('click.hideTip', function () {
+ $('.mapContributors .tip').hide()
+ })
+ },
+ updateNameDescPerm: function (name, desc, perm) {
+ $('.mapInfoName .best_in_place_name').html(name)
+ $('.mapInfoDesc .best_in_place_desc').html(desc)
+ $('.mapInfoBox .mapPermission').removeClass('commons public private').addClass(perm)
+ },
+ createContributorList: function () {
+ var self = Metamaps.Map.InfoBox
+
+ var string = ''
+ string += ''
+ return string
+ },
+ updateNumbers: function () {
+ var self = Metamaps.Map.InfoBox
+ var mapper = Metamaps.Active.Mapper
+
+ var contributors_class = ''
+ if (Metamaps.Mappers.length === 2) contributors_class = 'multiple mTwo'
+ else if (Metamaps.Mappers.length > 2) contributors_class = 'multiple'
+
+ var contributors_image = "<%= asset_path('user.png') %>"
+ if (Metamaps.Mappers.length > 0) {
+ // get the first contributor and use their image
+ contributors_image = Metamaps.Mappers.models[0].get('image')
+ }
+ $('.mapContributors img').attr('src', contributors_image).removeClass('multiple mTwo').addClass(contributors_class)
+ $('.mapContributors span').text(Metamaps.Mappers.length)
+ $('.mapContributors .tip').html(self.createContributorList())
+ $('.mapTopics').text(Metamaps.Topics.length)
+ $('.mapSynapses').text(Metamaps.Synapses.length)
+
+ $('.mapEditedAt').html('Last edited: ' + Metamaps.Util.nowDateFormatted())
+ },
+ onPermissionClick: function (event) {
+ var self = Metamaps.Map.InfoBox
+
+ if (!self.selectingPermission) {
+ self.selectingPermission = true
+ $(this).addClass('minimize') // this line flips the drop down arrow to a pull up arrow
+ if ($(this).hasClass('commons')) {
+ $(this).append('')
+ } else if ($(this).hasClass('public')) {
+ $(this).append('')
+ } else if ($(this).hasClass('private')) {
+ $(this).append('')
+ }
+ $('.mapPermission .permissionSelect li').click(self.selectPermission)
+ event.stopPropagation()
+ }
+ },
+ hidePermissionSelect: function () {
+ var self = Metamaps.Map.InfoBox
+
+ self.selectingPermission = false
+ $('.mapPermission').removeClass('minimize') // this line flips the pull up arrow to a drop down arrow
+ $('.mapPermission .permissionSelect').remove()
+ },
+ selectPermission: function (event) {
+ var self = Metamaps.Map.InfoBox
+
+ self.selectingPermission = false
+ var permission = $(this).attr('class')
+ var permBefore = Metamaps.Active.Map.get('permission')
+ Metamaps.Active.Map.save({
+ permission: permission
+ })
+ Metamaps.Active.Map.updateMapWrapper()
+ if (permBefore !== 'commons' && permission === 'commons') {
+ Metamaps.Realtime.setupSocket()
+ Metamaps.Realtime.turnOn()
+ }
+ else if (permBefore === 'commons' && permission === 'public') {
+ Metamaps.Realtime.turnOff(true); // true is to 'silence'
+ // the notification that would otherwise be sent
+ }
+ shareable = permission === 'private' ? '' : 'shareable'
+ $('.mapPermission').removeClass('commons public private minimize').addClass(permission)
+ $('.mapPermission .permissionSelect').remove()
+ $('.mapInfoBox').removeClass('shareable').addClass(shareable)
+ event.stopPropagation()
+ },
+ deleteActiveMap: function () {
+ var confirmString = 'Are you sure you want to delete this map? '
+ confirmString += 'This action is irreversible. It will not delete the topics and synapses on the map.'
+
+ var doIt = confirm(confirmString)
+ var map = Metamaps.Active.Map
+ var mapper = Metamaps.Active.Mapper
+ var authorized = map.authorizePermissionChange(mapper)
+
+ if (doIt && authorized) {
+ Metamaps.Map.InfoBox.close()
+ Metamaps.Maps.Active.remove(map)
+ Metamaps.Maps.Featured.remove(map)
+ Metamaps.Maps.Mine.remove(map)
+ map.destroy()
+ Metamaps.Router.home()
+ Metamaps.GlobalUI.notifyUser('Map eliminated!')
+ }
+ else if (!authorized) {
+ alert("Hey now. We can't just go around willy nilly deleting other people's maps now can we? Run off and find something constructive to do, eh?")
+ }
+ }
+}; // end Metamaps.Map.InfoBox
diff --git a/app/assets/javascripts/src/Metamaps.js.erb b/app/assets/javascripts/src/Metamaps.js.erb
index dbcb5b8a..c2585d76 100644
--- a/app/assets/javascripts/src/Metamaps.js.erb
+++ b/app/assets/javascripts/src/Metamaps.js.erb
@@ -4729,652 +4729,3 @@ Metamaps.Synapse = {
self.renderSynapse(mapping, synapse, node1, node2, true);
}
}; // end Metamaps.Synapse
-
-
-/*
- *
- * MAP
- *
- */
-Metamaps.Map = {
- events: {
- editedByActiveMapper: "Metamaps:Map:events:editedByActiveMapper"
- },
- nextX: 0,
- nextY: 0,
- sideLength: 1,
- turnCount: 0,
- nextXshift: 1,
- nextYshift: 0,
- timeToTurn: 0,
- init: function () {
- var self = Metamaps.Map;
-
- // prevent right clicks on the main canvas, so as to not get in the way of our right clicks
- $('#center-container').bind('contextmenu', function (e) {
- return false;
- });
-
- $('.sidebarFork').click(function () {
- self.fork();
- });
-
- Metamaps.GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html();
-
- self.InfoBox.init();
- self.CheatSheet.init();
-
- $(document).on(Metamaps.Map.events.editedByActiveMapper, self.editedByActiveMapper);
- },
- launch: function (id) {
- var bb = Metamaps.Backbone;
- var start = function (data) {
- Metamaps.Active.Map = new bb.Map(data.map);
- Metamaps.Mappers = new bb.MapperCollection(data.mappers);
- Metamaps.Topics = new bb.TopicCollection(data.topics);
- Metamaps.Synapses = new bb.SynapseCollection(data.synapses);
- Metamaps.Mappings = new bb.MappingCollection(data.mappings);
- Metamaps.Messages = data.messages;
- Metamaps.Backbone.attachCollectionEvents();
-
- var map = Metamaps.Active.Map;
- var mapper = Metamaps.Active.Mapper;
-
- // add class to .wrapper for specifying whether you can edit the map
- if (map.authorizeToEdit(mapper)) {
- $('.wrapper').addClass('canEditMap');
- }
-
- // add class to .wrapper for specifying if the map can
- // be collaborated on
- if (map.get('permission') === 'commons') {
- $('.wrapper').addClass('commonsMap');
- }
-
- // set filter mapper H3 text
- $('#filter_by_mapper h3').html('MAPPERS');
-
- // build and render the visualization
- Metamaps.Visualize.type = "ForceDirected";
- Metamaps.JIT.prepareVizData();
-
- // update filters
- Metamaps.Filter.reset();
-
- // reset selected arrays
- Metamaps.Selected.reset();
-
- // set the proper mapinfobox content
- Metamaps.Map.InfoBox.load();
-
- // these three update the actual filter box with the right list items
- Metamaps.Filter.checkMetacodes();
- Metamaps.Filter.checkSynapses();
- Metamaps.Filter.checkMappers();
-
- Metamaps.Realtime.startActiveMap();
- Metamaps.Loading.hide();
- }
-
- $.ajax({
- url: "/maps/" + id + "/contains.json",
- success: start
- });
- },
- end: function () {
- if (Metamaps.Active.Map) {
-
- $('.wrapper').removeClass('canEditMap commonsMap');
- Metamaps.Map.resetSpiral();
-
- $('.rightclickmenu').remove();
- Metamaps.TopicCard.hideCard();
- Metamaps.SynapseCard.hideCard();
- Metamaps.Create.newTopic.hide();
- Metamaps.Create.newSynapse.hide();
- Metamaps.Filter.close();
- Metamaps.Map.InfoBox.close();
- Metamaps.Realtime.endActiveMap();
- }
- },
- fork: function () {
- Metamaps.GlobalUI.openLightbox('forkmap');
-
- var nodes_data = "",
- synapses_data = "";
- var nodes_array = [];
- var synapses_array = [];
- // collect the unfiltered topics
- Metamaps.Visualize.mGraph.graph.eachNode(function (n) {
- // if the opacity is less than 1 then it's filtered
- if (n.getData('alpha') === 1) {
- var id = n.getData('topic').id;
- nodes_array.push(id);
- var x, y;
- if (n.pos.x && n.pos.y) {
- x = n.pos.x;
- y = n.pos.y;
- } else {
- var x = Math.cos(n.pos.theta) * n.pos.rho;
- var y = Math.sin(n.pos.theta) * n.pos.rho;
- }
- nodes_data += id + '/' + x + '/' + y + ',';
- }
- });
- // collect the unfiltered synapses
- Metamaps.Synapses.each(function(synapse){
- var desc = synapse.get("desc");
-
- var descNotFiltered = Metamaps.Filter.visible.synapses.indexOf(desc) > -1;
- // make sure that both topics are being added, otherwise, it
- // doesn't make sense to add the synapse
- var topicsNotFiltered = nodes_array.indexOf(synapse.get('node1_id')) > -1;
- topicsNotFiltered = topicsNotFiltered && nodes_array.indexOf(synapse.get('node2_id')) > -1;
- if (descNotFiltered && topicsNotFiltered) {
- synapses_array.push(synapse.id);
- }
- });
-
- synapses_data = synapses_array.join();
- nodes_data = nodes_data.slice(0, -1);
-
- Metamaps.GlobalUI.CreateMap.topicsToMap = nodes_data;
- Metamaps.GlobalUI.CreateMap.synapsesToMap = synapses_data;
-
- },
- leavePrivateMap: function(){
- var map = Metamaps.Active.Map;
- Metamaps.Maps.Active.remove(map);
- Metamaps.Maps.Featured.remove(map);
- Metamaps.Router.home();
- Metamaps.GlobalUI.notifyUser('Sorry! That map has been changed to Private.');
- },
- commonsToPublic: function(){
- Metamaps.Realtime.turnOff(true); // true is for 'silence'
- Metamaps.GlobalUI.notifyUser('Map was changed to Public. Editing is disabled.');
- Metamaps.Active.Map.trigger('changeByOther');
- },
- publicToCommons: function(){
- var confirmString = "This map permission has been changed to Commons! ";
- confirmString += "Do you want to reload and enable realtime collaboration?";
- var c = confirm(confirmString);
- if (c) {
- Metamaps.Router.maps(Metamaps.Active.Map.id);
- }
- },
- editedByActiveMapper: function () {
- if (Metamaps.Active.Mapper) {
- Metamaps.Mappers.add(Metamaps.Active.Mapper);
- }
- },
- getNextCoord: function() {
- var self = Metamaps.Map;
- var nextX = self.nextX;
- var nextY = self.nextY;
-
- var DISTANCE_BETWEEN = 120;
-
- self.nextX = self.nextX + DISTANCE_BETWEEN * self.nextXshift;
- self.nextY = self.nextY + DISTANCE_BETWEEN * self.nextYshift;
-
- self.timeToTurn += 1;
- // if true, it's time to turn
- if (self.timeToTurn === self.sideLength) {
-
- self.turnCount += 1;
- // if true, it's time to increase side length
- if (self.turnCount % 2 === 0) {
- self.sideLength += 1;
- }
- self.timeToTurn = 0;
-
- // going right? turn down
- if (self.nextXshift == 1 && self.nextYshift == 0) {
- self.nextXshift = 0;
- self.nextYshift = 1;
- }
- // going down? turn left
- else if (self.nextXshift == 0 && self.nextYshift == 1) {
- self.nextXshift = -1;
- self.nextYshift = 0;
- }
- // going left? turn up
- else if (self.nextXshift == -1 && self.nextYshift == 0) {
- self.nextXshift = 0;
- self.nextYshift = -1;
- }
- // going up? turn right
- else if (self.nextXshift == 0 && self.nextYshift == -1) {
- self.nextXshift = 1;
- self.nextYshift = 0;
- }
- }
-
- return {
- x: nextX,
- y: nextY
- }
- },
- resetSpiral: function() {
- Metamaps.Map.nextX = 0;
- Metamaps.Map.nextY = 0;
- Metamaps.Map.nextXshift = 1;
- Metamaps.Map.nextYshift = 0;
- 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()
- };
-
- 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 mapName = map.get("name").split(" ").join([separator = '-']);
- 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');
- }
- });
- }
-};
-
-
-/*
- *
- * CHEATSHEET
- *
- */
-Metamaps.Map.CheatSheet = {
- init: function () {
- // tab the cheatsheet
- $('#cheatSheet').tabs();
- $('#quickReference').tabs().addClass("ui-tabs-vertical ui-helper-clearfix");
- $("#quickReference .ui-tabs-nav li").removeClass("ui-corner-top").addClass("ui-corner-left");
-
- // id = the id of a vimeo video
- var switchVideo = function (element, id) {
- $('.tutorialItem').removeClass("active");
- $(element).addClass("active");
- $('#tutorialVideo').attr('src','//player.vimeo.com/video/'+id);
- };
-
- $('#gettingStarted').click(function() {
- //switchVideo(this,'88334167');
- });
- $('#upYourSkillz').click(function() {
- //switchVideo(this,'100118167');
- });
- $('#advancedMapping').click(function() {
- //switchVideo(this,'88334167');
- });
- }
-}; // end Metamaps.Map.CheatSheet
-
-
-/*
- *
- * INFOBOX
- *
- */
-Metamaps.Map.InfoBox = {
- isOpen: false,
- changing: false,
- selectingPermission: false,
- changePermissionText: "As the creator, you can change the permission of this map, but the permissions of the topics and synapses on it must be changed independently.
",
- nameHTML: '{{name}}',
- descHTML: '{{desc}}',
- init: function () {
- var self = Metamaps.Map.InfoBox;
-
- $('.mapInfoIcon').click(self.toggleBox);
- $('.mapInfoBox').click(function(event){
- event.stopPropagation();
- });
- $('body').click(self.close);
-
- self.attachEventListeners();
-
- self.generateBoxHTML = Hogan.compile($('#mapInfoBoxTemplate').html());
- },
- toggleBox: function (event) {
- var self = Metamaps.Map.InfoBox;
-
- if (self.isOpen) self.close();
- else self.open();
-
- event.stopPropagation();
- },
- open: function () {
- var self = Metamaps.Map.InfoBox;
- $('.mapInfoIcon div').addClass('hide');
- if (!self.isOpen && !self.changing) {
- self.changing = true;
- $('.mapInfoBox').fadeIn(200, function () {
- self.changing = false;
- self.isOpen = true;
- });
- }
- },
- close: function () {
- var self = Metamaps.Map.InfoBox;
-
- $('.mapInfoIcon div').removeClass('hide');
- if (!self.changing) {
- self.changing = true;
- $('.mapInfoBox').fadeOut(200, function () {
- self.changing = false;
- self.isOpen = false;
- self.hidePermissionSelect();
- $('.mapContributors .tip').hide();
- });
- }
- },
- load: function () {
- var self = Metamaps.Map.InfoBox;
-
- var map = Metamaps.Active.Map;
-
- var obj = map.pick("permission","contributor_count","topic_count","synapse_count");
-
- var isCreator = map.authorizePermissionChange(Metamaps.Active.Mapper);
- var canEdit = map.authorizeToEdit(Metamaps.Active.Mapper);
- var shareable = map.get('permission') !== 'private';
-
- obj["name"] = canEdit ? Hogan.compile(self.nameHTML).render({id: map.id, name: map.get("name")}) : map.get("name");
- obj["desc"] = canEdit ? Hogan.compile(self.descHTML).render({id: map.id, desc: map.get("desc")}) : map.get("desc");
- obj["map_creator_tip"] = isCreator ? self.changePermissionText : "";
- obj["contributors_class"] = Metamaps.Mappers.length > 1 ? "multiple" : "";
- obj["contributors_class"] += Metamaps.Mappers.length === 2 ? " mTwo" : "";
- obj["contributor_image"] = Metamaps.Mappers.length > 0 ? Metamaps.Mappers.models[0].get("image") : "<%= asset_path('user.png') %>";
- obj["contributor_list"] = self.createContributorList();
- obj["user_name"] = isCreator ? "You" : map.get("user_name");
- obj["created_at"] = map.get("created_at_clean");
- obj["updated_at"] = map.get("updated_at_clean");
-
- var classes = isCreator ? "yourMap" : "";
- classes += canEdit ? " canEdit" : "";
- classes += shareable ? " shareable" : "";
- $(".mapInfoBox").removeClass("shareable yourMap canEdit")
- .addClass(classes)
- .html(self.generateBoxHTML.render(obj));
-
- self.attachEventListeners();
- },
- attachEventListeners: function () {
- var self = Metamaps.Map.InfoBox;
-
- $('.mapInfoBox.canEdit .best_in_place').best_in_place();
-
- // because anyone who can edit the map can change the map title
- var bipName = $('.mapInfoBox .best_in_place_name');
- bipName.unbind("best_in_place:activate").bind("best_in_place:activate", function () {
- var $el = bipName.find('textarea');
- var el = $el[0];
-
- $el.attr('maxlength', '140');
-
- $('.mapInfoName').append('');
-
- var callback = function (data) {
- $('.nameCounter.forMap').html(data.all + '/140');
- };
- Countable.live(el, callback);
- });
- bipName.unbind("best_in_place:deactivate").bind("best_in_place:deactivate", function () {
- $('.nameCounter.forMap').remove();
- });
-
- $('.mapInfoName .best_in_place_name').unbind("ajax:success").bind("ajax:success", function () {
- var name = $(this).html();
- Metamaps.Active.Map.set('name', name);
- Metamaps.Active.Map.trigger('saved');
- });
-
- $('.mapInfoDesc .best_in_place_desc').unbind("ajax:success").bind("ajax:success", function () {
- var desc = $(this).html();
- Metamaps.Active.Map.set('desc', desc);
- Metamaps.Active.Map.trigger('saved');
- });
-
- $('.yourMap .mapPermission').unbind().click(self.onPermissionClick);
- // .yourMap in the unbind/bind is just a namespace for the events
- // not a reference to the class .yourMap on the .mapInfoBox
- $('.mapInfoBox.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect);
-
- $('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap);
-
- $('.mapContributors span, #mapContribs').unbind().click(function(event){
- $('.mapContributors .tip').toggle();
- event.stopPropagation();
- });
- $('.mapContributors .tip').unbind().click(function(event){
- event.stopPropagation();
- });
- $('.mapContributors .tip li a').click(Metamaps.Router.intercept);
-
- $('.mapInfoBox').unbind('.hideTip').bind('click.hideTip', function(){
- $('.mapContributors .tip').hide();
- });
- },
- updateNameDescPerm: function(name, desc, perm) {
- $('.mapInfoName .best_in_place_name').html(name);
- $('.mapInfoDesc .best_in_place_desc').html(desc);
- $('.mapInfoBox .mapPermission').removeClass('commons public private').addClass(perm);
- },
- createContributorList: function () {
- var self = Metamaps.Map.InfoBox;
-
- var string = "";
- string += "";
- return string;
- },
- updateNumbers: function () {
- var self = Metamaps.Map.InfoBox;
- var mapper = Metamaps.Active.Mapper;
-
- var contributors_class = "";
- if (Metamaps.Mappers.length === 2) contributors_class = "multiple mTwo";
- else if (Metamaps.Mappers.length > 2) contributors_class = "multiple";
-
- var contributors_image = "<%= asset_path('user.png') %>";
- if (Metamaps.Mappers.length > 0) {
- // get the first contributor and use their image
- contributors_image = Metamaps.Mappers.models[0].get("image");
- }
- $('.mapContributors img').attr('src', contributors_image).removeClass('multiple mTwo').addClass(contributors_class);
- $('.mapContributors span').text(Metamaps.Mappers.length)
- $('.mapContributors .tip').html(self.createContributorList());
- $('.mapTopics').text(Metamaps.Topics.length);
- $('.mapSynapses').text(Metamaps.Synapses.length);
-
- $('.mapEditedAt').html('Last edited: ' + Metamaps.Util.nowDateFormatted());
- },
- onPermissionClick: function (event) {
- var self = Metamaps.Map.InfoBox;
-
- if (!self.selectingPermission) {
- self.selectingPermission = true;
- $(this).addClass('minimize'); // this line flips the drop down arrow to a pull up arrow
- if ($(this).hasClass('commons')) {
- $(this).append('');
- } else if ($(this).hasClass('public')) {
- $(this).append('');
- } else if ($(this).hasClass('private')) {
- $(this).append('');
- }
- $('.mapPermission .permissionSelect li').click(self.selectPermission);
- event.stopPropagation();
- }
- },
- hidePermissionSelect: function () {
- var self = Metamaps.Map.InfoBox;
-
- self.selectingPermission = false;
- $('.mapPermission').removeClass('minimize'); // this line flips the pull up arrow to a drop down arrow
- $('.mapPermission .permissionSelect').remove();
- },
- selectPermission: function (event) {
- var self = Metamaps.Map.InfoBox;
-
- self.selectingPermission = false;
- var permission = $(this).attr('class');
- var permBefore = Metamaps.Active.Map.get('permission');
- Metamaps.Active.Map.save({
- permission: permission
- });
- Metamaps.Active.Map.updateMapWrapper();
- if (permBefore !== 'commons' && permission === 'commons') {
- Metamaps.Realtime.setupSocket();
- Metamaps.Realtime.turnOn();
- }
- else if (permBefore === 'commons' && permission === 'public') {
- Metamaps.Realtime.turnOff(true); // true is to 'silence'
- // the notification that would otherwise be sent
- }
- shareable = permission === 'private' ? '' : 'shareable';
- $('.mapPermission').removeClass('commons public private minimize').addClass(permission);
- $('.mapPermission .permissionSelect').remove();
- $('.mapInfoBox').removeClass('shareable').addClass(shareable);
- event.stopPropagation();
- },
- deleteActiveMap: function () {
- var confirmString = 'Are you sure you want to delete this map? ';
- confirmString += 'This action is irreversible. It will not delete the topics and synapses on the map.';
-
- var doIt = confirm(confirmString);
- var map = Metamaps.Active.Map;
- var mapper = Metamaps.Active.Mapper;
- var authorized = map.authorizePermissionChange(mapper);
-
- if (doIt && authorized) {
- Metamaps.Map.InfoBox.close();
- Metamaps.Maps.Active.remove(map);
- Metamaps.Maps.Featured.remove(map);
- Metamaps.Maps.Mine.remove(map);
- map.destroy();
- Metamaps.Router.home();
- Metamaps.GlobalUI.notifyUser('Map eliminated!');
- }
- else if (!authorized) {
- alert('Hey now. We can\'t just go around willy nilly deleting other people\'s maps now can we? Run off and find something constructive to do, eh?');
- }
- }
-}; // end Metamaps.Map.InfoBox