diff --git a/app/views/maps/_newtopic.html.erb b/app/views/maps/_newtopic.html.erb index 7b833d3c..f73649a4 100644 --- a/app/views/maps/_newtopic.html.erb +++ b/app/views/maps/_newtopic.html.erb @@ -22,11 +22,16 @@
<% end %> diff --git a/app/views/shared/_switchmetacodes.html.erb b/app/views/shared/_switchmetacodes.html.erb index 9dbdabb6..b99ec1e4 100644 --- a/app/views/shared/_switchmetacodes.html.erb +++ b/app/views/shared/_switchmetacodes.html.erb @@ -116,6 +116,7 @@
diff --git a/frontend/src/Metamaps/Active.js b/frontend/src/Metamaps/Active.js index ddd44152..7c4a49e5 100644 --- a/frontend/src/Metamaps/Active.js +++ b/frontend/src/Metamaps/Active.js @@ -1,7 +1,9 @@ -const Active = { - Map: null, - Mapper: null, - Topic: null +const Active = (map = null, mapper = null, topic = null) => { + return { + Map: map, + Mapper: mapper, + Topic: topic + } } export default Active diff --git a/frontend/src/Metamaps/AutoLayout.js b/frontend/src/Metamaps/AutoLayout.js index acbca6ff..3f8a4ca6 100644 --- a/frontend/src/Metamaps/AutoLayout.js +++ b/frontend/src/Metamaps/AutoLayout.js @@ -1,77 +1,79 @@ -const AutoLayout = { - nextX: 0, - nextY: 0, - sideLength: 1, - turnCount: 0, - nextXshift: 1, - nextYshift: 0, - timeToTurn: 0, +const AutoLayout = () => { + return { + nextX: 0, + nextY: 0, + sideLength: 1, + turnCount: 0, + nextXshift: 1, + nextYshift: 0, + timeToTurn: 0, - getNextCoord: function(opts = {}) { - var self = AutoLayout - var nextX = self.nextX - var nextY = self.nextY + getNextCoord: function(opts = {}) { + var self = AutoLayout + var nextX = self.nextX + var nextY = self.nextY - var DISTANCE_BETWEEN = 120 + var DISTANCE_BETWEEN = 120 - self.nextX = self.nextX + DISTANCE_BETWEEN * self.nextXshift - self.nextY = self.nextY + DISTANCE_BETWEEN * self.nextYshift + 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 += 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 + } else if (self.nextXshift === 0 && self.nextYshift === 1) { + // going down? turn left + self.nextXshift = -1 + self.nextYshift = 0 + } else if (self.nextXshift === -1 && self.nextYshift === 0) { + // going left? turn up + self.nextXshift = 0 + self.nextYshift = -1 + } else if (self.nextXshift === 0 && self.nextYshift === -1) { + // going up? turn right + self.nextXshift = 1 + self.nextYshift = 0 + } } + + if (opts.mappings && self.coordsTaken(nextX, nextY, opts.mappings)) { + // check if the coordinate is already taken on the current map + return self.getNextCoord(opts) + } else { + return { + x: nextX, + y: nextY + } + } + }, + coordsTaken: function(x, y, mappings) { + if (mappings.findWhere({ xloc: x, yloc: y })) { + return true + } else { + return false + } + }, + resetSpiral: function() { + var self = AutoLayout + self.nextX = 0 + self.nextY = 0 + self.nextXshift = 1 + self.nextYshift = 0 + self.sideLength = 1 self.timeToTurn = 0 - - // going right? turn down - if (self.nextXshift === 1 && self.nextYshift === 0) { - self.nextXshift = 0 - self.nextYshift = 1 - } else if (self.nextXshift === 0 && self.nextYshift === 1) { - // going down? turn left - self.nextXshift = -1 - self.nextYshift = 0 - } else if (self.nextXshift === -1 && self.nextYshift === 0) { - // going left? turn up - self.nextXshift = 0 - self.nextYshift = -1 - } else if (self.nextXshift === 0 && self.nextYshift === -1) { - // going up? turn right - self.nextXshift = 1 - self.nextYshift = 0 - } + self.turnCount = 0 } - - if (opts.mappings && self.coordsTaken(nextX, nextY, opts.mappings)) { - // check if the coordinate is already taken on the current map - return self.getNextCoord(opts) - } else { - return { - x: nextX, - y: nextY - } - } - }, - coordsTaken: function(x, y, mappings) { - if (mappings.findWhere({ xloc: x, yloc: y })) { - return true - } else { - return false - } - }, - resetSpiral: function() { - var self = AutoLayout - self.nextX = 0 - self.nextY = 0 - self.nextXshift = 1 - self.nextYshift = 0 - self.sideLength = 1 - self.timeToTurn = 0 - self.turnCount = 0 } } diff --git a/frontend/src/Metamaps/Cable.js b/frontend/src/Metamaps/Cable.js index a5809df5..38a94ec8 100644 --- a/frontend/src/Metamaps/Cable.js +++ b/frontend/src/Metamaps/Cable.js @@ -2,24 +2,13 @@ import { indexOf } from 'lodash' -import Active from './Active' -import Control from './Control' -import DataModel from './DataModel' -import Map from './Map' import Mapper from './Mapper' -import Synapse from './Synapse' -import Topic from './Topic' -import { ChatView } from './Views' -import Visualize from './Visualize' -const Cable = { - init: () => { - let self = Cable - self.cable = ActionCable.createConsumer() - }, +const Cable = ({Active, Control, DataModel, Map, Synapse, Topic, ChatView, Visualize}) => { +const toExport = { subscribeToMap: id => { - let self = Cable - self.sub = self.cable.subscriptions.create({ + let self = toExport + self.sub = Cable.cable.subscriptions.create({ channel: 'MapChannel', id: id }, { @@ -27,7 +16,7 @@ const Cable = { }) }, unsubscribeFromMap: () => { - let self = Cable + let self = toExport self.sub.unsubscribe() delete self.sub }, @@ -230,5 +219,10 @@ const Cable = { }) } } +return toExport +} +Cable.init = () => { + Cable.cable = ActionCable.createConsumer() +} export default Cable diff --git a/frontend/src/Metamaps/Control.js b/frontend/src/Metamaps/Control.js index aa4790ff..c82dae11 100644 --- a/frontend/src/Metamaps/Control.js +++ b/frontend/src/Metamaps/Control.js @@ -1,452 +1,448 @@ import _ from 'lodash' import outdent from 'outdent' -import Active from './Active' -import DataModel from './DataModel' -import Filter from './Filter' import GlobalUI from './GlobalUI' -import Mouse from './Mouse' -import Selected from './Selected' import Settings from './Settings' -import Visualize from './Visualize' -const Control = { - selectNode: function(node, e) { - var filtered = node.getData('alpha') === 0 +const Control = ({Active, DataModel, Filter, Mouse, Selected, Visualize}) => { + return { + selectNode: function(node, e) { + var filtered = node.getData('alpha') === 0 - if (filtered || Selected.Nodes.indexOf(node) !== -1) return - node.selected = true - node.setData('dim', 30, 'current') - Selected.Nodes.push(node) - }, - selectNeighbors: function() { - if (Selected.Nodes.length > 0) { - //For each selected node, select all connected node and the synapses too - Selected.Nodes.forEach((item) => { - if (Visualize.mGraph.graph.getNode(item.id).adjacencies) { - for (const adjID in Visualize.mGraph.graph.getNode(item.id).adjacencies) { - Control.selectNode(Visualize.mGraph.graph.getNode(adjID)) - Control.selectEdge(Visualize.mGraph.graph.getNode(item.id).adjacencies[adjID]) + if (filtered || Selected.Nodes.indexOf(node) !== -1) return + node.selected = true + node.setData('dim', 30, 'current') + Selected.Nodes.push(node) + }, + selectNeighbors: function() { + if (Selected.Nodes.length > 0) { + //For each selected node, select all connected node and the synapses too + Selected.Nodes.forEach((item) => { + if (Visualize.mGraph.graph.getNode(item.id).adjacencies) { + for (const adjID in Visualize.mGraph.graph.getNode(item.id).adjacencies) { + Control.selectNode(Visualize.mGraph.graph.getNode(adjID)) + Control.selectEdge(Visualize.mGraph.graph.getNode(item.id).adjacencies[adjID]) + } } - } - }) + }) - Visualize.mGraph.plot() - } - }, - deselectAllNodes: function() { - var l = Selected.Nodes.length - for (var i = l - 1; i >= 0; i -= 1) { - var node = Selected.Nodes[i] - Control.deselectNode(node) - } - Visualize.mGraph.plot() - }, - deselectNode: function(node) { - delete node.selected - node.setData('dim', 25, 'current') - - // remove the node - Selected.Nodes.splice( - Selected.Nodes.indexOf(node), 1) - }, - deleteSelected: function() { - if (!Active.Map) return - - var n = Selected.Nodes.length - var e = Selected.Edges.length - var ntext = n === 1 ? '1 topic' : n + ' topics' - var etext = e === 1 ? '1 synapse' : e + ' synapses' - - var authorized = Active.Map.authorizeToEdit(Active.Mapper) - - if (!authorized) { - GlobalUI.notifyUser('Cannot edit Public map.') - return - } - - var r = window.confirm(outdent` - You have ${ntext} and ${etext} selected. Are you sure you want - to permanently delete them all? This will remove them from all - maps they appear on.`) - if (r) { - Control.deleteSelectedEdges() - Control.deleteSelectedNodes() - } - - if (DataModel.Topics.length === 0) { - Map.setHasLearnedTopicCreation(false) - } - }, - deleteSelectedNodes: function() { // refers to deleting topics permanently - if (!Active.Map) return - - var authorized = Active.Map.authorizeToEdit(Active.Mapper) - - if (!authorized) { - GlobalUI.notifyUser('Cannot edit Public map.') - return - } - - var l = Selected.Nodes.length - for (var i = l - 1; i >= 0; i -= 1) { - var node = Selected.Nodes[i] - Control.deleteNode(node.id) - } - }, - deleteNode: function(nodeid) { // refers to deleting topics permanently - if (!Active.Map) return - - var authorized = Active.Map.authorizeToEdit(Active.Mapper) - - if (!authorized) { - GlobalUI.notifyUser('Cannot edit Public map.') - return - } - - var node = Visualize.mGraph.graph.getNode(nodeid) - var topic = node.getData('topic') - - var permToDelete = Active.Mapper.id === topic.get('user_id') || Active.Mapper.get('admin') - if (permToDelete) { - var mapping = node.getData('mapping') - topic.destroy() - DataModel.Mappings.remove(mapping) - Control.hideNode(nodeid) - } else { - GlobalUI.notifyUser('Only topics you created can be deleted') - } - }, - removeSelectedNodes: function() { // refers to removing topics permanently from a map - if (Active.Topic) { - // hideNode will handle synapses as well - var nodeids = _.map(Selected.Nodes, function(node) { - return node.id - }) - _.each(nodeids, function(nodeid) { - if (Active.Topic.id !== nodeid) { - DataModel.Topics.remove(nodeid) - Control.hideNode(nodeid) - } - }) - return - } - if (!Active.Map) return - - const l = Selected.Nodes.length - const authorized = Active.Map.authorizeToEdit(Active.Mapper) - - if (!authorized) { - GlobalUI.notifyUser('Cannot edit this map.') - return - } - - if (Active.Mapper.get('follow_map_on_contributed')) { - Active.Mapper.followMap(Active.Map.id) - } - - for (let i = l - 1; i >= 0; i -= 1) { - const node = Selected.Nodes[i] - Control.removeNode(node.id) - } - }, - removeNode: function(nodeid) { // refers to removing topics permanently from a map - if (!Active.Map) return - - var authorized = Active.Map.authorizeToEdit(Active.Mapper) - var node = Visualize.mGraph.graph.getNode(nodeid) - - if (!authorized) { - GlobalUI.notifyUser('Cannot edit this map.') - return - } - - if (Active.Mapper.get('follow_map_on_contributed')) { - Active.Mapper.followMap(Active.Map.id) - } - - var topic = node.getData('topic') - var mapping = node.getData('mapping') - mapping.destroy() - DataModel.Topics.remove(topic) - Control.hideNode(nodeid) - }, - hideSelectedNodes: function() { - const l = Selected.Nodes.length - for (let i = l - 1; i >= 0; i -= 1) { - const node = Selected.Nodes[i] - Control.hideNode(node.id) - } - }, - hideNode: function(nodeid) { - var node = Visualize.mGraph.graph.getNode(nodeid) - var graph = Visualize.mGraph - - Control.deselectNode(node) - - node.setData('alpha', 0, 'end') - node.eachAdjacency(function(adj) { - adj.setData('alpha', 0, 'end') - }) - Visualize.mGraph.fx.animate({ - modes: ['node-property:alpha', - 'edge-property:alpha' - ], - duration: 500 - }) - setTimeout(function() { - if (nodeid === Visualize.mGraph.root) { // && Visualize.type === "RGraph" - var newroot = _.find(graph.graph.nodes, function(n) { return n.id !== nodeid }) - graph.root = newroot ? newroot.id : null + Visualize.mGraph.plot() } - Visualize.mGraph.graph.removeNode(nodeid) - }, 500) - Filter.checkMetacodes() - Filter.checkMappers() - }, - selectEdge: function(edge) { - var filtered = edge.getData('alpha') === 0 // don't select if the edge is filtered + }, + deselectAllNodes: function() { + var l = Selected.Nodes.length + for (var i = l - 1; i >= 0; i -= 1) { + var node = Selected.Nodes[i] + Control.deselectNode(node) + } + Visualize.mGraph.plot() + }, + deselectNode: function(node) { + delete node.selected + node.setData('dim', 25, 'current') - if (filtered || Selected.Edges.indexOf(edge) !== -1) return + // remove the node + Selected.Nodes.splice( + Selected.Nodes.indexOf(node), 1) + }, + deleteSelected: function() { + if (!Active.Map) return - var width = Mouse.edgeHoveringOver === edge ? 4 : 2 - edge.setDataset('current', { - showDesc: true, - lineWidth: width, - color: Settings.colors.synapses.selected - }) - Visualize.mGraph.plot() + var n = Selected.Nodes.length + var e = Selected.Edges.length + var ntext = n === 1 ? '1 topic' : n + ' topics' + var etext = e === 1 ? '1 synapse' : e + ' synapses' - Selected.Edges.push(edge) - }, - deselectAllEdges: function() { - var l = Selected.Edges.length - for (var i = l - 1; i >= 0; i -= 1) { - var edge = Selected.Edges[i] - Control.deselectEdge(edge) - } - Visualize.mGraph.plot() - }, - deselectEdge: function(edge) { - edge.setData('showDesc', false, 'current') + var authorized = Active.Map.authorizeToEdit(Active.Mapper) - edge.setDataset('current', { - lineWidth: 2, - color: Settings.colors.synapses.normal - }) + if (!authorized) { + GlobalUI.notifyUser('Cannot edit Public map.') + return + } - if (Mouse.edgeHoveringOver === edge) { + var r = window.confirm(outdent` + You have ${ntext} and ${etext} selected. Are you sure you want + to permanently delete them all? This will remove them from all + maps they appear on.`) + if (r) { + Control.deleteSelectedEdges() + Control.deleteSelectedNodes() + } + + if (DataModel.Topics.length === 0) { + Map.setHasLearnedTopicCreation(false) + } + }, + deleteSelectedNodes: function() { // refers to deleting topics permanently + if (!Active.Map) return + + var authorized = Active.Map.authorizeToEdit(Active.Mapper) + + if (!authorized) { + GlobalUI.notifyUser('Cannot edit Public map.') + return + } + + var l = Selected.Nodes.length + for (var i = l - 1; i >= 0; i -= 1) { + var node = Selected.Nodes[i] + Control.deleteNode(node.id) + } + }, + deleteNode: function(nodeid) { // refers to deleting topics permanently + if (!Active.Map) return + + var authorized = Active.Map.authorizeToEdit(Active.Mapper) + + if (!authorized) { + GlobalUI.notifyUser('Cannot edit Public map.') + return + } + + var node = Visualize.mGraph.graph.getNode(nodeid) + var topic = node.getData('topic') + + var permToDelete = Active.Mapper.id === topic.get('user_id') || Active.Mapper.get('admin') + if (permToDelete) { + var mapping = node.getData('mapping') + topic.destroy() + DataModel.Mappings.remove(mapping) + Control.hideNode(nodeid) + } else { + GlobalUI.notifyUser('Only topics you created can be deleted') + } + }, + removeSelectedNodes: function() { // refers to removing topics permanently from a map + if (Active.Topic) { + // hideNode will handle synapses as well + var nodeids = _.map(Selected.Nodes, function(node) { + return node.id + }) + _.each(nodeids, function(nodeid) { + if (Active.Topic.id !== nodeid) { + DataModel.Topics.remove(nodeid) + Control.hideNode(nodeid) + } + }) + return + } + if (!Active.Map) return + + const l = Selected.Nodes.length + const authorized = Active.Map.authorizeToEdit(Active.Mapper) + + if (!authorized) { + GlobalUI.notifyUser('Cannot edit this map.') + return + } + + if (Active.Mapper.get('follow_map_on_contributed')) { + Active.Mapper.followMap(Active.Map.id) + } + + for (let i = l - 1; i >= 0; i -= 1) { + const node = Selected.Nodes[i] + Control.removeNode(node.id) + } + }, + removeNode: function(nodeid) { // refers to removing topics permanently from a map + if (!Active.Map) return + + var authorized = Active.Map.authorizeToEdit(Active.Mapper) + var node = Visualize.mGraph.graph.getNode(nodeid) + + if (!authorized) { + GlobalUI.notifyUser('Cannot edit this map.') + return + } + + if (Active.Mapper.get('follow_map_on_contributed')) { + Active.Mapper.followMap(Active.Map.id) + } + + var topic = node.getData('topic') + var mapping = node.getData('mapping') + mapping.destroy() + DataModel.Topics.remove(topic) + Control.hideNode(nodeid) + }, + hideSelectedNodes: function() { + const l = Selected.Nodes.length + for (let i = l - 1; i >= 0; i -= 1) { + const node = Selected.Nodes[i] + Control.hideNode(node.id) + } + }, + hideNode: function(nodeid) { + var node = Visualize.mGraph.graph.getNode(nodeid) + var graph = Visualize.mGraph + + Control.deselectNode(node) + + node.setData('alpha', 0, 'end') + node.eachAdjacency(function(adj) { + adj.setData('alpha', 0, 'end') + }) + Visualize.mGraph.fx.animate({ + modes: ['node-property:alpha', + 'edge-property:alpha' + ], + duration: 500 + }) + setTimeout(function() { + if (nodeid === Visualize.mGraph.root) { // && Visualize.type === "RGraph" + var newroot = _.find(graph.graph.nodes, function(n) { return n.id !== nodeid }) + graph.root = newroot ? newroot.id : null + } + Visualize.mGraph.graph.removeNode(nodeid) + }, 500) + Filter.checkMetacodes() + Filter.checkMappers() + }, + selectEdge: function(edge) { + var filtered = edge.getData('alpha') === 0 // don't select if the edge is filtered + + if (filtered || Selected.Edges.indexOf(edge) !== -1) return + + var width = Mouse.edgeHoveringOver === edge ? 4 : 2 edge.setDataset('current', { showDesc: true, - lineWidth: 4 + lineWidth: width, + color: Settings.colors.synapses.selected }) - } + Visualize.mGraph.plot() - Visualize.mGraph.plot() + Selected.Edges.push(edge) + }, + deselectAllEdges: function() { + var l = Selected.Edges.length + for (var i = l - 1; i >= 0; i -= 1) { + var edge = Selected.Edges[i] + Control.deselectEdge(edge) + } + Visualize.mGraph.plot() + }, + deselectEdge: function(edge) { + edge.setData('showDesc', false, 'current') - // remove the edge - Selected.Edges.splice( - Selected.Edges.indexOf(edge), 1) - }, - deleteSelectedEdges: function() { // refers to deleting topics permanently - if (!Active.Map) return + edge.setDataset('current', { + lineWidth: 2, + color: Settings.colors.synapses.normal + }) - var authorized = Active.Map.authorizeToEdit(Active.Mapper) + if (Mouse.edgeHoveringOver === edge) { + edge.setDataset('current', { + showDesc: true, + lineWidth: 4 + }) + } - if (!authorized) { - GlobalUI.notifyUser('Cannot edit Public map.') - return - } + Visualize.mGraph.plot() - const l = Selected.Edges.length - for (let i = l - 1; i >= 0; i -= 1) { - const edge = Selected.Edges[i] - Control.deleteEdge(edge) - } - }, - deleteEdge: function(edge) { - if (!Active.Map) return + // remove the edge + Selected.Edges.splice( + Selected.Edges.indexOf(edge), 1) + }, + deleteSelectedEdges: function() { // refers to deleting topics permanently + if (!Active.Map) return - var authorized = Active.Map.authorizeToEdit(Active.Mapper) + var authorized = Active.Map.authorizeToEdit(Active.Mapper) - if (!authorized) { - GlobalUI.notifyUser('Cannot edit Public map.') - return - } + if (!authorized) { + GlobalUI.notifyUser('Cannot edit Public map.') + return + } - var index = edge.getData('displayIndex') ? edge.getData('displayIndex') : 0 + const l = Selected.Edges.length + for (let i = l - 1; i >= 0; i -= 1) { + const edge = Selected.Edges[i] + Control.deleteEdge(edge) + } + }, + deleteEdge: function(edge) { + if (!Active.Map) return - var synapse = edge.getData('synapses')[index] - var mapping = edge.getData('mappings')[index] + var authorized = Active.Map.authorizeToEdit(Active.Mapper) - var permToDelete = Active.Mapper.id === synapse.get('user_id') || Active.Mapper.get('admin') - if (permToDelete) { - if (edge.getData('synapses').length - 1 === 0) { + if (!authorized) { + GlobalUI.notifyUser('Cannot edit Public map.') + return + } + + var index = edge.getData('displayIndex') ? edge.getData('displayIndex') : 0 + + var synapse = edge.getData('synapses')[index] + var mapping = edge.getData('mappings')[index] + + var permToDelete = Active.Mapper.id === synapse.get('user_id') || Active.Mapper.get('admin') + if (permToDelete) { + if (edge.getData('synapses').length - 1 === 0) { + Control.hideEdge(edge) + } + synapse.destroy() + + // the server will destroy the mapping, we just need to remove it here + DataModel.Mappings.remove(mapping) + edge.getData('mappings').splice(index, 1) + edge.getData('synapses').splice(index, 1) + if (edge.getData('displayIndex')) { + delete edge.data.$displayIndex + } + } else { + GlobalUI.notifyUser('Only synapses you created can be deleted') + } + }, + removeSelectedEdges: function() { + // Topic view is handled by removeSelectedNodes + if (!Active.Map) return + + const l = Selected.Edges.length + + var authorized = Active.Map.authorizeToEdit(Active.Mapper) + + if (!authorized) { + GlobalUI.notifyUser('Cannot edit this map.') + return + } + + if (Active.Mapper.get('follow_map_on_contributed')) { + Active.Mapper.followMap(Active.Map.id) + } + + for (let i = l - 1; i >= 0; i -= 1) { + const edge = Selected.Edges[i] + Control.removeEdge(edge) + } + Selected.Edges = [ ] + }, + removeEdge: function(edge) { + if (!Active.Map) return + + var authorized = Active.Map.authorizeToEdit(Active.Mapper) + + if (!authorized) { + GlobalUI.notifyUser('Cannot edit this map.') + return + } + + if (Active.Mapper.get('follow_map_on_contributed')) { + Active.Mapper.followMap(Active.Map.id) + } + + if (edge.getData('mappings').length - 1 === 0) { Control.hideEdge(edge) } - synapse.destroy() - // the server will destroy the mapping, we just need to remove it here - DataModel.Mappings.remove(mapping) + var index = edge.getData('displayIndex') ? edge.getData('displayIndex') : 0 + + var synapse = edge.getData('synapses')[index] + var mapping = edge.getData('mappings')[index] + mapping.destroy() + + DataModel.Synapses.remove(synapse) + edge.getData('mappings').splice(index, 1) edge.getData('synapses').splice(index, 1) if (edge.getData('displayIndex')) { delete edge.data.$displayIndex } - } else { - GlobalUI.notifyUser('Only synapses you created can be deleted') - } - }, - removeSelectedEdges: function() { - // Topic view is handled by removeSelectedNodes - if (!Active.Map) return - - const l = Selected.Edges.length - - var authorized = Active.Map.authorizeToEdit(Active.Mapper) - - if (!authorized) { - GlobalUI.notifyUser('Cannot edit this map.') - return - } - - if (Active.Mapper.get('follow_map_on_contributed')) { - Active.Mapper.followMap(Active.Map.id) - } - - for (let i = l - 1; i >= 0; i -= 1) { - const edge = Selected.Edges[i] - Control.removeEdge(edge) - } - Selected.Edges = [ ] - }, - removeEdge: function(edge) { - if (!Active.Map) return - - var authorized = Active.Map.authorizeToEdit(Active.Mapper) - - if (!authorized) { - GlobalUI.notifyUser('Cannot edit this map.') - return - } - - if (Active.Mapper.get('follow_map_on_contributed')) { - Active.Mapper.followMap(Active.Map.id) - } - - if (edge.getData('mappings').length - 1 === 0) { - Control.hideEdge(edge) - } - - var index = edge.getData('displayIndex') ? edge.getData('displayIndex') : 0 - - var synapse = edge.getData('synapses')[index] - var mapping = edge.getData('mappings')[index] - mapping.destroy() - - DataModel.Synapses.remove(synapse) - - edge.getData('mappings').splice(index, 1) - edge.getData('synapses').splice(index, 1) - if (edge.getData('displayIndex')) { - delete edge.data.$displayIndex - } - }, - hideSelectedEdges: function() { - const l = Selected.Edges.length - for (let i = l - 1; i >= 0; i -= 1) { - const edge = Selected.Edges[i] - Control.hideEdge(edge) - } - Selected.Edges = [ ] - }, - hideEdge: function(edge) { - var from = edge.nodeFrom.id - var to = edge.nodeTo.id - edge.setData('alpha', 0, 'end') - Control.deselectEdge(edge) - Visualize.mGraph.fx.animate({ - modes: ['edge-property:alpha'], - duration: 500 - }) - setTimeout(function() { - Visualize.mGraph.graph.removeAdjacence(from, to) - }, 500) - Filter.checkSynapses() - Filter.checkMappers() - }, - updateSelectedPermissions: function(permission) { - var edge, synapse, node, topic - - GlobalUI.notifyUser('Working...') - - // variables to keep track of how many nodes and synapses you had the ability to change the permission of - var nCount = 0 - var sCount = 0 - - // change the permission of the selected synapses, if logged in user is the original creator - const edgesLength = Selected.Edges.length - for (let i = edgesLength - 1; i >= 0; i -= 1) { - edge = Selected.Edges[i] - synapse = edge.getData('synapses')[0] - - if (synapse.authorizePermissionChange(Active.Mapper)) { - synapse.save({ - permission: permission - }) - sCount++ + }, + hideSelectedEdges: function() { + const l = Selected.Edges.length + for (let i = l - 1; i >= 0; i -= 1) { + const edge = Selected.Edges[i] + Control.hideEdge(edge) } - } + Selected.Edges = [ ] + }, + hideEdge: function(edge) { + var from = edge.nodeFrom.id + var to = edge.nodeTo.id + edge.setData('alpha', 0, 'end') + Control.deselectEdge(edge) + Visualize.mGraph.fx.animate({ + modes: ['edge-property:alpha'], + duration: 500 + }) + setTimeout(function() { + Visualize.mGraph.graph.removeAdjacence(from, to) + }, 500) + Filter.checkSynapses() + Filter.checkMappers() + }, + updateSelectedPermissions: function(permission) { + var edge, synapse, node, topic - // change the permission of the selected topics, if logged in user is the original creator - const nodesLength = Selected.Nodes.length - for (let i = nodesLength - 1; i >= 0; i -= 1) { - node = Selected.Nodes[i] - topic = node.getData('topic') + GlobalUI.notifyUser('Working...') - if (topic.authorizePermissionChange(Active.Mapper)) { - topic.save({ - permission: permission - }) - nCount++ + // variables to keep track of how many nodes and synapses you had the ability to change the permission of + var nCount = 0 + var sCount = 0 + + // change the permission of the selected synapses, if logged in user is the original creator + const edgesLength = Selected.Edges.length + for (let i = edgesLength - 1; i >= 0; i -= 1) { + edge = Selected.Edges[i] + synapse = edge.getData('synapses')[0] + + if (synapse.authorizePermissionChange(Active.Mapper)) { + synapse.save({ + permission: permission + }) + sCount++ + } } - } - var nString = nCount === 1 ? (nCount.toString() + ' topic and ') : (nCount.toString() + ' topics and ') - var sString = sCount === 1 ? (sCount.toString() + ' synapse') : (sCount.toString() + ' synapses') + // change the permission of the selected topics, if logged in user is the original creator + const nodesLength = Selected.Nodes.length + for (let i = nodesLength - 1; i >= 0; i -= 1) { + node = Selected.Nodes[i] + topic = node.getData('topic') - var message = nString + sString + ' you created updated to ' + permission - GlobalUI.notifyUser(message) - }, - updateSelectedMetacodes: function(metacodeId) { - var node, topic - - GlobalUI.notifyUser('Working...') - - var metacode = DataModel.Metacodes.get(metacodeId) - - // variables to keep track of how many nodes and synapses you had the ability to change the permission of - var nCount = 0 - - // change the permission of the selected topics, if logged in user is the original creator - var l = Selected.Nodes.length - for (var i = l - 1; i >= 0; i -= 1) { - node = Selected.Nodes[i] - topic = node.getData('topic') - - if (topic.authorizeToEdit(Active.Mapper)) { - topic.save({ - 'metacode_id': metacodeId - }) - nCount++ + if (topic.authorizePermissionChange(Active.Mapper)) { + topic.save({ + permission: permission + }) + nCount++ + } } + + var nString = nCount === 1 ? (nCount.toString() + ' topic and ') : (nCount.toString() + ' topics and ') + var sString = sCount === 1 ? (sCount.toString() + ' synapse') : (sCount.toString() + ' synapses') + + var message = nString + sString + ' you created updated to ' + permission + GlobalUI.notifyUser(message) + }, + updateSelectedMetacodes: function(metacodeId) { + var node, topic + + GlobalUI.notifyUser('Working...') + + var metacode = DataModel.Metacodes.get(metacodeId) + + // variables to keep track of how many nodes and synapses you had the ability to change the permission of + var nCount = 0 + + // change the permission of the selected topics, if logged in user is the original creator + var l = Selected.Nodes.length + for (var i = l - 1; i >= 0; i -= 1) { + node = Selected.Nodes[i] + topic = node.getData('topic') + + if (topic.authorizeToEdit(Active.Mapper)) { + topic.save({ + 'metacode_id': metacodeId + }) + nCount++ + } + } + + var nString = nCount === 1 ? (nCount.toString() + ' topic') : (nCount.toString() + ' topics') + + var message = nString + ' you can edit updated to ' + metacode.get('name') + GlobalUI.notifyUser(message) + Visualize.mGraph.plot() } - - var nString = nCount === 1 ? (nCount.toString() + ' topic') : (nCount.toString() + ' topics') - - var message = nString + ' you can edit updated to ' + metacode.get('name') - GlobalUI.notifyUser(message) - Visualize.mGraph.plot() } } diff --git a/frontend/src/Metamaps/Create.js b/frontend/src/Metamaps/Create.js index 454ab331..11bbb409 100644 --- a/frontend/src/Metamaps/Create.js +++ b/frontend/src/Metamaps/Create.js @@ -1,15 +1,9 @@ /* global $, Hogan, Bloodhound */ -import DataModel from './DataModel' -import Map from './Map' -import Mouse from './Mouse' -import Selected from './Selected' -import Synapse from './Synapse' -import Topic from './Topic' -import Visualize from './Visualize' import GlobalUI from './GlobalUI' -const Create = { +const toExport = ({DataModel, Map, Mouse, Selected, Synapse, Topic, Visualize}) => { +const toExport = { isSwitchingSet: false, // indicates whether the metacode set switch lightbox is open selectedMetacodeSet: null, selectedMetacodeSetIndex: null, @@ -17,52 +11,54 @@ const Create = { newSelectedMetacodeNames: [], selectedMetacodes: [], newSelectedMetacodes: [], - init: function() { - var self = Create - self.newTopic.init() - self.newSynapse.init() - + init: function(serverData) { + toExport.newTopic.init() + toExport.newSynapse.init() + toExport.selectedMetacodeSet = serverData.selectedMetacodeSet + toExport.selectedMetacodeSetIndex = serverData.selectedMetacodeSetIndex + toExport.selectedMetacodes = serverData.selectedMetacodes + toExport.newSelectedMetacodes = serverData.newSelectedMetacodes + toExport.selectedMetacodeNames = serverData.newSelectedMetacodeNames + toExport.newSelectedMetacodeNames = serverData.newSelectedMetacodeNames // // SWITCHING METACODE SETS - $('#metacodeSwitchTabs').tabs({ - active: self.selectedMetacodeSetIndex + active: toExport.selectedMetacodeSetIndex }).addClass('ui-tabs-vertical ui-helper-clearfix') $('#metacodeSwitchTabs .ui-tabs-nav li').removeClass('ui-corner-top').addClass('ui-corner-left') - $('.customMetacodeList li').click(self.toggleMetacodeSelected) // within the custom metacode set tab - $('.selectAll').click(self.metacodeSelectorSelectAll) - $('.selectNone').click(self.metacodeSelectorSelectNone) + $('.customMetacodeList li').click(toExport.toggleMetacodeSelected) // within the custom metacode set tab + $('.selectAll').click(toExport.metacodeSelectorSelectAll) + $('.selectNone').click(toExport.metacodeSelectorSelectNone) }, toggleMetacodeSelected: function() { - var self = Create if ($(this).attr('class') !== 'toggledOff') { $(this).addClass('toggledOff') var valueToRemove = $(this).attr('id') var nameToRemove = $(this).attr('data-name') - self.newSelectedMetacodes.splice(self.newSelectedMetacodes.indexOf(valueToRemove), 1) - self.newSelectedMetacodeNames.splice(self.newSelectedMetacodeNames.indexOf(nameToRemove), 1) + toExport.newSelectedMetacodes.splice(self.newSelectedMetacodes.indexOf(valueToRemove), 1) + toExport.newSelectedMetacodeNames.splice(self.newSelectedMetacodeNames.indexOf(nameToRemove), 1) } else if ($(this).attr('class') === 'toggledOff') { $(this).removeClass('toggledOff') - self.newSelectedMetacodes.push($(this).attr('id')) - self.newSelectedMetacodeNames.push($(this).attr('data-name')) + toExport.newSelectedMetacodes.push($(this).attr('id')) + toExport.newSelectedMetacodeNames.push($(this).attr('data-name')) } - self.updateSelectAllColors() + toExport.updateSelectAllColors() }, updateSelectAllColors: function() { $('.selectAll, .selectNone').removeClass('selected') - if (Create.metacodeSelectorAreAllSelected()) { + if (toExport.metacodeSelectorAreAllSelected()) { $('.selectAll').addClass('selected') - } else if (Create.metacodeSelectorAreNoneSelected()) { + } else if (toExport.metacodeSelectorAreNoneSelected()) { $('.selectNone').addClass('selected') } }, metacodeSelectorSelectAll: function() { - $('.customMetacodeList li.toggledOff').each(Create.toggleMetacodeSelected) - Create.updateSelectAllColors() + $('.customMetacodeList li.toggledOff').each(toExport.toggleMetacodeSelected) + toExport.updateSelectAllColors() }, metacodeSelectorSelectNone: function() { - $('.customMetacodeList li').not('.toggledOff').each(Create.toggleMetacodeSelected) - Create.updateSelectAllColors() + $('.customMetacodeList li').not('.toggledOff').each(toExport.toggleMetacodeSelected) + toExport.updateSelectAllColors() }, metacodeSelectorAreAllSelected: function() { return $('.customMetacodeList li').toArray() @@ -75,41 +71,41 @@ const Create = { .reduce((curr, prev) => curr && prev) }, metacodeSelectorToggleSelectAll: function() { - // should be called when Create.isSwitchingSet is true and .customMetacodeList is visible - if (!Create.isSwitchingSet) return + // should be called when toExport.isSwitchingSet is true and .customMetacodeList is visible + if (!toExport.isSwitchingSet) return if (!$('.customMetacodeList').is(':visible')) return // If all are selected, then select none. Otherwise, select all. - if (Create.metacodeSelectorAreAllSelected()) { - Create.metacodeSelectorSelectNone() + if (toExport.metacodeSelectorAreAllSelected()) { + toExport.metacodeSelectorSelectNone() } else { // if some, but not all, are selected, it still runs this function - Create.metacodeSelectorSelectAll() + toExport.metacodeSelectorSelectAll() } }, updateMetacodeSet: function(set, index, custom) { - if (custom && Create.newSelectedMetacodes.length === 0) { + if (custom && toExport.newSelectedMetacodes.length === 0) { window.alert('Please select at least one metacode to use!') return false } var codesToSwitchToIds var metacodeModels = new DataModel.MetacodeCollection() - Create.selectedMetacodeSetIndex = index - Create.selectedMetacodeSet = 'metacodeset-' + set + toExport.selectedMetacodeSetIndex = index + toExport.selectedMetacodeSet = 'metacodeset-' + set if (!custom) { codesToSwitchToIds = $('#metacodeSwitchTabs' + set).attr('data-metacodes').split(',') $('.customMetacodeList li').addClass('toggledOff') - Create.selectedMetacodes = [] - Create.selectedMetacodeNames = [] - Create.newSelectedMetacodes = [] - Create.newSelectedMetacodeNames = [] + toExport.selectedMetacodes = [] + toExport.selectedMetacodeNames = [] + toExport.newSelectedMetacodes = [] + toExport.newSelectedMetacodeNames = [] } else if (custom) { // uses .slice to avoid setting the two arrays to the same actual array - Create.selectedMetacodes = Create.newSelectedMetacodes.slice(0) - Create.selectedMetacodeNames = Create.newSelectedMetacodeNames.slice(0) - codesToSwitchToIds = Create.selectedMetacodes.slice(0) + toExport.selectedMetacodes = Create.newSelectedMetacodes.slice(0) + toExport.selectedMetacodeNames = Create.newSelectedMetacodeNames.slice(0) + codesToSwitchToIds = toExport.selectedMetacodes.slice(0) } // sort by name @@ -141,7 +137,7 @@ const Create = { var mdata = { 'metacodes': { - 'value': custom ? Create.selectedMetacodes.toString() : Create.selectedMetacodeSet + 'value': custom ? toExport.selectedMetacodes.toString() : Create.selectedMetacodeSet } } $.ajax({ @@ -158,26 +154,25 @@ const Create = { }) }, cancelMetacodeSetSwitch: function() { - var self = Create - self.isSwitchingSet = false + toExport.isSwitchingSet = false - if (self.selectedMetacodeSet !== 'metacodeset-custom') { + if (toExport.selectedMetacodeSet !== 'metacodeset-custom') { $('.customMetacodeList li').addClass('toggledOff') - self.selectedMetacodes = [] - self.selectedMetacodeNames = [] - self.newSelectedMetacodes = [] - self.newSelectedMetacodeNames = [] + toExport.selectedMetacodes = [] + toExport.selectedMetacodeNames = [] + toExport.newSelectedMetacodes = [] + toExport.newSelectedMetacodeNames = [] } else { // custom set is selected // reset it to the current actual selection $('.customMetacodeList li').addClass('toggledOff') - for (var i = 0; i < self.selectedMetacodes.length; i++) { - $('#' + self.selectedMetacodes[i]).removeClass('toggledOff') + for (var i = 0; i < toExport.selectedMetacodes.length; i++) { + $('#' + toExport.selectedMetacodes[i]).removeClass('toggledOff') } // uses .slice to avoid setting the two arrays to the same actual array - self.newSelectedMetacodeNames = self.selectedMetacodeNames.slice(0) - self.newSelectedMetacodes = self.selectedMetacodes.slice(0) + toExport.newSelectedMetacodeNames = self.selectedMetacodeNames.slice(0) + toExport.newSelectedMetacodes = self.selectedMetacodes.slice(0) } - $('#metacodeSwitchTabs').tabs('option', 'active', self.selectedMetacodeSetIndex) + $('#metacodeSwitchTabs').tabs('option', 'active', toExport.selectedMetacodeSetIndex) $('#topic_name').focus() }, newTopic: { @@ -186,19 +181,19 @@ const Create = { const ESC = 27 if (e.keyCode === ESC) { - Create.newTopic.hide() + toExport.newTopic.hide() } // if - Create.newTopic.name = $(this).val() + toExport.newTopic.name = $(this).val() }) $('.pinCarousel').click(function() { - if (Create.newTopic.pinned) { + if (toExport.newTopic.pinned) { $('.pinCarousel').removeClass('isPinned') - Create.newTopic.pinned = false + toExport.newTopic.pinned = false } else { $('.pinCarousel').addClass('isPinned') - Create.newTopic.pinned = true + toExport.newTopic.pinned = true } }) @@ -232,7 +227,7 @@ const Create = { // tell the autocomplete to submit the form with the topic you clicked on if you pick from the autocomplete $('#topic_name').bind('typeahead:select', function(event, datum, dataset) { - Create.newTopic.beingCreated = false + toExport.newTopic.beingCreated = false if (datum.rtype === 'topic') { Topic.getTopicFromAutocomplete(datum.id) } else if (datum.rtype === 'map') { @@ -259,7 +254,7 @@ const Create = { }, name: null, newId: 1, - beingCreated: false, + beingtoExportd: false, metacode: null, x: null, y: null, @@ -269,22 +264,22 @@ const Create = { $('#new_topic').fadeIn('fast', function() { $('#topic_name').focus() }) - Create.newTopic.beingCreated = true - Create.newTopic.name = '' - Map.setHasLearnedTopicCreation(true) + toExport.newTopic.beingCreated = true + toExport.newTopic.name = '' + //Map.setHasLearnedTopicCreation(true) }, hide: function(force) { - if (force || !Create.newTopic.pinned) { + if (force || !toExport.newTopic.pinned) { $('#new_topic').fadeOut('fast') } if (force) { $('.pinCarousel').removeClass('isPinned') - Create.newTopic.pinned = false + toExport.newTopic.pinned = false } if (DataModel.Topics.length === 0) { Map.setHasLearnedTopicCreation(false) } - Create.newTopic.beingCreated = false + toExport.newTopic.beingCreated = false }, reset: function() { $('#topic_name').typeahead('val', '') @@ -306,9 +301,8 @@ const Create = { remote: { url: '/search/synapses?topic1id=%TOPIC1&topic2id=%TOPIC2', prepare: function(query, settings) { - var self = Create.newSynapse - if (Selected.Nodes.length < 2 && self.topic1id && self.topic2id) { - settings.url = settings.url.replace('%TOPIC1', self.topic1id).replace('%TOPIC2', self.topic2id) + if (Selected.Nodes.length < 2 && toExport.newSynapse.topic1id && self.newSynapse.topic2id) { + settings.url = settings.url.replace('%TOPIC1', toExport.newSynapse.topic1id).replace('%TOPIC2', toExport.newSynapse.topic2id) return settings } else { return null @@ -351,21 +345,21 @@ const Create = { const ESC = 27 if (e.keyCode === ESC) { - Create.newSynapse.hide() + toExport.newSynapse.hide() } // if - Create.newSynapse.description = $(this).val() + toExport.newSynapse.description = $(this).val() }) $('#synapse_desc').focusout(function() { - if (Create.newSynapse.beingCreated) { + if (toExport.newSynapse.beingCreated) { Synapse.createSynapseLocally() } }) $('#synapse_desc').keydown(function(e) { const TAB = 9 - if (Create.newSynapse.beingCreated && e.keyCode === TAB) { + if (toExport.newSynapse.beingCreated && e.keyCode === TAB) { e.preventDefault() Synapse.createSynapseLocally() } @@ -375,12 +369,12 @@ const Create = { if (datum.id) { // if they clicked on an existing synapse get it Synapse.getSynapseFromAutocomplete(datum.id) } else { - Create.newSynapse.description = datum.value + toExport.newSynapse.description = datum.value Synapse.createSynapseLocally() } }) }, - beingCreated: false, + beingtoExportd: false, description: null, topic1id: null, topic2id: null, @@ -389,19 +383,21 @@ const Create = { $('#new_synapse').fadeIn(100, function() { $('#synapse_desc').focus() }) - Create.newSynapse.beingCreated = true + toExport.newSynapse.beingCreated = true }, hide: function() { $('#new_synapse').fadeOut('fast') $('#synapse_desc').typeahead('val', '') - Create.newSynapse.beingCreated = false - Create.newTopic.addSynapse = false - Create.newSynapse.topic1id = 0 - Create.newSynapse.topic2id = 0 + toExport.newSynapse.beingCreated = false + toExport.newTopic.addSynapse = false + toExport.newSynapse.topic1id = 0 + toExport.newSynapse.topic2id = 0 Mouse.synapseStartCoordinates = [] if (Visualize.mGraph) Visualize.mGraph.plot() } } } +return toExport +} -export default Create +export default toExport diff --git a/frontend/src/Metamaps/DataModel/index.js b/frontend/src/Metamaps/DataModel/index.js index 272f1717..2094684a 100644 --- a/frontend/src/Metamaps/DataModel/index.js +++ b/frontend/src/Metamaps/DataModel/index.js @@ -1,7 +1,3 @@ -import Active from '../Active' -import Filter from '../Filter' -import { InfoBox } from '../Map' - import Map from './Map' import MapCollection from './MapCollection' import Message from './Message' @@ -17,57 +13,20 @@ import SynapseCollection from './SynapseCollection' import Mapping from './Mapping' import MappingCollection from './MappingCollection' -const DataModel = { - Map: Map, - MapCollection: MapCollection, - Message: Message, - MessageCollection: MessageCollection, - Mapper: Mapper, - MapperCollection: MapperCollection, - Metacode: Metacode, - MetacodeCollection: MetacodeCollection, - Topic: Topic, - TopicCollection: TopicCollection, - Synapse: Synapse, - SynapseCollection: SynapseCollection, - Mapping: Mapping, - MappingCollection: MappingCollection, - +const DataModel = ({Filter, InfoBox}) => { +const toExport = { Collaborators: new MapperCollection(), Creators: new MapperCollection(), Mappers: new MapperCollection(), Mappings: new MappingCollection(), - Maps: { - Mine: [], - Shared: [], - Starred: [], - Mapper: { - models: [], - mapperId: null - }, - Featured: [], - Active: [] - }, Messages: [], Metacodes: new MetacodeCollection(), Stars: [], Synapses: new SynapseCollection(), Topics: new TopicCollection(), - - init: function(serverData) { - var self = DataModel - - // workaround circular import problem - if (!self.MapCollection.model) self.MapCollection.model = Map - - self.synapseIconUrl = serverData['synapse16.png'] - - if (serverData.ActiveMap) Active.Map = new Map(serverData.ActiveMap) - if (serverData.ActiveMapper) Active.Mapper = new Mapper(serverData.ActiveMapper) - if (serverData.ActiveTopic) Active.Topic = new Topic(serverData.ActiveTopic) - + setMap: function(serverData) { + var self = toExport if (serverData.Collaborators) self.Collaborators = new MapperCollection(serverData.Collaborators) - if (serverData.Creators) self.Creators = new MapperCollection(serverData.Creators) if (serverData.Mappers) self.Mappers = new MapperCollection(serverData.Mappers) if (serverData.Mappings) self.Mappings = new MappingCollection(serverData.Mappings) if (serverData.Messages) self.Messages = serverData.Messages @@ -75,42 +34,28 @@ const DataModel = { if (serverData.Stars) self.Stars = serverData.Stars if (serverData.Synapses) self.Synapses = new SynapseCollection(serverData.Synapses) if (serverData.Topics) self.Topics = new TopicCollection(serverData.Topics) - - // initialize global backbone models and collections - var myCollection = serverData.Mine ? serverData.Mine : [] - var sharedCollection = serverData.Shared ? serverData.Shared : [] - var starredCollection = serverData.Starred ? serverData.Starred : [] - var mapperCollection = serverData.Mapper ? serverData.Mapper : [] - var mapperOptionsObj = { id: 'mapper', sortBy: 'updated_at' } - if (serverData.Mapper && serverData.Mapper.mapperId) { - mapperCollection = serverData.Mapper.models - mapperOptionsObj.mapperId = serverData.Mapper.mapperId - } - var featuredCollection = serverData.Featured ? serverData.Featured : [] - var activeCollection = serverData.Active ? serverData.Active : [] - - self.Maps.Mine = new MapCollection(myCollection, { id: 'mine', sortBy: 'updated_at' }) - self.Maps.Shared = new MapCollection(sharedCollection, { id: 'shared', sortBy: 'updated_at' }) - self.Maps.Starred = new MapCollection(starredCollection, { id: 'starred', sortBy: 'updated_at' }) - // 'Mapper' refers to another mapper - self.Maps.Mapper = new MapCollection(mapperCollection, mapperOptionsObj) - self.Maps.Featured = new MapCollection(featuredCollection, { id: 'featured', sortBy: 'updated_at' }) - self.Maps.Active = new MapCollection(activeCollection, { id: 'active', sortBy: 'updated_at' }) - + self.attachCollectionEvents() + }, + setTopic: function(serverData) { + var self = toExport + if (serverData.Creators) self.Creators = new MapperCollection(serverData.Creators) + if (serverData.Metacodes) self.Metacodes = new MetacodeCollection(serverData.Metacodes) + if (serverData.Synapses) self.Synapses = new SynapseCollection(serverData.Synapses) + if (serverData.Topics) self.Topics = new TopicCollection(serverData.Topics) self.attachCollectionEvents() }, attachCollectionEvents: function() { - DataModel.Topics.on('add remove', function(topic) { + toExport.Topics.on('add remove', function(topic) { InfoBox.updateNumbers() Filter.checkMetacodes() Filter.checkMappers() }) - DataModel.Synapses.on('add remove', function(synapse) { + toExport.Synapses.on('add remove', function(synapse) { InfoBox.updateNumbers() Filter.checkSynapses() Filter.checkMappers() }) - DataModel.Mappings.on('add remove', function(mapping) { + toExport.Mappings.on('add remove', function(mapping) { InfoBox.updateNumbers() Filter.checkSynapses() Filter.checkMetacodes() @@ -118,7 +63,43 @@ const DataModel = { }) } } +return toExport +} +DataModel.Maps = { + Mine: [], + Shared: [], + Starred: [], + Mapper: { + models: [], + mapperId: null + }, + Featured: [], + Active: [] +} +DataModel.init = function(serverData) { + var self = DataModel + self.synapseIconUrl = serverData['synapse16.png'] + // initialize global backbone models and collections + var myCollection = serverData.Mine ? serverData.Mine : [] + var sharedCollection = serverData.Shared ? serverData.Shared : [] + var starredCollection = serverData.Starred ? serverData.Starred : [] + var mapperCollection = serverData.Mapper ? serverData.Mapper : [] + var mapperOptionsObj = { id: 'mapper', sortBy: 'updated_at' } + if (serverData.Mapper && serverData.Mapper.mapperId) { + mapperCollection = serverData.Mapper.models + mapperOptionsObj.mapperId = serverData.Mapper.mapperId + } + var featuredCollection = serverData.Featured ? serverData.Featured : [] + var activeCollection = serverData.Active ? serverData.Active : [] + self.Maps.Mine = new MapCollection(myCollection, { id: 'mine', sortBy: 'updated_at' }) + self.Maps.Shared = new MapCollection(sharedCollection, { id: 'shared', sortBy: 'updated_at' }) + self.Maps.Starred = new MapCollection(starredCollection, { id: 'starred', sortBy: 'updated_at' }) + // 'Mapper' refers to another mapper + self.Maps.Mapper = new MapCollection(mapperCollection, mapperOptionsObj) + self.Maps.Featured = new MapCollection(featuredCollection, { id: 'featured', sortBy: 'updated_at' }) + self.Maps.Active = new MapCollection(activeCollection, { id: 'active', sortBy: 'updated_at' }) +} // Note: Topics, Metacodes, Synapses, Mappers, Mappings, Collaborators, Creators are not exported // You can access them by importing DataModel diff --git a/frontend/src/Metamaps/Filter.js b/frontend/src/Metamaps/Filter.js index 7744a759..c14b552b 100644 --- a/frontend/src/Metamaps/Filter.js +++ b/frontend/src/Metamaps/Filter.js @@ -2,14 +2,11 @@ import _ from 'lodash' -import Active from './Active' -import Control from './Control' -import DataModel from './DataModel' import GlobalUI, { ReactApp } from './GlobalUI' import Settings from './Settings' -import Visualize from './Visualize' -const Filter = { +const Filter = ({Active, Control, DataModel, Visualize}) => { +const toExport = { dataForPresentation: { metacodes: {}, mappers: {}, @@ -26,7 +23,7 @@ const Filter = { synapses: [] }, reset: function() { - var self = Filter + var self = toExport self.filters.metacodes = [] self.filters.mappers = [] self.filters.synapses = [] @@ -41,7 +38,7 @@ const Filter = { // an abstraction function for checkMetacodes, checkMappers, checkSynapses to reduce // code redundancy updateFilters: function(collection, propertyToCheck, correlatedModel, filtersToUse, listToModify) { - var self = Filter + var self = toExport var newList = [] var removed = [] var added = [] @@ -97,11 +94,11 @@ const Filter = { ReactApp.render() }, checkMetacodes: function() { - var self = Filter + var self = toExport self.updateFilters('Topics', 'metacode_id', 'Metacodes', 'metacodes', 'metacode') }, checkMappers: function() { - var self = Filter + var self = toExport if (Active.Map) { self.updateFilters('Mappings', 'user_id', 'Mappers', 'mappers', 'mapper') } else { @@ -110,23 +107,23 @@ const Filter = { } }, checkSynapses: function() { - var self = Filter + var self = toExport self.updateFilters('Synapses', 'desc', 'Synapses', 'synapses', 'synapse') }, filterAllMetacodes: function(toVisible) { - var self = Filter + var self = toExport self.visible.metacodes = toVisible ? self.filters.metacodes.slice() : [] ReactApp.render() self.passFilters() }, filterAllMappers: function(toVisible) { - var self = Filter + var self = toExport self.visible.mappers = toVisible ? self.filters.mappers.slice() : [] ReactApp.render() self.passFilters() }, filterAllSynapses: function(toVisible) { - var self = Filter + var self = toExport self.visible.synapses = toVisible ? self.filters.synapses.slice() : [] ReactApp.render() self.passFilters() @@ -135,7 +132,7 @@ const Filter = { // to reduce code redundancy // gets called in the context of a list item in a filter box toggleLi: function(whichToFilter, id) { - var self = Filter + var self = toExport if (self.visible[whichToFilter].indexOf(id) === -1) { self.visible[whichToFilter].push(id) } else { @@ -146,19 +143,19 @@ const Filter = { self.passFilters() }, toggleMetacode: function(id) { - var self = Filter + var self = toExport self.toggleLi('metacodes', id) }, toggleMapper: function(id) { - var self = Filter + var self = toExport self.toggleLi('mappers', id) }, toggleSynapse: function(id) { - var self = Filter + var self = toExport self.toggleLi('synapses', id) }, passFilters: function() { - var self = Filter + var self = toExport var visible = self.visible var passesMetacode, passesMapper, passesSynapse @@ -276,5 +273,7 @@ const Filter = { }) } } +return toExport +} export default Filter diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index db850091..96f17b28 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -7,13 +7,12 @@ import { merge } from 'lodash' import { notifyUser } from './index.js' import ImportDialog from './ImportDialog' -import Active from '../Active' -import DataModel from '../DataModel' +import Mapper from '../DataModel/Mapper' import { ExploreMaps, ChatView, TopicCard } from '../Views' import Filter from '../Filter' import JIT from '../JIT' import Realtime from '../Realtime' -import Map, { InfoBox } from '../Map' +import Map, { mapControl } from '../Map' import Topic from '../Topic' import Visualize from '../Visualize' import makeRoutes from '../../components/makeRoutes' @@ -27,8 +26,11 @@ const MAX_COLUMNS = 4 const ReactApp = { serverData: {}, + currentUser: null, mapId: null, + openMap: null, topicId: null, + openTopic: null, unreadNotificationsCount: 0, mapsWidth: 0, toast: '', @@ -36,9 +38,11 @@ const ReactApp = { mobileTitle: '', mobileTitleWidth: 0, metacodeSets: [], + juntoState: { connectedPeople: {}, liveMaps: {} }, init: function(serverData, openLightbox) { const self = ReactApp self.serverData = serverData + self.currentUser = new Mapper(serverData.ActiveMapper) self.unreadNotificationsCount = serverData.unreadNotificationsCount self.mobileTitle = serverData.mobileTitle self.openLightbox = openLightbox @@ -52,12 +56,13 @@ const ReactApp = { const pathname = this.state.location.pathname switch (pathname.split('/')[1]) { case '': - if (Active.Mapper && Active.Mapper.id) { + if (self.currentUser && self.currentUser.id) { $('#yield').hide() ExploreMaps.updateFromPath(pathname) self.mapId = null - Active.Map = null - Active.Topic = null + self.topicId = null + self.openMap = null + self.openTopic = null } break case 'explore': @@ -65,19 +70,19 @@ const ReactApp = { ExploreMaps.updateFromPath(pathname) self.mapId = null self.topicId = null - Active.Map = null - Active.Topic = null + self.openMap = null + self.openTopic = null break case 'topics': $('#yield').hide() - Active.Map = null + self.openMap = null self.mapId = null self.topicId = pathname.split('/')[2] break case 'maps': if (!pathname.includes('request_access')) { $('#yield').hide() - Active.Topic = null + self.openTopic = null self.topicId = null self.mapId = pathname.split('/')[2] } @@ -99,14 +104,18 @@ const ReactApp = { const self = ReactApp return merge({ unreadNotificationsCount: self.unreadNotificationsCount, - currentUser: Active.Mapper, + currentUser: self.currentUser, toast: self.toast, mobile: self.mobile, mobileTitle: self.mobileTitle, mobileTitleWidth: self.mobileTitleWidth, - mobileTitleClick: (e) => Active.Map && InfoBox.toggleBox(e), + mobileTitleClick: (e) => self.openMap && self.openMap.InfoBox.toggleBox(e), openInviteLightbox: () => self.openLightbox('invite'), - serverData: self.serverData + serverData: self.serverData, + endActiveMap: mapControl.end, + launchNewMap: mapControl.launch, + mapId: self.mapId, + topicId: self.topicId }, self.getMapProps(), self.getTopicProps(), @@ -118,27 +127,27 @@ const ReactApp = { }, getMapProps: function() { const self = ReactApp + if (!self.openMap) return {} return { - mapId: self.mapId, - map: Active.Map, - hasLearnedTopicCreation: Map.hasLearnedTopicCreation, - userRequested: Map.userRequested, - requestAnswered: Map.requestAnswered, - requestApproved: Map.requestApproved, - onRequestAccess: Map.requestAccess, - mapIsStarred: Map.mapIsStarred, - endActiveMap: Map.end, - launchNewMap: Map.launch, - toggleMapInfoBox: InfoBox.toggleBox, - infoBoxHtml: InfoBox.html, + map: self.openMap.Active.Map, + hasLearnedTopicCreation: self.openMap.Map.hasLearnedTopicCreation, + userRequested: self.openMap.Map.userRequested, + requestAnswered: self.openMap.Map.requestAnswered, + requestApproved: self.openMap.Map.requestApproved, + onRequestAccess: self.openMap.Map.requestAccess, + mapIsStarred: self.openMap.Map.mapIsStarred, + toggleMapInfoBox: self.openMap.InfoBox.toggleBox, + infoBoxHtml: self.openMap.InfoBox.html, openImportLightbox: () => ImportDialog.show(), - forkMap: Map.fork, - onMapStar: Map.star, - onMapUnstar: Map.unstar + forkMap: self.openMap.Map.fork, + onMapStar: self.openMap.Map.star, + onMapUnstar: self.openMap.Map.unstar } }, getCommonProps: function() { const self = ReactApp + if (!(self.openMap || self.openTopic)) return {} + const { JIT, Visualize } = self.openMap || self.openTopic return { openHelpLightbox: () => self.openLightbox('cheatsheet'), onZoomExtents: event => JIT.zoomExtents(event, Visualize.mGraph.canvas), @@ -148,20 +157,22 @@ const ReactApp = { }, getTopicCardProps: function() { const self = ReactApp + if (!(self.openMap || self.openTopic)) return {} + const { TopicCard } = self.openMap || self.openTopic return { openTopic: TopicCard.openTopic, metacodeSets: self.metacodeSets, updateTopic: (topic, obj) => topic.save(obj), - onTopicFollow: Topic.onTopicFollow + onTopicFollow: Topic.onTopicFollow // todo } }, getTopicProps: function() { const self = ReactApp + if (!self.openTopic) return {} return { - topicId: self.topicId, - topic: Active.Topic, - endActiveTopic: Topic.end, - launchNewTopic: Topic.launch + topic: self.openTopic.Active.Topic, + endActiveTopic: Topic.end, // todo + launchNewTopic: Topic.launch // todo } }, getMapsProps: function() { @@ -169,7 +180,7 @@ const ReactApp = { return { section: ExploreMaps.collection && ExploreMaps.collection.id, maps: ExploreMaps.collection, - juntoState: Realtime.juntoState, + juntoState: self.juntoState, moreToLoad: ExploreMaps.collection && ExploreMaps.collection.page !== 'loadedAll', user: ExploreMaps.collection && ExploreMaps.collection.id === 'mapper' ? ExploreMaps.mapper : null, loadMore: ExploreMaps.loadMore, @@ -182,6 +193,11 @@ const ReactApp = { }, getChatProps: function() { const self = ReactApp + if (!self.openMap) return { + participants: [], + messages: [] + } + const { ChatView, Realtime } = self.openMap return { unreadMessages: ChatView.unreadMessages, conversationLive: ChatView.conversationLive, @@ -204,6 +220,8 @@ const ReactApp = { }, getFilterProps: function() { const self = ReactApp + if (!self.openMap) return {} + const { Filter } = self.openMap return { filterData: Filter.dataForPresentation, allForFiltering: Filter.filters, @@ -219,7 +237,7 @@ const ReactApp = { resize: function() { const self = ReactApp const maps = ExploreMaps.collection - const currentUser = Active.Mapper + const currentUser = self.currentUser const user = maps && maps.id === 'mapper' ? ExploreMaps.mapper : null const numCards = (maps ? maps.length : 0) + (user || currentUser ? 1 : 0) const mapSpaces = Math.floor(document.body.clientWidth / MAP_WIDTH) diff --git a/frontend/src/Metamaps/Import.js b/frontend/src/Metamaps/Import.js index 54e256b2..b47299a4 100644 --- a/frontend/src/Metamaps/Import.js +++ b/frontend/src/Metamaps/Import.js @@ -3,15 +3,10 @@ import parse from 'csv-parse' import _ from 'lodash' -import Active from './Active' -import AutoLayout from './AutoLayout' -import DataModel from './DataModel' import GlobalUI from './GlobalUI' -import Map from './Map' -import Synapse from './Synapse' -import Topic from './Topic' -const Import = { +const Import = ({Active, AutoLayout, DataModel, Map, Synapse, Topic}) => { +const toExport = { // note that user is not imported topicWhitelist: [ 'id', 'name', 'metacode', 'x', 'y', 'description', 'link', 'permission' @@ -22,12 +17,12 @@ const Import = { cidMappings: {}, // to be filled by importId => cid mappings handleTSV: function(text) { - const results = Import.parseTabbedString(text) - Import.handle(results) + const results = toExport.parseTabbedString(text) + toExport.handle(results) }, handleCSV: function(text, parserOpts = {}) { - const self = Import + const self = toExport const topicsRegex = /("?Topics"?[, \t"]*)([\s\S]*)/mi const synapsesRegex = /("?Synapses"?[, \t"]*)([\s\S]*)/mi @@ -68,11 +63,11 @@ const Import = { handleJSON: function(text) { const results = JSON.parse(text) - Import.handle(results) + toExport.handle(results) }, handle: function(results) { - var self = Import + var self = toExport var topics = results.topics.map(topic => self.normalizeKeys(topic)) var synapses = results.synapses.map(synapse => self.normalizeKeys(synapse)) @@ -87,7 +82,7 @@ const Import = { }, parseTabbedString: function(text) { - var self = Import + var self = toExport // determine line ending and split lines var delim = '\n' @@ -213,7 +208,7 @@ const Import = { }, importTopics: function(parsedTopics) { - var self = Import + var self = toExport parsedTopics.forEach(topic => { let coords = { x: topic.x, y: topic.y } @@ -240,7 +235,7 @@ const Import = { }, importSynapses: function(parsedSynapses) { - var self = Import + var self = toExport parsedSynapses.forEach(function(synapse) { // only createSynapseWithParameters once both topics are persisted @@ -279,7 +274,7 @@ const Import = { createTopicWithParameters: function(name, metacodeName, permission, desc, link, xloc, yloc, importId, opts = {}) { - var self = Import + var self = toExport $(document).trigger(Map.events.editedByActiveMapper) var metacode = DataModel.Metacodes.where({name: metacodeName})[0] || null if (metacode === null) { @@ -359,7 +354,7 @@ const Import = { const permission = opts.permission || null // use default const desc = opts.desc || url - Import.createTopicWithParameters( + toExport.createTopicWithParameters( name, metacode, permission, @@ -423,5 +418,7 @@ const Import = { }) } } +return toExport +} export default Import diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index 6f953782..419c5cc8 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -35,17 +35,6 @@ return { animationDone: 'Metamaps:JIT:events:animationDone' }, vizData: [], // contains the visualization-compatible graph - /** - * This method will bind the event handlers it is interested and initialize the class. - */ - init: function(serverData) { - const self = JIT - self.topicDescImage = new Image() - self.topicDescImage.src = serverData['topic_description_signifier.png'] - - self.topicLinkImage = new Image() - self.topicLinkImage.src = serverData['topic_link_signifier.png'] - }, /** * convert our topic JSON into something JIT can use */ @@ -1968,5 +1957,12 @@ return { } } } +JIT.init = function(serverData) { + JIT.topicDescImage = new Image() + JIT.topicDescImage.src = serverData['topic_description_signifier.png'] + + JIT.topicLinkImage = new Image() + JIT.topicLinkImage.src = serverData['topic_link_signifier.png'] +} export default JIT diff --git a/frontend/src/Metamaps/Listeners.js b/frontend/src/Metamaps/Listeners.js index 48fd0747..74be079f 100644 --- a/frontend/src/Metamaps/Listeners.js +++ b/frontend/src/Metamaps/Listeners.js @@ -5,9 +5,9 @@ import { Search } from './GlobalUI' const Listeners = ({ Active, Create, Control, DataModel, JIT, Realtime, Selected, Topic, Visualize }) => { return { - init: function() { + activate: function() { var self = this - $(document).on('keydown', function(e) { + $(document).on('keydown.map', function(e) { if (!(Active.Map || Active.Topic)) return const onCanvas = e.target.tagName === 'BODY' @@ -131,7 +131,7 @@ return { break } }) - $(window).resize(function() { + $(window).on('resize.map', function() { if (Visualize && Visualize.mGraph) { Util.resizeCanvas(Visualize.mGraph.canvas) } diff --git a/frontend/src/Metamaps/Map/InfoBox.js b/frontend/src/Metamaps/Map/InfoBox.js index 3f6fcd9c..f26aafc0 100644 --- a/frontend/src/Metamaps/Map/InfoBox.js +++ b/frontend/src/Metamaps/Map/InfoBox.js @@ -3,12 +3,11 @@ import outdent from 'outdent' import { browserHistory } from 'react-router' -import Active from '../Active' -import DataModel from '../DataModel' import GlobalUI, { ReactApp } from '../GlobalUI' import Util from '../Util' -const InfoBox = { +const InfoBox = ({Active, DataModel}) => { +const toExport = { isOpen: false, selectingPermission: false, changePermissionText: "
As the creator, you can change the permission of this map, and the permission of all the topics and synapses you have authority to change will change as well.
", @@ -36,32 +35,32 @@ const InfoBox = { userImageUrl: '', html: '', init: function(serverData, updateThumbnail) { - var self = InfoBox + var self = toExport self.updateThumbnail = updateThumbnail - $('.mapInfoBox').click(function(event) { + $('.maptoExport').click(function(event) { event.stopPropagation() }) $('body').click(self.close) self.attachEventListeners() - self.generateBoxHTML = Hogan.compile($('#mapInfoBoxTemplate').html()) + self.generateBoxHTML = Hogan.compile($('#maptoExportTemplate').html()) self.userImageUrl = serverData['user.png'] var querystring = window.location.search.replace(/^\?/, '') if (querystring === 'new') { self.open() - $('.mapInfoBox').addClass('mapRequestTitle') + $('.maptoExport').addClass('mapRequestTitle') $('#mapInfoName').trigger('click') $('#mapInfoName textarea').focus() $('#mapInfoName textarea').select() } }, toggleBox: function(event) { - var self = InfoBox + var self = toExport if (self.isOpen) self.close() else self.open() @@ -69,23 +68,23 @@ const InfoBox = { event.stopPropagation() }, open: function() { - var self = InfoBox + var self = toExport $('.mapInfoIcon div').addClass('hide') - $('.mapInfoBox').fadeIn(200, function() { + $('.maptoExport').fadeIn(200, function() { self.isOpen = true }) }, close: function() { - var self = InfoBox + var self = toExport $('.mapInfoIcon div').removeClass('hide') - $('.mapInfoBox').fadeOut(200, function() { + $('.maptoExport').fadeOut(200, function() { self.isOpen = false self.hidePermissionSelect() $('.mapContributors .tip').hide() }) }, load: function() { - var self = InfoBox + var self = toExport var map = Active.Map @@ -115,12 +114,12 @@ const InfoBox = { self.attachEventListeners() }, attachEventListeners: function() { - var self = InfoBox + var self = toExport - $('.mapInfoBox.canEdit .best_in_place').best_in_place() + $('.maptoExport.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') + var bipName = $('.maptoExport .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] @@ -144,7 +143,7 @@ const InfoBox = { Active.Map.trigger('saved') // mobile menu $('#header_content').html(name) - $('.mapInfoBox').removeClass('mapRequestTitle') + $('.maptoExport').removeClass('mapRequestTitle') document.title = `${name} | Metamaps` window.history.replaceState('', `${name} | Metamaps`, window.location.pathname) }) @@ -164,8 +163,8 @@ const InfoBox = { $('.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) + // not a reference to the class .yourMap on the .maptoExport + $('.maptoExport.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect) $('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap) $('.mapInfoThumbnail').unbind().click(self.updateThumbnail) @@ -178,14 +177,14 @@ const InfoBox = { event.stopPropagation() }) - $('.mapInfoBox').unbind('.hideTip').bind('click.hideTip', function() { + $('.maptoExport').unbind('.hideTip').bind('click.hideTip', function() { $('.mapContributors .tip').hide() }) self.addTypeahead() }, addTypeahead: function() { - var self = InfoBox + var self = toExport if (!Active.Map) return @@ -232,14 +231,14 @@ const InfoBox = { } }, removeCollaborator: function(collaboratorId) { - var self = InfoBox + var self = toExport DataModel.Collaborators.remove(DataModel.Collaborators.get(collaboratorId)) var mapperIds = DataModel.Collaborators.models.map(function(mapper) { return mapper.id }) $.post('/maps/' + Active.Map.id + '/access', { access: mapperIds }) self.updateNumbers() }, addCollaborator: function(newCollaboratorId) { - var self = InfoBox + var self = toExport if (DataModel.Collaborators.get(newCollaboratorId)) { GlobalUI.notifyUser('That user already has access') @@ -258,16 +257,16 @@ const InfoBox = { $.getJSON('/users/' + newCollaboratorId + '.json', callback) }, handleResultClick: function(event, item) { - var self = InfoBox + var self = toExport self.addCollaborator(item.id) $('.collaboratorSearchField').typeahead('val', '') }, updateNameDescPerm: function(name, desc, perm) { - $('.mapInfoBox').removeClass('mapRequestTitle') + $('.maptoExport').removeClass('mapRequestTitle') $('.mapInfoName .best_in_place_name').html(name) $('.mapInfoDesc .best_in_place_desc').html(desc) - $('.mapInfoBox .mapPermission').removeClass('commons public private').addClass(perm) + $('.maptoExport .mapPermission').removeClass('commons public private').addClass(perm) }, createContributorList: function() { var relevantPeople = Active.Map.get('permission') === 'commons' ? DataModel.Mappers : DataModel.Collaborators @@ -294,7 +293,7 @@ const InfoBox = { updateNumbers: function() { if (!Active.Map) return - const self = InfoBox + const self = toExport var relevantPeople = Active.Map.get('permission') === 'commons' ? DataModel.Mappers : DataModel.Collaborators @@ -323,7 +322,7 @@ const InfoBox = { $('.mapEditedAt').html('Last edited: ' + Util.nowDateFormatted()) }, onPermissionClick: function(event) { - var self = InfoBox + var self = toExport if (!self.selectingPermission) { self.selectingPermission = true @@ -340,14 +339,14 @@ const InfoBox = { } }, hidePermissionSelect: function() { - var self = InfoBox + var self = toExport 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 = InfoBox + var self = toExport self.selectingPermission = false var permission = $(this).attr('class') @@ -358,7 +357,7 @@ const InfoBox = { const shareable = permission === 'private' ? '' : 'shareable' $('.mapPermission').removeClass('commons public private minimize').addClass(permission) $('.mapPermission .permissionSelect').remove() - $('.mapInfoBox').removeClass('shareable').addClass(shareable) + $('.maptoExport').removeClass('shareable').addClass(shareable) event.stopPropagation() }, deleteActiveMap: function() { @@ -371,7 +370,7 @@ const InfoBox = { var authorized = map.authorizePermissionChange(mapper) if (doIt && authorized) { - InfoBox.close() + toExport.close() DataModel.Maps.Active.remove(map) DataModel.Maps.Featured.remove(map) DataModel.Maps.Mine.remove(map) @@ -384,5 +383,7 @@ const InfoBox = { } } } +return toExport +} export default InfoBox diff --git a/frontend/src/Metamaps/Map/index.js b/frontend/src/Metamaps/Map/index.js index 1f0c8ac7..11de3fcc 100644 --- a/frontend/src/Metamaps/Map/index.js +++ b/frontend/src/Metamaps/Map/index.js @@ -6,414 +6,442 @@ import { browserHistory } from 'react-router' import Active from '../Active' import AutoLayout from '../AutoLayout' +import Cable from '../Cable' +import Control from '../Control' import Create from '../Create' import DataModel from '../DataModel' import DataModelMap from '../DataModel/Map' +import MapperCollection from '../DataModel/MapperCollection' +import TopicCollection from '../DataModel/TopicCollection' +import SynapseCollection from '../DataModel/SynapseCollection' +import MappingCollection from '../DataModel/MappingCollection' import Filter from '../Filter' import GlobalUI, { ReactApp } from '../GlobalUI' +import Import from '../Import' +import InfoBox from './InfoBox' import JIT from '../JIT' import Listeners from '../Listeners' import Loading from '../Loading' +import Mouse from '../Mouse' import Organize from '../Organize' +import PasteInput from '../PasteInput' import Realtime from '../Realtime' import Selected from '../Selected' +import Synapse from '../Synapse' import SynapseCard from '../SynapseCard' import Topic from '../Topic' import TopicCard from '../Views/TopicCard' +import ChatView from '../Views/ChatView' import Visualize from '../Visualize' import CheatSheet from './CheatSheet' -import InfoBox from './InfoBox' -const Map = { - events: { - editedByActiveMapper: 'Metamaps:Map:events:editedByActiveMapper' - }, - mapIsStarred: false, - requests: [], - userRequested: false, - requestAnswered: false, - requestApproved: false, - hasLearnedTopicCreation: true, - init: function(serverData) { - var self = Map - self.mapIsStarred = serverData.mapIsStarred - self.requests = serverData.requests - self.setAccessRequest() - $('#wrapper').mousedown(function(e) { - if (e.button === 1) return false - }) - GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html() - InfoBox.init(serverData, function updateThumbnail() { - self.uploadMapScreenshot() - }) - CheatSheet.init(serverData) - $(document).on(Map.events.editedByActiveMapper, self.editedByActiveMapper) - }, - setHasLearnedTopicCreation: function(value) { - const self = Map - self.hasLearnedTopicCreation = value - ReactApp.render() - }, - requestAccess: function() { - const self = Map - self.requests.push({ - user_id: Active.Mapper.id, - answered: false, - approved: false - }) - self.setAccessRequest() - const mapId = Active.Map.id - $.post({ - url: `/maps/${mapId}/access_request` - }) - GlobalUI.notifyUser('Map creator will be notified of your request') - }, - setAccessRequest: function() { - const self = Map - if (Active.Mapper) { - const request = _find(self.requests, r => r.user_id === Active.Mapper.id) - if (!request) { - self.userRequested = false - self.requestAnswered = false - self.requestApproved = false +const mapControl = { + launch: function(id, serverData) { + var dataIsReadySetupMap = function(data) { + const newMap = { + Active: null, + AutoLayout: null, + Cable: null, + ChatView: null, + Control: null, + Create: null, + DataModel: null, + Filter: null, + Import: null, + JIT: null, + Listeners: null, + Mouse: null, + Organize: null, + PasteInput: null, + Realtime: null, + Selected: null, + Synapse: null, + SynapseCard: null, + Topic: null, + TopicCard: null, + Visualize: null } - else if (request && !request.answered) { - self.userRequested = true - self.requestAnswered = false - self.requestApproved = false - } - else if (request && request.answered && !request.approved) { - self.userRequested = true - self.requestAnswered = true - self.requestApproved = false - } - } - ReactApp.render() - }, - launch: function(id) { - const self = Map - - const newMap = { - Active: null, - AutoLayout: null, - Cable: null, - Control: null, - Create: null, - DataModel: null, - Filter: null, - Import: null, - JIT: null, - Listeners: null, - Loading: null, - Map: null, - Mouse: null, - Organize: null, - PasteInput: null, - Realtime: null, - Selected: null, - Settings: null, - Synapse: null, - SynapseCard: null, - Topic: null, - Views: null, - Visualize: null - } - newMap.JIT = JIT(newMap) - newMap.Listeners = Listeners(newMap) - console.log(newMap) - - var dataIsReadySetupMap = function() { - Map.setAccessRequest() - Visualize.type = 'ForceDirected' + newMap.Active = Active() + newMap.AutoLayout = AutoLayout(newMap) + newMap.Cable = Cable(newMap) + newMap.ChatView = ChatView(newMap) + newMap.Control = Control(newMap) + newMap.Create = Create(newMap) + newMap.DataModel = DataModel(newMap) + newMap.Filter = Filter(newMap) + newMap.Import = Import(newMap) + newMap.InfoBox = InfoBox(newMap) + newMap.JIT = JIT(newMap) + newMap.Listeners = Listeners(newMap) + newMap.Map = Map(newMap) + newMap.Mouse = Mouse(newMap) + newMap.Organize = Organize(newMap) + newMap.PasteInput = PasteInput(newMap) + newMap.Realtime = Realtime(newMap) + newMap.Selected = Selected(newMap) + newMap.Synapse = Synapse(newMap) + newMap.SynapseCard = SynapseCard(newMap) + newMap.Topic = Topic(newMap) + newMap.TopicCard = TopicCard(newMap) + newMap.Visualize = Visualize(newMap) + + console.log(newMap) + + newMap.Active.Map = new DataModelMap(data.map) + newMap.DataModel.Mappers = new MapperCollection(data.mappers) + newMap.DataModel.Collaborators = new MapperCollection(data.collaborators) + newMap.DataModel.Topics = new TopicCollection(data.topics) + newMap.DataModel.Synapses = new SynapseCollection(data.synapses) + newMap.DataModel.Mappings = new MappingCollection(data.mappings) + newMap.DataModel.Messages = data.messages + newMap.DataModel.Stars = data.stars + newMap.DataModel.attachCollectionEvents() + newMap.Map.requests = data.requests + newMap.Map.setAccessRequest() + newMap.Visualize.type = 'ForceDirected' newMap.JIT.prepareVizData() - Selected.reset() - InfoBox.load() - Filter.reset() - Filter.checkMetacodes() - Filter.checkSynapses() - Filter.checkMappers() - Realtime.startActiveMap() + newMap.InfoBox.load() + newMap.Filter.checkMetacodes() + newMap.Filter.checkSynapses() + newMap.Filter.checkMappers() + newMap.Realtime.startActiveMap() Loading.hide() - document.title = Active.Map.get('name') + ' | Metamaps' - ReactApp.mobileTitle = Active.Map.get('name') + document.title = newMap.Active.Map.get('name') + ' | Metamaps' + ReactApp.openMap = newMap + ReactApp.mobileTitle = newMap.Active.Map.get('name') ReactApp.render() } - function isLoaded() { - if (InfoBox.generateBoxHTML) dataIsReadySetupMap() - else setTimeout(() => isLoaded(), 50) - } - if (Active.Map && Active.Map.id === id) { - isLoaded() + if (false) { + // do something with serverData here + dataIsReadySetupMap() } else { Loading.show() $.ajax({ url: '/maps/' + id + '/contains.json', - success: function(data) { - Active.Map = new DataModelMap(data.map) - DataModel.Mappers = new DataModel.MapperCollection(data.mappers) - DataModel.Collaborators = new DataModel.MapperCollection(data.collaborators) - DataModel.Topics = new DataModel.TopicCollection(data.topics) - DataModel.Synapses = new DataModel.SynapseCollection(data.synapses) - DataModel.Mappings = new DataModel.MappingCollection(data.mappings) - DataModel.Messages = data.messages - DataModel.Stars = data.stars - DataModel.attachCollectionEvents() - self.requests = data.requests - isLoaded() - } + success: dataIsReadySetupMap }) } }, - end: function() { - if (Active.Map) { - $('.main').removeClass('compressed') - AutoLayout.resetSpiral() - $('.rightclickmenu').remove() - TopicCard.hideCard() - SynapseCard.hideCard() - Create.newTopic.hide(true) // true means force (and override pinned) - Create.newSynapse.hide() - InfoBox.close() - Realtime.endActiveMap() - self.requests = [] - self.hasLearnedTopicCreation = true - } - }, - star: function() { - var self = Map - - if (!Active.Map) return - $.post('/maps/' + Active.Map.id + '/star') - DataModel.Stars.push({ user_id: Active.Mapper.id, map_id: Active.Map.id }) - DataModel.Maps.Starred.add(Active.Map) - GlobalUI.notifyUser('Map is now starred') - self.mapIsStarred = true - ReactApp.render() - }, - unstar: function() { - var self = Map - - if (!Active.Map) return - $.post('/maps/' + Active.Map.id + '/unstar') - DataModel.Stars = DataModel.Stars.filter(function(s) { return s.user_id !== Active.Mapper.id }) - DataModel.Maps.Starred.remove(Active.Map) - self.mapIsStarred = false - ReactApp.render() - }, - fork: function() { - GlobalUI.openLightbox('forkmap') - - let nodesData = '' - let synapsesData = '' - let nodesArray = [] - let synapsesArray = [] - // collect the unfiltered topics - 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 - nodesArray.push(id) - let x, y - if (n.pos.x && n.pos.y) { - x = n.pos.x - y = n.pos.y - } else { - x = Math.cos(n.pos.theta) * n.pos.rho - y = Math.sin(n.pos.theta) * n.pos.rho - } - nodesData += id + '/' + x + '/' + y + ',' - } - }) - // collect the unfiltered synapses - DataModel.Synapses.each(function(synapse) { - var desc = synapse.get('desc') - - var descNotFiltered = 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 = nodesArray.indexOf(synapse.get('topic1_id')) > -1 - topicsNotFiltered = topicsNotFiltered && nodesArray.indexOf(synapse.get('topic2_id')) > -1 - if (descNotFiltered && topicsNotFiltered) { - synapsesArray.push(synapse.id) - } - }) - - synapsesData = synapsesArray.join() - nodesData = nodesData.slice(0, -1) - - GlobalUI.CreateMap.topicsToMap = nodesData - GlobalUI.CreateMap.synapsesToMap = synapsesData - }, - leavePrivateMap: function() { - var map = Active.Map - DataModel.Maps.Active.remove(map) - DataModel.Maps.Featured.remove(map) - browserHistory.push('/') - GlobalUI.notifyUser('Sorry! That map has been changed to Private.') - }, - cantEditNow: function() { - Realtime.turnOff(true) // true is for 'silence' - GlobalUI.notifyUser('Map was changed to Public. Editing is disabled.') - Active.Map.trigger('changeByOther') - }, - canEditNow: function() { - var confirmString = "You've been granted permission to edit this map. " - confirmString += 'Do you want to reload and enable realtime collaboration?' - var c = window.confirm(confirmString) - if (c) { - window.location.reload() - } - }, - editedByActiveMapper: function() { - if (Active.Mapper) { - DataModel.Mappers.add(Active.Mapper) - } - }, - offerScreenshotDownload: () => { - const canvas = Map.getMapCanvasForScreenshots() - const filename = Map.getMapScreenshotFilename(Active.Map) - - var downloadMessage = outdent` - Captured map screenshot! - - DOWNLOAD - ` - GlobalUI.notifyUser(downloadMessage) - }, - uploadMapScreenshot: () => { - const canvas = Map.getMapCanvasForScreenshots() - const filename = Map.getMapScreenshotFilename(Active.Map) - - canvas.canvas.toBlob(imageBlob => { - const formData = new window.FormData() - formData.append('map[screenshot]', imageBlob, filename) - $.ajax({ - type: 'PATCH', - dataType: 'json', - url: `/maps/${Active.Map.id}`, - data: formData, - processData: false, - contentType: false, - success: function(data) { - GlobalUI.notifyUser('Successfully updated map screenshot.') - }, - error: function() { - GlobalUI.notifyUser('Failed to update map screenshot.') - } - }) - }) - }, - getMapCanvasForScreenshots: () => { - 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 - this.size = { - width: canvas.width, - height: canvas.height - } - return this.size - } - canvas.scale = function(x, y) { - const px = this.scaleOffsetX * x - const py = this.scaleOffsetY * y - const dx = this.translateOffsetX * (x - 1) / px - const 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) { - const sx = this.scaleOffsetX - const 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 = 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 - JIT.zoomExtents(null, canvas, true) - - const c = canvas.canvas - const ctx = canvas.getCtx() - const scale = canvas.scaleOffsetX - - // draw a grey background - ctx.fillStyle = '#d8d9da' - const xPoint = (-(c.width / scale) / 2) - (canvas.translateOffsetX / scale) - const 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 - }) - - return canvas - }, - getMapScreenshotFilename: 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(['-']) - const filename = `metamap-${map.id}-${mapName}-${today}.png` - return filename + end: function(map) { + $('.main').removeClass('compressed') + $('.rightclickmenu').remove() + map.AutoLayout.resetSpiral() + map.TopicCard.hideCard() + map.SynapseCard.hideCard() + map.Create.newTopic.hide(true) // true means force (and override pinned) + map.Create.newSynapse.hide() + map.InfoBox.close() + map.Realtime.endActiveMap() + map.Map.requests = [] + map.Map.hasLearnedTopicCreation = true } } +export { mapControl } -export { CheatSheet, InfoBox } +const Map = ({Active, DataModel, JIT, Visualize, Realtime}) => { + const toExport = { + mapIsStarred: false, + requests: [], + userRequested: false, + requestAnswered: false, + requestApproved: false, + hasLearnedTopicCreation: true, + init: function(serverData) { + var self = toExport + self.mapIsStarred = serverData.mapIsStarred + self.requests = serverData.requests + self.setAccessRequest() + $('#wrapper').mousedown(function(e) { + if (e.button === 1) return false + }) + GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html() + //InfoBox.init(serverData, function updateThumbnail() { + // self.uploadMapScreenshot() + //}) + CheatSheet.init(serverData) + $(document).on(Map.events.editedByActiveMapper, self.editedByActiveMapper) + }, + setHasLearnedTopicCreation: function(value) { + const self = toExport + self.hasLearnedTopicCreation = value + ReactApp.render() + }, + requestAccess: function() { + const self = toExport + self.requests.push({ + user_id: Active.Mapper.id, + answered: false, + approved: false + }) + self.setAccessRequest() + const mapId = Active.Map.id + $.post({ + url: `/maps/${mapId}/access_request` + }) + GlobalUI.notifyUser('Map creator will be notified of your request') + }, + setAccessRequest: function() { + const self = toExport + if (Active.Mapper) { + const request = _find(self.requests, r => r.user_id === Active.Mapper.id) + if (!request) { + self.userRequested = false + self.requestAnswered = false + self.requestApproved = false + } + else if (request && !request.answered) { + self.userRequested = true + self.requestAnswered = false + self.requestApproved = false + } + else if (request && request.answered && !request.approved) { + self.userRequested = true + self.requestAnswered = true + self.requestApproved = false + } + } + ReactApp.render() + }, + star: function() { + var self = toExport + + if (!Active.Map) return + $.post('/maps/' + Active.Map.id + '/star') + DataModel.Stars.push({ user_id: Active.Mapper.id, map_id: Active.Map.id }) + DataModel.Maps.Starred.add(Active.Map) + GlobalUI.notifyUser('Map is now starred') + self.mapIsStarred = true + ReactApp.render() + }, + unstar: function() { + var self = toExport + + if (!Active.Map) return + $.post('/maps/' + Active.Map.id + '/unstar') + DataModel.Stars = DataModel.Stars.filter(function(s) { return s.user_id !== Active.Mapper.id }) + DataModel.Maps.Starred.remove(Active.Map) + self.mapIsStarred = false + ReactApp.render() + }, + fork: function() { + GlobalUI.openLightbox('forkmap') + + let nodesData = '' + let synapsesData = '' + let nodesArray = [] + let synapsesArray = [] + // collect the unfiltered topics + 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 + nodesArray.push(id) + let x, y + if (n.pos.x && n.pos.y) { + x = n.pos.x + y = n.pos.y + } else { + x = Math.cos(n.pos.theta) * n.pos.rho + y = Math.sin(n.pos.theta) * n.pos.rho + } + nodesData += id + '/' + x + '/' + y + ',' + } + }) + // collect the unfiltered synapses + DataModel.Synapses.each(function(synapse) { + var desc = synapse.get('desc') + + var descNotFiltered = 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 = nodesArray.indexOf(synapse.get('topic1_id')) > -1 + topicsNotFiltered = topicsNotFiltered && nodesArray.indexOf(synapse.get('topic2_id')) > -1 + if (descNotFiltered && topicsNotFiltered) { + synapsesArray.push(synapse.id) + } + }) + + synapsesData = synapsesArray.join() + nodesData = nodesData.slice(0, -1) + + GlobalUI.CreateMap.topicsToMap = nodesData + GlobalUI.CreateMap.synapsesToMap = synapsesData + }, + leavePrivateMap: function() { + var map = Active.Map + DataModel.Maps.Active.remove(map) + DataModel.Maps.Featured.remove(map) + browserHistory.push('/') + GlobalUI.notifyUser('Sorry! That map has been changed to Private.') + }, + cantEditNow: function() { + Realtime.turnOff(true) // true is for 'silence' + GlobalUI.notifyUser('Map was changed to Public. Editing is disabled.') + Active.Map.trigger('changeByOther') + }, + canEditNow: function() { + var confirmString = "You've been granted permission to edit this map. " + confirmString += 'Do you want to reload and enable realtime collaboration?' + var c = window.confirm(confirmString) + if (c) { + window.location.reload() + } + }, + editedByActiveMapper: function() { + if (Active.Mapper) { + DataModel.Mappers.add(Active.Mapper) + } + }, + offerScreenshotDownload: () => { + const canvas = toExport.getMapCanvasForScreenshots() + const filename = toExport.getMapScreenshotFilename(Active.Map) + + var downloadMessage = outdent` + Captured map screenshot! + + DOWNLOAD + ` + GlobalUI.notifyUser(downloadMessage) + }, + uploadMapScreenshot: () => { + const canvas = toExport.getMapCanvasForScreenshots() + const filename = toExport.getMapScreenshotFilename(Active.Map) + + canvas.canvas.toBlob(imageBlob => { + const formData = new window.FormData() + formData.append('map[screenshot]', imageBlob, filename) + $.ajax({ + type: 'PATCH', + dataType: 'json', + url: `/maps/${Active.Map.id}`, + data: formData, + processData: false, + contentType: false, + success: function(data) { + GlobalUI.notifyUser('Successfully updated map screenshot.') + }, + error: function() { + GlobalUI.notifyUser('Failed to update map screenshot.') + } + }) + }) + }, + getMapCanvasForScreenshots: () => { + 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 + this.size = { + width: canvas.width, + height: canvas.height + } + return this.size + } + canvas.scale = function(x, y) { + const px = this.scaleOffsetX * x + const py = this.scaleOffsetY * y + const dx = this.translateOffsetX * (x - 1) / px + const 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) { + const sx = this.scaleOffsetX + const 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 = 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 + JIT.zoomExtents(null, canvas, true) + + const c = canvas.canvas + const ctx = canvas.getCtx() + const scale = canvas.scaleOffsetX + + // draw a grey background + ctx.fillStyle = '#d8d9da' + const xPoint = (-(c.width / scale) / 2) - (canvas.translateOffsetX / scale) + const 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 + }) + + return canvas + }, + getMapScreenshotFilename: 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(['-']) + const filename = `metamap-${map.id}-${mapName}-${today}.png` + return filename + } + } + return toExport +} +Map.events = { + editedByActiveMapper: 'Metamaps:Map:events:editedByActiveMapper' +} + +export { CheatSheet } export default Map diff --git a/frontend/src/Metamaps/Mouse.js b/frontend/src/Metamaps/Mouse.js index 9989bc20..49f6b6f4 100644 --- a/frontend/src/Metamaps/Mouse.js +++ b/frontend/src/Metamaps/Mouse.js @@ -1,16 +1,18 @@ -const Mouse = { - didPan: false, - didBoxZoom: false, - changeInX: 0, - changeInY: 0, - edgeHoveringOver: false, - boxStartCoordinates: false, - boxEndCoordinates: false, - synapseStartCoordinates: [], - synapseEndCoordinates: null, - lastNodeClick: 0, - lastCanvasClick: 0, - DOUBLE_CLICK_TOLERANCE: 300 +const Mouse = () => { + return { + didPan: false, + didBoxZoom: false, + changeInX: 0, + changeInY: 0, + edgeHoveringOver: false, + boxStartCoordinates: false, + boxEndCoordinates: false, + synapseStartCoordinates: [], + synapseEndCoordinates: null, + lastNodeClick: 0, + lastCanvasClick: 0, + DOUBLE_CLICK_TOLERANCE: 300 + } } export default Mouse diff --git a/frontend/src/Metamaps/Organize.js b/frontend/src/Metamaps/Organize.js index 09d6e261..8852d98c 100644 --- a/frontend/src/Metamaps/Organize.js +++ b/frontend/src/Metamaps/Organize.js @@ -2,10 +2,8 @@ import _ from 'lodash' import $jit from '../patched/JIT' -import Visualize from './Visualize' -import JIT from './JIT' - -const Organize = { +const Organize = ({Visualize, JIT}) => { +const toExport = { arrange: function(layout, centerNode) { // first option for layout to implement is 'grid', will do an evenly spaced grid with its center at the 0,0 origin if (layout === 'grid') { @@ -112,5 +110,7 @@ const Organize = { } } } +return toExport +} export default Organize diff --git a/frontend/src/Metamaps/PasteInput.js b/frontend/src/Metamaps/PasteInput.js index 03a92f86..9a209618 100644 --- a/frontend/src/Metamaps/PasteInput.js +++ b/frontend/src/Metamaps/PasteInput.js @@ -1,16 +1,15 @@ /* global $ */ -import Import from './Import' import Util from './Util' -import Visualize from './Visualize' -const PasteInput = { +const PasteInput = ({Import, Visualize}) => { +const toReturn = { // thanks to https://github.com/kevva/url-regex // eslint-disable-next-line no-useless-escape URL_REGEX: new RegExp('^(?:(?:(?:[a-z]+:)?//)|www\.)(?:\S+(?::\S*)?@)?(?:localhost|(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])){3}|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#][^\s"]*)?$'), init: function() { - var self = PasteInput + var self = toReturn // intercept dragged files // see http://stackoverflow.com/questions/6756583 @@ -50,7 +49,7 @@ const PasteInput = { }, handleFile: (file, coords = null) => { - var self = PasteInput + var self = toReturn var fileReader = new window.FileReader() fileReader.readAsText(file) fileReader.onload = function(e) { @@ -64,7 +63,7 @@ const PasteInput = { }, handle: function(text, coords = {}) { - var self = PasteInput + var self = toReturn if (text.match(self.URL_REGEX)) { Import.handleURL(text, coords) @@ -78,5 +77,7 @@ const PasteInput = { } } } +return toReturn +} export default PasteInput diff --git a/frontend/src/Metamaps/Realtime/index.js b/frontend/src/Metamaps/Realtime/index.js index 9e15ef62..a39be2cc 100644 --- a/frontend/src/Metamaps/Realtime/index.js +++ b/frontend/src/Metamaps/Realtime/index.js @@ -3,13 +3,8 @@ import SimpleWebRTC from 'simplewebrtc' import SocketIoConnection from 'simplewebrtc/socketioconnection' -import Active from '../Active' -import Cable from '../Cable' -import DataModel from '../DataModel' -import JIT from '../JIT' import Util from '../Util' -import Views, { ChatView } from '../Views' -import Visualize from '../Visualize' +import Views from '../Views' import { JUNTO_UPDATED, @@ -63,8 +58,8 @@ import { dragTopic } from './sendable' -let Realtime = { - juntoState: { connectedPeople: {}, liveMaps: {} }, +const Realtime = ({Active, Cable, DataModel, JIT, Visualize}) => { +const toExport = { videoId: 'video-wrapper', socket: null, webrtc: null, @@ -78,7 +73,7 @@ let Realtime = { localVideo: null, 'junto_spinner_darkgrey.gif': '', init: function(serverData) { - var self = Realtime + var self = toExport self.addJuntoListeners() @@ -154,7 +149,7 @@ let Realtime = { } // if Active.Mapper }, addJuntoListeners: function() { - var self = Realtime + var self = toExport $(document).on(ChatView.events.openTray, function() { $('.main').addClass('compressed') @@ -180,7 +175,7 @@ let Realtime = { }) }, startActiveMap: function() { - var self = Realtime + var self = toExport if (Active.Map && Active.Mapper) { if (Active.Map.authorizeToEdit(Active.Mapper)) { self.turnOn() @@ -192,7 +187,7 @@ let Realtime = { } }, endActiveMap: function() { - var self = Realtime + var self = toExport $(document).off('.map') // leave the appropriate rooms to leave if (self.inConversation) self.leaveCall() @@ -202,7 +197,7 @@ let Realtime = { Cable.unsubscribeFromMap() }, turnOn: function(notify) { - var self = Realtime + var self = toExport $('.collabCompass').show() self.room.room = 'map-' + Active.Map.id self.activeMapper = { @@ -219,13 +214,13 @@ let Realtime = { self.setupLocalEvents() }, setupChat: function() { - const self = Realtime + const self = toExport ChatView.setNewMap() ChatView.addParticipant(self.activeMapper) ChatView.addMessages(new DataModel.MessageCollection(DataModel.Messages), true) }, setupLocalEvents: function() { - var self = Realtime + var self = toExport // local event listeners that trigger events $(document).on(JIT.events.zoom + '.map', self.positionPeerIcons) $(document).on(JIT.events.pan + '.map', self.positionPeerIcons) @@ -242,7 +237,7 @@ let Realtime = { }) }, countOthersInConversation: function() { - var self = Realtime + var self = toExport var count = 0 for (var key in self.mappersOnMap) { if (self.mappersOnMap[key].inConversation) count++ @@ -250,7 +245,7 @@ let Realtime = { return count }, handleVideoAdded: function(v, id) { - var self = Realtime + var self = toExport self.positionVideos() v.setParent($('#wrapper')) v.$container.find('.video-cutoff').css({ @@ -259,7 +254,7 @@ let Realtime = { $('#wrapper').append(v.$container) }, positionVideos: function() { - var self = Realtime + var self = toExport var videoIds = Object.keys(self.room.videos) // var numOfVideos = videoIds.length // var numOfVideosToPosition = _.filter(videoIds, function(id) { @@ -290,7 +285,7 @@ let Realtime = { } // do self first - var myVideo = Realtime.localVideo.view + var myVideo = toExport.localVideo.view if (!myVideo.manuallyPositioned) { myVideo.$container.css({ top: yFormula() + 'px', @@ -308,7 +303,7 @@ let Realtime = { }) }, callEnded: function() { - var self = Realtime + var self = toExport ChatView.conversationEnded() self.room.leaveVideoOnly() @@ -336,13 +331,13 @@ let Realtime = { }) }, positionPeerIcons: function() { - var self = Realtime + var self = toExport for (var key in self.mappersOnMap) { self.positionPeerIcon(key) } }, positionPeerIcon: function(id) { - var self = Realtime + var self = toExport var mapper = self.mappersOnMap[id] var origPixels = Util.coordsToPixels(Visualize.mGraph, mapper.coords) @@ -371,7 +366,7 @@ let Realtime = { } }, limitPixelsToScreen: function(pixels) { - var self = Realtime + var self = toExport var boundary = self.chatOpen ? '#wrapper' : document var xLimit, yLimit @@ -405,25 +400,28 @@ const sendables = [ ['dragTopic', dragTopic] ] sendables.forEach(sendable => { - Realtime[sendable[0]] = sendable[1](Realtime) + toExport[sendable[0]] = sendable[1](toExport) }) -const subscribeToEvents = (Realtime, socket) => { - socket.on(JUNTO_UPDATED, juntoUpdated(Realtime)) - socket.on(INVITED_TO_CALL, invitedToCall(Realtime)) - socket.on(INVITED_TO_JOIN, invitedToJoin(Realtime)) - socket.on(CALL_ACCEPTED, callAccepted(Realtime)) - socket.on(CALL_DENIED, callDenied(Realtime)) - socket.on(INVITE_DENIED, inviteDenied(Realtime)) - socket.on(CALL_IN_PROGRESS, callInProgress(Realtime)) - socket.on(CALL_STARTED, callStarted(Realtime)) - socket.on(MAPPER_LIST_UPDATED, mapperListUpdated(Realtime)) - socket.on(MAPPER_JOINED_CALL, mapperJoinedCall(Realtime)) - socket.on(MAPPER_LEFT_CALL, mapperLeftCall(Realtime)) - socket.on(PEER_COORDS_UPDATED, peerCoordsUpdated(Realtime)) - socket.on(NEW_MAPPER, newMapper(Realtime)) - socket.on(LOST_MAPPER, lostMapper(Realtime)) - socket.on(TOPIC_DRAGGED, topicDragged(Realtime)) +const subscribeToEvents = (toExport, socket) => { + socket.on(JUNTO_UPDATED, juntoUpdated(toExport)) + socket.on(INVITED_TO_CALL, invitedToCall(toExport)) + socket.on(INVITED_TO_JOIN, invitedToJoin(toExport)) + socket.on(CALL_ACCEPTED, callAccepted(toExport)) + socket.on(CALL_DENIED, callDenied(toExport)) + socket.on(INVITE_DENIED, inviteDenied(toExport)) + socket.on(CALL_IN_PROGRESS, callInProgress(toExport)) + socket.on(CALL_STARTED, callStarted(toExport)) + socket.on(MAPPER_LIST_UPDATED, mapperListUpdated(toExport)) + socket.on(MAPPER_JOINED_CALL, mapperJoinedCall(toExport)) + socket.on(MAPPER_LEFT_CALL, mapperLeftCall(toExport)) + socket.on(PEER_COORDS_UPDATED, peerCoordsUpdated(toExport)) + socket.on(NEW_MAPPER, newMapper(toExport)) + socket.on(LOST_MAPPER, lostMapper(toExport)) + socket.on(TOPIC_DRAGGED, topicDragged(toExport)) +} +return toExport } + export default Realtime diff --git a/frontend/src/Metamaps/Realtime/receivable.js b/frontend/src/Metamaps/Realtime/receivable.js index f673d2e5..277f19b8 100644 --- a/frontend/src/Metamaps/Realtime/receivable.js +++ b/frontend/src/Metamaps/Realtime/receivable.js @@ -9,12 +9,12 @@ import { JUNTO_UPDATED } from './events' import Active from '../Active' import { ChatView } from '../Views' import DataModel from '../DataModel' -import GlobalUI from '../GlobalUI' +import GlobalUI, { ReactApp } from '../GlobalUI' import Util from '../Util' import Visualize from '../Visualize' export const juntoUpdated = self => state => { - self.juntoState = state + ReactApp.juntoState = state $(document).trigger(JUNTO_UPDATED) } diff --git a/frontend/src/Metamaps/Selected.js b/frontend/src/Metamaps/Selected.js index 86654b8f..e389f661 100644 --- a/frontend/src/Metamaps/Selected.js +++ b/frontend/src/Metamaps/Selected.js @@ -1,11 +1,14 @@ -const Selected = { - reset: function() { - var self = Selected - self.Nodes = [] - self.Edges = [] - }, - Nodes: [], - Edges: [] +const Selected = () => { + const toExport = { + reset: function() { + var self = toExport + self.Nodes = [] + self.Edges = [] + }, + Nodes: [], + Edges: [] + } + return toExport } export default Selected diff --git a/frontend/src/Metamaps/Synapse.js b/frontend/src/Metamaps/Synapse.js index 3d6c5f9a..882e1672 100644 --- a/frontend/src/Metamaps/Synapse.js +++ b/frontend/src/Metamaps/Synapse.js @@ -1,17 +1,11 @@ /* global $ */ -import Active from './Active' -import Control from './Control' -import Create from './Create' -import DataModel from './DataModel' -import Map from './Map' -import Selected from './Selected' import Settings from './Settings' -import Visualize from './Visualize' const noOp = () => {} -const Synapse = { +const Synapse = ({Active, Control, Create, DataModel, Map, Selected, Visualize}) => { +const toExport = { // this function is to retrieve a synapse JSON object from the database // @param id = the id of the synapse to retrieve get: function(id, callback = noOp) { @@ -77,7 +71,7 @@ const Synapse = { } }, createSynapseLocally: function() { - var self = Synapse + var self = toExport let topic1 let topic2 let node1 @@ -124,7 +118,7 @@ const Synapse = { Create.newSynapse.hide() }, getSynapseFromAutocomplete: function(id) { - var self = Synapse + var self = toExport self.get(id, synapse => { const mapping = new DataModel.Mapping({ @@ -141,5 +135,7 @@ const Synapse = { }) } } +return toExport +} export default Synapse diff --git a/frontend/src/Metamaps/SynapseCard.js b/frontend/src/Metamaps/SynapseCard.js index 5855441c..ad28e101 100644 --- a/frontend/src/Metamaps/SynapseCard.js +++ b/frontend/src/Metamaps/SynapseCard.js @@ -1,13 +1,11 @@ /* global $ */ -import Active from './Active' -import Control from './Control' import Mapper from './Mapper' -import Visualize from './Visualize' -const SynapseCard = { +const SynapseCard = ({Active, Control, Visualize}) => { +const toExport = { openSynapseCard: null, showCard: function(edge, e) { - var self = SynapseCard + var self = toExport // reset so we don't interfere with other edges, but first, save its x and y var myX = $('#edit_synapse').css('left') @@ -53,11 +51,11 @@ const SynapseCard = { hideCard: function() { $('#edit_synapse').remove() - SynapseCard.openSynapseCard = null + toExport.openSynapseCard = null }, populateShowCard: function(edge, synapse) { - var self = SynapseCard + var self = toExport self.add_synapse_count(edge) self.add_desc_form(synapse) @@ -156,7 +154,7 @@ const SynapseCard = { var index = parseInt($(this).attr('data-synapse-index')) edge.setData('displayIndex', index) Visualize.mGraph.plot() - SynapseCard.showCard(edge, false) + toExport.showCard(edge, false) }) } }, @@ -291,5 +289,7 @@ const SynapseCard = { } // if } // add_direction_form } +return toExport +} export default SynapseCard diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 0054ad55..533e70ef 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -2,27 +2,15 @@ import $jit from '../patched/JIT' -import Active from './Active' -import AutoLayout from './AutoLayout' -import Create from './Create' -import DataModel from './DataModel' -import Filter from './Filter' import GlobalUI, { ReactApp } from './GlobalUI' -import JIT from './JIT' import Loading from './Loading' -import Map from './Map' -import Selected from './Selected' import Settings from './Settings' -import SynapseCard from './SynapseCard' -import TopicCard from './Views/TopicCard' import Util from './Util' -import Visualize from './Visualize' const noOp = () => {} -const Topic = { - // this function is to retrieve a topic JSON object from the database - // @param id = the id of the topic to retrieve +const Topic = ({Active, AutoLayout, Create, DataModel, Filter, JIT, MAP, Selected, SynapseCard, TopicCard, Visualize}) => { +const toExport = { get: function(id, callback = noOp) { // if the desired topic is not yet in the local topic repository, fetch it if (DataModel.Topics.get(id) === undefined) { @@ -101,7 +89,7 @@ const Topic = { ReactApp.render() }, fetchRelatives: function(nodes, metacodeId) { - var self = this + var self = toExport var node = $.isArray(nodes) ? nodes[0] : nodes @@ -292,7 +280,7 @@ const Topic = { } }, createTopicLocally: function() { - var self = Topic + var self = toExport if (Create.newTopic.name === '') { GlobalUI.notifyUser('Please enter a topic title...') @@ -330,7 +318,7 @@ const Topic = { self.renderTopic(mapping, topic, true, true) // this function also includes the creation of the topic in the database }, getTopicFromAutocomplete: function(id) { - var self = Topic + var self = toExport Map.setHasLearnedTopicCreation(true) @@ -357,7 +345,7 @@ const Topic = { }) }, getMapFromAutocomplete: function(data) { - var self = Topic + var self = toExport $(document).trigger(Map.events.editedByActiveMapper) @@ -387,7 +375,7 @@ const Topic = { if (Create.newTopic.pinned) Create.newTopic.beingCreated = true }, getTopicFromSearch: function(event, id) { - var self = Topic + var self = toExport $(document).trigger(Map.events.editedByActiveMapper) @@ -409,5 +397,7 @@ const Topic = { return false } } +return toExport +} export default Topic diff --git a/frontend/src/Metamaps/Views/ChatView.js b/frontend/src/Metamaps/Views/ChatView.js index f058d35a..3405bb67 100644 --- a/frontend/src/Metamaps/Views/ChatView.js +++ b/frontend/src/Metamaps/Views/ChatView.js @@ -3,18 +3,17 @@ import Backbone from 'backbone' import { Howl } from 'howler' -import Active from '../Active' -import DataModel from '../DataModel' import ReactApp from '../GlobalUI/ReactApp' -const ChatView = { +const ChatView = ({Active, DataModel}) => { +const toExport = { isOpen: false, unreadMessages: 0, messages: new Backbone.Collection(), conversationLive: false, isParticipating: false, init: function(urls) { - const self = ChatView + const self = toExport self.sound = new Howl({ src: urls, sprite: { @@ -27,7 +26,7 @@ const ChatView = { }) }, setNewMap: function() { - const self = ChatView + const self = toExport self.unreadMessages = 0 self.isOpen = false self.conversationLive = false @@ -41,74 +40,74 @@ const ChatView = { }, render: () => { if (!Active.Map) return - const self = ChatView + const self = toExport ReactApp.render() }, onOpen: () => { - const self = ChatView + const self = toExport self.isOpen = true self.unreadMessages = 0 self.render() $(document).trigger(ChatView.events.openTray) }, onClose: () => { - const self = ChatView + const self = toExport self.isOpen = false $(document).trigger(ChatView.events.closeTray) }, addParticipant: participant => { - ChatView.participants.add(participant) - ChatView.render() + toExport.participants.add(participant) + toExport.render() }, removeParticipant: participant => { - ChatView.participants.remove(participant) - ChatView.render() + toExport.participants.remove(participant) + toExport.render() }, leaveConversation: () => { - ChatView.isParticipating = false - ChatView.render() + toExport.isParticipating = false + toExport.render() }, mapperJoinedCall: id => { - const mapper = ChatView.participants.findWhere({id}) + const mapper = toExport.participants.findWhere({id}) mapper && mapper.set('isParticipating', true) - ChatView.render() + toExport.render() }, mapperLeftCall: id => { - const mapper = ChatView.participants.findWhere({id}) + const mapper = toExport.participants.findWhere({id}) mapper && mapper.set('isParticipating', false) - ChatView.render() + toExport.render() }, invitationPending: id => { - const mapper = ChatView.participants.findWhere({id}) + const mapper = toExport.participants.findWhere({id}) mapper && mapper.set('isPending', true) - ChatView.render() + toExport.render() }, invitationAnswered: id => { - const mapper = ChatView.participants.findWhere({id}) + const mapper = toExport.participants.findWhere({id}) mapper && mapper.set('isPending', false) - ChatView.render() + toExport.render() }, conversationInProgress: participating => { - ChatView.conversationLive = true - ChatView.isParticipating = participating - ChatView.render() + toExport.conversationLive = true + toExport.isParticipating = participating + toExport.render() }, conversationEnded: () => { - ChatView.conversationLive = false - ChatView.isParticipating = false - ChatView.participants.forEach(p => p.set({isParticipating: false, isPending: false})) - ChatView.render() + toExport.conversationLive = false + toExport.isParticipating = false + toExport.participants.forEach(p => p.set({isParticipating: false, isPending: false})) + toExport.render() }, videoToggleClick: function() { - ChatView.videosShowing = !ChatView.videosShowing - $(document).trigger(ChatView.videosShowing ? ChatView.events.videosOn : ChatView.events.videosOff) + toExport.videosShowing = !toExport.videosShowing + $(document).trigger(toExport.videosShowing ? ChatView.events.videosOn : ChatView.events.videosOff) }, cursorToggleClick: function() { - ChatView.cursorsShowing = !ChatView.cursorsShowing - $(document).trigger(ChatView.cursorsShowing ? ChatView.events.cursorsOn : ChatView.events.cursorsOff) + toExport.cursorsShowing = !toExport.cursorsShowing + $(document).trigger(toExport.cursorsShowing ? ChatView.events.cursorsOn : ChatView.events.cursorsOff) }, soundToggleClick: function() { - ChatView.alertSound = !ChatView.alertSound + toExport.alertSound = !toExport.alertSound }, inputFocus: () => { $(document).trigger(ChatView.events.inputFocus) @@ -117,15 +116,15 @@ const ChatView = { $(document).trigger(ChatView.events.inputBlur) }, addMessage: (message, isInitial, wasMe) => { - const self = ChatView + const self = toExport if (!isInitial && !self.isOpen) self.unreadMessages += 1 if (!wasMe && !isInitial && self.alertSound) self.sound.play('receivechat') self.messages.add(message) if (!isInitial && self.isOpen) self.render() }, sendChatMessage: message => { - var self = ChatView - if (ChatView.alertSound) ChatView.sound.play('sendchat') + var self = toExport + if (toExport.alertSound) toExport.sound.play('sendchat') var m = new DataModel.Message({ message: message.message, resource_id: Active.Map.id, @@ -141,14 +140,16 @@ const ChatView = { }) }, handleInputMessage: text => { - ChatView.sendChatMessage({message: text}) + toExport.sendChatMessage({message: text}) }, // they should be instantiated as backbone models before they get // passed to this function addMessages: (messages, isInitial, wasMe) => { - messages.models.forEach(m => ChatView.addMessage(m, isInitial, wasMe)) + messages.models.forEach(m => toExport.addMessage(m, isInitial, wasMe)) } } +return toExport +} /** * @class diff --git a/frontend/src/Metamaps/Views/TopicCard.js b/frontend/src/Metamaps/Views/TopicCard.js index 943869e9..d38a2daa 100644 --- a/frontend/src/Metamaps/Views/TopicCard.js +++ b/frontend/src/Metamaps/Views/TopicCard.js @@ -1,15 +1,18 @@ import { ReactApp } from '../GlobalUI' -const TopicCard = { +const TopicCard = () => { +const toExport = { openTopic: null, showCard: function(node) { - TopicCard.openTopic = node.getData('topic') + toExport.openTopic = node.getData('topic') ReactApp.render() }, hideCard: function() { - TopicCard.openTopic = null + toExport.openTopic = null ReactApp.render() } } +return toExport +} export default TopicCard diff --git a/frontend/src/Metamaps/Views/index.js b/frontend/src/Metamaps/Views/index.js index 0f7cf566..b613624c 100644 --- a/frontend/src/Metamaps/Views/index.js +++ b/frontend/src/Metamaps/Views/index.js @@ -10,7 +10,7 @@ import { JUNTO_UPDATED } from '../Realtime/events' const Views = { init: (serverData) => { $(document).on(JUNTO_UPDATED, () => ExploreMaps.render()) - ChatView.init([serverData['sounds/MM_sounds.mp3'], serverData['sounds/MM_sounds.ogg']]) + //ChatView.init([serverData['sounds/MM_sounds.mp3'], serverData['sounds/MM_sounds.ogg']]) }, ExploreMaps, ChatView, diff --git a/frontend/src/Metamaps/Visualize.js b/frontend/src/Metamaps/Visualize.js index aa0745da..cc7248fe 100644 --- a/frontend/src/Metamaps/Visualize.js +++ b/frontend/src/Metamaps/Visualize.js @@ -10,14 +10,15 @@ import JIT from './JIT' import Loading from './Loading' import TopicCard from './Views/TopicCard' -const Visualize = { +const Visualize = ({Active, DataModel, JIT, TopicCard}) => { +const toExport = { mGraph: null, // a reference to the graph object. cameraPosition: null, // stores the camera position when using a 3D visualization type: 'ForceDirected', // the type of graph we're building, could be "RGraph", "ForceDirected", or "ForceDirected3D" loadLater: false, // indicates whether there is JSON that should be loaded right in the offset, or whether to wait till the first topic is created touchDragNode: null, init: function(serverData) { - var self = Visualize + var self = toExport if (serverData.VisualizeType) self.type = serverData.VisualizeType @@ -45,7 +46,7 @@ const Visualize = { }) }, computePositions: function() { - const self = Visualize + const self = toExport if (self.type === 'RGraph') { let i @@ -107,7 +108,7 @@ const Visualize = { * */ render: function() { - const self = Visualize + const self = toExport if (self.type === 'RGraph') { // clear the previous canvas from #infovis @@ -199,11 +200,14 @@ const Visualize = { hold() }, clearVisualization: function() { - Visualize.mGraph.graph.empty() - Visualize.mGraph.plot() + const self = toExport + self.mGraph.graph.empty() + self.mGraph.plot() JIT.centerMap(Visualize.mGraph.canvas) $('#infovis').empty() } } +return toExport +} export default Visualize diff --git a/frontend/src/Metamaps/index.js b/frontend/src/Metamaps/index.js index 9804133e..b2366e74 100644 --- a/frontend/src/Metamaps/index.js +++ b/frontend/src/Metamaps/index.js @@ -1,32 +1,72 @@ import Account from './Account' import Active from './Active' import Admin from './Admin' +import AutoLayout from './AutoLayout' +import Cable from './Cable' +import Control from './Control' +import Create from './Create' +import DataModel from './DataModel' import Debug from './Debug' +import Filter from './Filter' import GlobalUI, { ReactApp, Search, CreateMap, ImportDialog } from './GlobalUI' import Import from './Import' +import JIT from './JIT' +import Listeners from './Listeners' import Loading from './Loading' +import Map, { CheatSheet } from './Map' import Mapper from './Mapper' +import Mouse from './Mouse' +import Organize from './Organize' +import PasteInput from './PasteInput' +import Realtime from './Realtime' +import Selected from './Selected' +import Settings from './Settings' +import Synapse from './Synapse' +import SynapseCard from './SynapseCard' import Topic from './Topic' import Util from './Util' +import Views from './Views' +import Visualize from './Visualize' const Metamaps = window.Metamaps || {} Metamaps.Account = Account Metamaps.Active = Active Metamaps.Admin = Admin +Metamaps.AutoLayout = AutoLayout +Metamaps.Cable = Cable +Metamaps.Control = Control +Metamaps.Create = Create +Metamaps.DataModel = DataModel Metamaps.Debug = Debug +Metamaps.Filter = Filter +Metamaps.Import = Import +Metamaps.JIT = JIT +Metamaps.Listeners = Listeners +Metamaps.Loading = Loading +Metamaps.Map = Map +Metamaps.Map.CheatSheet = CheatSheet +Metamaps.Maps = {} +Metamaps.Mapper = Mapper +Metamaps.Mouse = Mouse +Metamaps.Organize = Organize +Metamaps.PasteInput = PasteInput +Metamaps.Realtime = Realtime +Metamaps.Selected = Selected +Metamaps.Settings = Settings +Metamaps.Synapse = Synapse +Metamaps.SynapseCard = SynapseCard +Metamaps.Topic = Topic +Metamaps.Util = Util +Metamaps.Views = Views +Metamaps.Visualize = Visualize + Metamaps.GlobalUI = GlobalUI Metamaps.GlobalUI.ReactApp = ReactApp Metamaps.GlobalUI.Search = Search Metamaps.GlobalUI.CreateMap = CreateMap Metamaps.GlobalUI.ImportDialog = ImportDialog -Metamaps.Import = Import -Metamaps.Loading = Loading -Metamaps.Maps = {} -Metamaps.Mapper = Mapper -Metamaps.Topic = Topic -Metamaps.Util = Util document.addEventListener('DOMContentLoaded', function() { // initialize all the modules @@ -37,6 +77,7 @@ document.addEventListener('DOMContentLoaded', function() { Metamaps[prop].hasOwnProperty('init') && typeof (Metamaps[prop].init) === 'function' ) { + console.log(prop) Metamaps[prop].init(Metamaps.ServerData) } }