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: "