diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js
index 4ab4d110..43df6660 100644
--- a/frontend/src/Metamaps/GlobalUI/ReactApp.js
+++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js
@@ -108,7 +108,7 @@ const ReactApp = {
mobileTitleClick: (e) => self.openMap && self.openMap.InfoBox.toggleBox(e),
openInviteLightbox: () => self.openLightbox('invite'),
serverData: self.serverData,
- endActiveMap: mapControl.end,
+ endActiveMap: () => mapControl.end(self.openMap),
launchNewMap: mapControl.launch,
mapId: self.mapId,
topicId: self.topicId
@@ -159,7 +159,7 @@ const ReactApp = {
openTopic: TopicCard.openTopic,
metacodeSets: self.metacodeSets,
updateTopic: (topic, obj) => topic.save(obj),
- onTopicFollow: Topic.onTopicFollow // todo
+ onTopicFollow: () => {} // Topic.onTopicFollow // todo
}
},
getTopicProps: function() {
diff --git a/frontend/src/Metamaps/GlobalUI/Search.js b/frontend/src/Metamaps/GlobalUI/Search.js
index 45869f96..07c3a4a3 100644
--- a/frontend/src/Metamaps/GlobalUI/Search.js
+++ b/frontend/src/Metamaps/GlobalUI/Search.js
@@ -1,4 +1,4 @@
-ReactApp.currentUserReactApp.currentUser/* global $, Hogan, Bloodhound, CanvasLoader */
+/* global $, Hogan, Bloodhound, CanvasLoader */
import { browserHistory } from 'react-router'
diff --git a/frontend/src/Metamaps/Map/ChatView.js b/frontend/src/Metamaps/Map/ChatView.js
index 7e4043b3..9df36f07 100644
--- a/frontend/src/Metamaps/Map/ChatView.js
+++ b/frontend/src/Metamaps/Map/ChatView.js
@@ -4,6 +4,7 @@ import Backbone from 'backbone'
import { Howl } from 'howler'
import ReactApp from '../GlobalUI/ReactApp'
+import MessageCollection from '../DataModel/MessageCollection'
const ChatView = (map) => {
const toExport = {
@@ -12,19 +13,6 @@ const toExport = {
messages: new Backbone.Collection(),
conversationLive: false,
isParticipating: false,
- init: function(urls) {
- const self = toExport
- self.sound = new Howl({
- src: urls,
- sprite: {
- joinmap: [0, 561],
- leavemap: [1000, 592],
- receivechat: [2000, 318],
- sendchat: [3000, 296],
- sessioninvite: [4000, 5393, true]
- }
- })
- },
setNewMap: function() {
const self = toExport
self.unreadMessages = 0
@@ -118,13 +106,13 @@ const toExport = {
addMessage: (message, isInitial, wasMe) => {
const self = toExport
if (!isInitial && !self.isOpen) self.unreadMessages += 1
- if (!wasMe && !isInitial && self.alertSound) self.sound.play('receivechat')
+ if (!wasMe && !isInitial && self.alertSound) ChatView.sound.play('receivechat')
self.messages.add(message)
if (!isInitial && self.isOpen) self.render()
},
sendChatMessage: message => {
var self = toExport
- if (toExport.alertSound) toExport.sound.play('sendchat')
+ if (toExport.alertSound) ChatView.sound.play('sendchat')
var m = new DataModel.Message({
message: message.message,
resource_id: map.Active.Map.id,
@@ -132,7 +120,7 @@ const toExport = {
})
m.save(null, {
success: function(model, response) {
- self.addMessages(new DataModel.MessageCollection(model), false, true)
+ self.addMessages(new MessageCollection(model), false, true)
},
error: function(model, response) {
console.log('error!', response)
@@ -150,7 +138,6 @@ const toExport = {
}
return toExport
}
-
/**
* @class
* @static
@@ -165,5 +152,17 @@ ChatView.events = {
videosOff: 'ChatView:videosOff',
videosOn: 'ChatView:videosOn'
}
+ChatView.init = function(urls) {
+ ChatView.sound = new Howl({
+ src: urls,
+ sprite: {
+ joinmap: [0, 561],
+ leavemap: [1000, 592],
+ receivechat: [2000, 318],
+ sendchat: [3000, 296],
+ sessioninvite: [4000, 5393, true]
+ }
+ })
+}
export default ChatView
diff --git a/frontend/src/Metamaps/Map/Create.js b/frontend/src/Metamaps/Map/Create.js
index c0db24ee..80c44b39 100644
--- a/frontend/src/Metamaps/Map/Create.js
+++ b/frontend/src/Metamaps/Map/Create.js
@@ -266,7 +266,7 @@ const toExport = {
})
toExport.newTopic.beingCreated = true
toExport.newTopic.name = ''
- //Map.setHasLearnedTopicCreation(true)
+ map.Map.setHasLearnedTopicCreation(true)
},
hide: function(force) {
if (force || !toExport.newTopic.pinned) {
@@ -277,7 +277,7 @@ const toExport = {
toExport.newTopic.pinned = false
}
if (map.DataModel.Topics.length === 0) {
- Map.setHasLearnedTopicCreation(false)
+ map.Map.setHasLearnedTopicCreation(false)
}
toExport.newTopic.beingCreated = false
},
diff --git a/frontend/src/Metamaps/Map/Filter.js b/frontend/src/Metamaps/Map/Filter.js
index 9b19d284..319a70d3 100644
--- a/frontend/src/Metamaps/Map/Filter.js
+++ b/frontend/src/Metamaps/Map/Filter.js
@@ -6,274 +6,274 @@ import GlobalUI, { ReactApp } from '../GlobalUI'
import Settings from '../Settings'
const Filter = (map) => {
-const toExport = {
- dataForPresentation: {
- metacodes: {},
- mappers: {},
- synapses: {}
- },
- filters: {
- metacodes: [],
- mappers: [],
- synapses: []
- },
- visible: {
- metacodes: [],
- mappers: [],
- synapses: []
- },
- reset: function() {
- var self = toExport
- self.filters.metacodes = []
- self.filters.mappers = []
- self.filters.synapses = []
- self.visible.metacodes = []
- self.visible.mappers = []
- self.visible.synapses = []
- self.dataForPresentation.metacodes = {}
- self.dataForPresentation.mappers = {}
- self.dataForPresentation.synapses = {}
- ReactApp.render()
- },
- // an abstraction function for checkMetacodes, checkMappers, checkSynapses to reduce
- // code redundancy
- updateFilters: function(collection, propertyToCheck, correlatedModel, filtersToUse, listToModify) {
- var self = toExport
- var newList = []
- var removed = []
- var added = []
- // the first option enables us to accept
- // ['Topics', 'Synapses'] as 'collection'
- if (typeof collection === 'object') {
- DataModel[collection[0]].each(function(model) {
- var prop = model.get(propertyToCheck)
- if (prop !== null) {
- prop = prop.toString()
- if (newList.indexOf(prop) === -1) {
- newList.push(prop)
- }
- }
- })
- DataModel[collection[1]].each(function(model) {
- var prop = model.get(propertyToCheck)
- if (prop !== null) {
- prop = prop.toString()
- if (newList.indexOf(prop) === -1) {
- newList.push(prop)
- }
- }
- })
- } else if (typeof collection === 'string') {
- DataModel[collection].each(function(model) {
- var prop = model.get(propertyToCheck)
- if (prop !== null) {
- prop = prop.toString()
- if (newList.indexOf(prop) === -1) {
- newList.push(prop)
- }
- }
- })
- }
- removed = _.difference(self.filters[filtersToUse], newList)
- added = _.difference(newList, self.filters[filtersToUse])
- _.each(removed, function(identifier) {
- const index = self.visible[filtersToUse].indexOf(identifier)
- self.visible[filtersToUse].splice(index, 1)
- delete self.dataForPresentation[filtersToUse][identifier]
- })
- _.each(added, function(identifier) {
- const model = DataModel[correlatedModel].get(identifier) ||
- DataModel[correlatedModel].find(function(m) {
- return m.get(propertyToCheck) === identifier
- })
- self.dataForPresentation[filtersToUse][identifier] = model.prepareDataForFilter()
- self.visible[filtersToUse].push(identifier)
- })
- // update the list of filters with the new list we just generated
- self.filters[filtersToUse] = newList
- ReactApp.render()
- },
- checkMetacodes: function() {
- var self = toExport
- self.updateFilters('Topics', 'metacode_id', 'Metacodes', 'metacodes', 'metacode')
- },
- checkMappers: function() {
- var self = toExport
- if (map.Active.Map) {
- self.updateFilters('Mappings', 'user_id', 'Mappers', 'mappers', 'mapper')
- } else {
- // on topic view
- self.updateFilters(['Topics', 'Synapses'], 'user_id', 'Creators', 'mappers', 'mapper')
- }
- },
- checkSynapses: function() {
- var self = toExport
- self.updateFilters('Synapses', 'desc', 'Synapses', 'synapses', 'synapse')
- },
- filterAllMetacodes: function(toVisible) {
- var self = toExport
- self.visible.metacodes = toVisible ? self.filters.metacodes.slice() : []
- ReactApp.render()
- self.passFilters()
- },
- filterAllMappers: function(toVisible) {
- var self = toExport
- self.visible.mappers = toVisible ? self.filters.mappers.slice() : []
- ReactApp.render()
- self.passFilters()
- },
- filterAllSynapses: function(toVisible) {
- var self = toExport
- self.visible.synapses = toVisible ? self.filters.synapses.slice() : []
- ReactApp.render()
- self.passFilters()
- },
- // an abstraction function for toggleMetacode, toggleMapper, toggleSynapse
- // to reduce code redundancy
- // gets called in the context of a list item in a filter box
- toggleLi: function(whichToFilter, id) {
- var self = toExport
- if (self.visible[whichToFilter].indexOf(id) === -1) {
- self.visible[whichToFilter].push(id)
- } else {
- const index = self.visible[whichToFilter].indexOf(id)
- self.visible[whichToFilter].splice(index, 1)
- }
- ReactApp.render()
- self.passFilters()
- },
- toggleMetacode: function(id) {
- var self = toExport
- self.toggleLi('metacodes', id)
- },
- toggleMapper: function(id) {
- var self = toExport
- self.toggleLi('mappers', id)
- },
- toggleSynapse: function(id) {
- var self = toExport
- self.toggleLi('synapses', id)
- },
- passFilters: function() {
- var self = toExport
- var visible = self.visible
-
- var passesMetacode, passesMapper, passesSynapse
-
- var opacityForFilter = map.Active.Map ? 0 : 0.4
-
- map.DataModel.Topics.each(function(topic) {
- var n = topic.get('node')
- var metacodeId = topic.get('metacode_id').toString()
-
- if (visible.metacodes.indexOf(metacodeId) === -1) passesMetacode = false
- else passesMetacode = true
-
- if (map.Active.Map) {
- // when on a map,
- // we filter by mapper according to the person who added the
- // topic or synapse to the map
- let userId = topic.getMapping().get('user_id').toString()
- if (visible.mappers.indexOf(userId) === -1) passesMapper = false
- else passesMapper = true
- } else {
- // when on a topic view,
- // we filter by mapper according to the person who created the
- // topic or synapse
- let userId = topic.get('user_id').toString()
- if (visible.mappers.indexOf(userId) === -1) passesMapper = false
- else passesMapper = true
- }
-
- if (passesMetacode && passesMapper) {
- if (n) {
- n.setData('alpha', 1, 'end')
- } else {
- console.log(topic)
- }
- } else {
- if (n) {
- map.Control.deselectNode(n, true)
- n.setData('alpha', opacityForFilter, 'end')
- n.eachAdjacency(function(e) {
- map.Control.deselectEdge(e, true)
- })
- } else {
- console.log(topic)
- }
- }
- })
-
- // flag all the edges back to 'untouched'
- map.DataModel.Synapses.each(function(synapse) {
- var e = synapse.get('edge')
- e.setData('touched', false)
- })
- map.DataModel.Synapses.each(function(synapse) {
- var e = synapse.get('edge')
- var desc
- var userId = synapse.get('user_id').toString()
-
- if (e && !e.getData('touched')) {
- var synapses = e.getData('synapses')
-
- // if any of the synapses represent by the edge are still unfiltered
- // leave the edge visible
- passesSynapse = false
- for (let i = 0; i < synapses.length; i++) {
- desc = synapses[i].get('desc')
- if (visible.synapses.indexOf(desc) > -1) passesSynapse = true
- }
-
- // if the synapse description being displayed is now being
- // filtered, set the displayIndex to the first unfiltered synapse if there is one
- var displayIndex = e.getData('displayIndex') ? e.getData('displayIndex') : 0
- var displayedSynapse = synapses[displayIndex]
- desc = displayedSynapse.get('desc')
- if (passesSynapse && visible.synapses.indexOf(desc) === -1) {
- // iterate and find an unfiltered one
- for (let i = 0; i < synapses.length; i++) {
- desc = synapses[i].get('desc')
- if (visible.synapses.indexOf(desc) > -1) {
- e.setData('displayIndex', i)
- break
+ const toExport = {
+ dataForPresentation: {
+ metacodes: {},
+ mappers: {},
+ synapses: {}
+ },
+ filters: {
+ metacodes: [],
+ mappers: [],
+ synapses: []
+ },
+ visible: {
+ metacodes: [],
+ mappers: [],
+ synapses: []
+ },
+ reset: function() {
+ var self = toExport
+ self.filters.metacodes = []
+ self.filters.mappers = []
+ self.filters.synapses = []
+ self.visible.metacodes = []
+ self.visible.mappers = []
+ self.visible.synapses = []
+ self.dataForPresentation.metacodes = {}
+ self.dataForPresentation.mappers = {}
+ self.dataForPresentation.synapses = {}
+ ReactApp.render()
+ },
+ // an abstraction function for checkMetacodes, checkMappers, checkSynapses to reduce
+ // code redundancy
+ updateFilters: function(collection, propertyToCheck, correlatedModel, filtersToUse, listToModify) {
+ var self = toExport
+ var newList = []
+ var removed = []
+ var added = []
+ // the first option enables us to accept
+ // ['Topics', 'Synapses'] as 'collection'
+ if (typeof collection === 'object') {
+ map.DataModel[collection[0]].each(function(model) {
+ var prop = model.get(propertyToCheck)
+ if (prop !== null) {
+ prop = prop.toString()
+ if (newList.indexOf(prop) === -1) {
+ newList.push(prop)
}
}
- }
+ })
+ map.DataModel[collection[1]].each(function(model) {
+ var prop = model.get(propertyToCheck)
+ if (prop !== null) {
+ prop = prop.toString()
+ if (newList.indexOf(prop) === -1) {
+ newList.push(prop)
+ }
+ }
+ })
+ } else if (typeof collection === 'string') {
+ map.DataModel[collection].each(function(model) {
+ var prop = model.get(propertyToCheck)
+ if (prop !== null) {
+ prop = prop.toString()
+ if (newList.indexOf(prop) === -1) {
+ newList.push(prop)
+ }
+ }
+ })
+ }
+ removed = _.difference(self.filters[filtersToUse], newList)
+ added = _.difference(newList, self.filters[filtersToUse])
+ _.each(removed, function(identifier) {
+ const index = self.visible[filtersToUse].indexOf(identifier)
+ self.visible[filtersToUse].splice(index, 1)
+ delete self.dataForPresentation[filtersToUse][identifier]
+ })
+ _.each(added, function(identifier) {
+ const model = map.DataModel[correlatedModel].get(identifier) ||
+ map.DataModel[correlatedModel].find(function(m) {
+ return m.get(propertyToCheck) === identifier
+ })
+ self.dataForPresentation[filtersToUse][identifier] = model.prepareDataForFilter()
+ self.visible[filtersToUse].push(identifier)
+ })
+ // update the list of filters with the new list we just generated
+ self.filters[filtersToUse] = newList
+ ReactApp.render()
+ },
+ checkMetacodes: function() {
+ var self = toExport
+ self.updateFilters('Topics', 'metacode_id', 'Metacodes', 'metacodes', 'metacode')
+ },
+ checkMappers: function() {
+ var self = toExport
+ if (map.Active.Map) {
+ self.updateFilters('Mappings', 'user_id', 'Mappers', 'mappers', 'mapper')
+ } else {
+ // on topic view
+ self.updateFilters(['Topics', 'Synapses'], 'user_id', 'Creators', 'mappers', 'mapper')
+ }
+ },
+ checkSynapses: function() {
+ var self = toExport
+ self.updateFilters('Synapses', 'desc', 'Synapses', 'synapses', 'synapse')
+ },
+ filterAllMetacodes: function(toVisible) {
+ var self = toExport
+ self.visible.metacodes = toVisible ? self.filters.metacodes.slice() : []
+ ReactApp.render()
+ self.passFilters()
+ },
+ filterAllMappers: function(toVisible) {
+ var self = toExport
+ self.visible.mappers = toVisible ? self.filters.mappers.slice() : []
+ ReactApp.render()
+ self.passFilters()
+ },
+ filterAllSynapses: function(toVisible) {
+ var self = toExport
+ self.visible.synapses = toVisible ? self.filters.synapses.slice() : []
+ ReactApp.render()
+ self.passFilters()
+ },
+ // an abstraction function for toggleMetacode, toggleMapper, toggleSynapse
+ // to reduce code redundancy
+ // gets called in the context of a list item in a filter box
+ toggleLi: function(whichToFilter, id) {
+ var self = toExport
+ if (self.visible[whichToFilter].indexOf(id) === -1) {
+ self.visible[whichToFilter].push(id)
+ } else {
+ const index = self.visible[whichToFilter].indexOf(id)
+ self.visible[whichToFilter].splice(index, 1)
+ }
+ ReactApp.render()
+ self.passFilters()
+ },
+ toggleMetacode: function(id) {
+ var self = toExport
+ self.toggleLi('metacodes', id)
+ },
+ toggleMapper: function(id) {
+ var self = toExport
+ self.toggleLi('mappers', id)
+ },
+ toggleSynapse: function(id) {
+ var self = toExport
+ self.toggleLi('synapses', id)
+ },
+ passFilters: function() {
+ var self = toExport
+ var visible = self.visible
+
+ var passesMetacode, passesMapper, passesSynapse
+
+ var opacityForFilter = map.Active.Map ? 0 : 0.4
+
+ map.DataModel.Topics.each(function(topic) {
+ var n = topic.get('node')
+ var metacodeId = topic.get('metacode_id').toString()
+
+ if (visible.metacodes.indexOf(metacodeId) === -1) passesMetacode = false
+ else passesMetacode = true
if (map.Active.Map) {
// when on a map,
// we filter by mapper according to the person who added the
// topic or synapse to the map
- userId = synapse.getMapping().get('user_id').toString()
- }
- if (visible.mappers.indexOf(userId) === -1) passesMapper = false
- else passesMapper = true
-
- var color = Settings.colors.synapses.normal
- if (passesSynapse && passesMapper) {
- e.setData('alpha', 1, 'end')
- e.setData('color', color, 'end')
+ let userId = topic.getMapping().get('user_id').toString()
+ if (visible.mappers.indexOf(userId) === -1) passesMapper = false
+ else passesMapper = true
} else {
- map.Control.deselectEdge(e, true)
- e.setData('alpha', opacityForFilter, 'end')
+ // when on a topic view,
+ // we filter by mapper according to the person who created the
+ // topic or synapse
+ let userId = topic.get('user_id').toString()
+ if (visible.mappers.indexOf(userId) === -1) passesMapper = false
+ else passesMapper = true
}
- e.setData('touched', true)
- } else if (!e) {
- console.log(synapse)
- }
- })
+ if (passesMetacode && passesMapper) {
+ if (n) {
+ n.setData('alpha', 1, 'end')
+ } else {
+ console.log(topic)
+ }
+ } else {
+ if (n) {
+ map.Control.deselectNode(n, true)
+ n.setData('alpha', opacityForFilter, 'end')
+ n.eachAdjacency(function(e) {
+ map.Control.deselectEdge(e, true)
+ })
+ } else {
+ console.log(topic)
+ }
+ }
+ })
- // run the animation
- map.Visualize.mGraph.fx.animate({
- modes: ['node-property:alpha',
- 'edge-property:alpha'],
- duration: 200
- })
+ // flag all the edges back to 'untouched'
+ map.DataModel.Synapses.each(function(synapse) {
+ var e = synapse.get('edge')
+ e.setData('touched', false)
+ })
+ map.DataModel.Synapses.each(function(synapse) {
+ var e = synapse.get('edge')
+ var desc
+ var userId = synapse.get('user_id').toString()
+
+ if (e && !e.getData('touched')) {
+ var synapses = e.getData('synapses')
+
+ // if any of the synapses represent by the edge are still unfiltered
+ // leave the edge visible
+ passesSynapse = false
+ for (let i = 0; i < synapses.length; i++) {
+ desc = synapses[i].get('desc')
+ if (visible.synapses.indexOf(desc) > -1) passesSynapse = true
+ }
+
+ // if the synapse description being displayed is now being
+ // filtered, set the displayIndex to the first unfiltered synapse if there is one
+ var displayIndex = e.getData('displayIndex') ? e.getData('displayIndex') : 0
+ var displayedSynapse = synapses[displayIndex]
+ desc = displayedSynapse.get('desc')
+ if (passesSynapse && visible.synapses.indexOf(desc) === -1) {
+ // iterate and find an unfiltered one
+ for (let i = 0; i < synapses.length; i++) {
+ desc = synapses[i].get('desc')
+ if (visible.synapses.indexOf(desc) > -1) {
+ e.setData('displayIndex', i)
+ break
+ }
+ }
+ }
+
+ if (map.Active.Map) {
+ // when on a map,
+ // we filter by mapper according to the person who added the
+ // topic or synapse to the map
+ userId = synapse.getMapping().get('user_id').toString()
+ }
+ if (visible.mappers.indexOf(userId) === -1) passesMapper = false
+ else passesMapper = true
+
+ var color = Settings.colors.synapses.normal
+ if (passesSynapse && passesMapper) {
+ e.setData('alpha', 1, 'end')
+ e.setData('color', color, 'end')
+ } else {
+ map.Control.deselectEdge(e, true)
+ e.setData('alpha', opacityForFilter, 'end')
+ }
+
+ e.setData('touched', true)
+ } else if (!e) {
+ console.log(synapse)
+ }
+ })
+
+ // run the animation
+ map.Visualize.mGraph.fx.animate({
+ modes: ['node-property:alpha',
+ 'edge-property:alpha'],
+ duration: 200
+ })
+ }
}
-}
-return toExport
+ return toExport
}
export default Filter
diff --git a/frontend/src/Metamaps/Map/InfoBox.js b/frontend/src/Metamaps/Map/InfoBox.js
index 344f8edb..9e4c1e5a 100644
--- a/frontend/src/Metamaps/Map/InfoBox.js
+++ b/frontend/src/Metamaps/Map/InfoBox.js
@@ -3,387 +3,389 @@
import outdent from 'outdent'
import { browserHistory } from 'react-router'
+import DataModel from '../DataModel'
import GlobalUI, { ReactApp } from '../GlobalUI'
import Util from '../Util'
const InfoBox = (map) => {
-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.
",
- nameHTML: outdent`
- {{name}} `,
- descHTML: outdent`
- {{desc}} `,
- userImageUrl: '',
- html: '',
- init: function(serverData, updateThumbnail) {
- var self = toExport
+ 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.
",
+ nameHTML: outdent`
+ {{name}} `,
+ descHTML: outdent`
+ {{desc}} `,
+ userImageUrl: '',
+ html: '',
+ init: function(serverData, updateThumbnail) {
+ var self = toExport
- self.updateThumbnail = updateThumbnail
+ self.updateThumbnail = updateThumbnail
- $('.maptoExport').click(function(event) {
- event.stopPropagation()
- })
- $('body').click(self.close)
+ $('.mapInfoBox').click(function(event) {
+ event.stopPropagation()
+ })
+ $('body').click(self.close)
- self.attachEventListeners()
+ self.attachEventListeners()
- self.generateBoxHTML = Hogan.compile($('#maptoExportTemplate').html())
+ self.generateBoxHTML = Hogan.compile($('#mapInfoBoxTemplate').html())
- self.userImageUrl = serverData['user.png']
+ self.userImageUrl = serverData['user.png']
- var querystring = window.location.search.replace(/^\?/, '')
- if (querystring === 'new') {
- self.open()
- $('.maptoExport').addClass('mapRequestTitle')
- $('#mapInfoName').trigger('click')
- $('#mapInfoName textarea').focus()
- $('#mapInfoName textarea').select()
- }
- },
- toggleBox: function(event) {
- var self = toExport
-
- if (self.isOpen) self.close()
- else self.open()
-
- event.stopPropagation()
- },
- open: function() {
- var self = toExport
- $('.mapInfoIcon div').addClass('hide')
- $('.maptoExport').fadeIn(200, function() {
- self.isOpen = true
- })
- },
- close: function() {
- var self = toExport
- $('.mapInfoIcon div').removeClass('hide')
- $('.maptoExport').fadeOut(200, function() {
- self.isOpen = false
- self.hidePermissionSelect()
- $('.mapContributors .tip').hide()
- })
- },
- load: function() {
- var self = toExport
-
- var map = map.Active.Map
-
- var obj = map.pick('permission', 'topic_count', 'synapse_count')
-
- var isCreator = map.authorizePermissionChange(map.Active.Mapper)
- var canEdit = map.authorizeToEdit(map.Active.Mapper)
- var relevantPeople = map.get('permission') === 'commons' ? map.DataModel.Mappers : map.DataModel.Collaborators
- var shareable = map.get('permission') !== 'private'
-
- obj['name'] = canEdit ? Hogan.compile(self.nameHTML).render({id: map.id, name: map.get('name')}) : map.get('name')
- obj['desc'] = canEdit ? Hogan.compile(self.descHTML).render({id: map.id, desc: map.get('desc')}) : map.get('desc')
- obj['map_creator_tip'] = isCreator ? self.changePermissionText : ''
-
- obj['contributor_count'] = relevantPeople.length
- obj['contributors_class'] = relevantPeople.length > 1 ? 'multiple' : ''
- obj['contributors_class'] += relevantPeople.length === 2 ? ' mTwo' : ''
- obj['contributor_image'] = relevantPeople.length > 0 ? relevantPeople.models[0].get('image') : self.userImageUrl
- obj['contributor_list'] = self.createContributorList()
-
- obj['user_name'] = isCreator ? 'You' : map.get('user_name')
- obj['created_at'] = map.get('created_at_clean')
- obj['updated_at'] = map.get('updated_at_clean')
-
- self.html = self.generateBoxHTML.render(obj)
- ReactApp.render()
- self.attachEventListeners()
- },
- attachEventListeners: function() {
- var self = toExport
-
- $('.maptoExport.canEdit .best_in_place').best_in_place()
-
- // because anyone who can edit the map can change the map title
- 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]
-
- $el.attr('maxlength', '140')
-
- $('.mapInfoName').append('
')
-
- var callback = function(data) {
- $('.nameCounter.forMap').html(data.all + '/140')
+ var querystring = window.location.search.replace(/^\?/, '')
+ if (querystring === 'new') {
+ self.open()
+ $('.mapInfoBox').addClass('mapRequestTitle')
+ $('#mapInfoName').trigger('click')
+ $('#mapInfoName textarea').focus()
+ $('#mapInfoName textarea').select()
}
- Countable.live(el, callback)
- })
- bipName.unbind('best_in_place:deactivate').bind('best_in_place:deactivate', function() {
- $('.nameCounter.forMap').remove()
- })
+ },
+ toggleBox: function(event) {
+ var self = toExport
- $('.mapInfoName .best_in_place_name').unbind('ajax:success').bind('ajax:success', function() {
- var name = $(this).html()
- map.Active.Map.set('name', name)
- map.Active.Map.trigger('saved')
- // mobile menu
- $('#header_content').html(name)
- $('.maptoExport').removeClass('mapRequestTitle')
- document.title = `${name} | Metamaps`
- window.history.replaceState('', `${name} | Metamaps`, window.location.pathname)
- })
+ if (self.isOpen) self.close()
+ else self.open()
- $('.mapInfoDesc .best_in_place_desc').unbind('ajax:success').bind('ajax:success', function() {
- var desc = $(this).html()
- map.Active.Map.set('desc', desc)
- map.Active.Map.trigger('saved')
- })
-
- $('.mapInfoDesc .best_in_place_desc, .mapInfoName .best_in_place_name').unbind('keypress').keypress(function(e) {
- const ENTER = 13
- if (e.which === ENTER) {
- $(this).data('bestInPlaceEditor').update()
- }
- })
-
- $('.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 .maptoExport
- $('.maptoExport.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect)
-
- $('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap)
- $('.mapInfoThumbnail').unbind().click(self.updateThumbnail)
-
- $('.mapContributors span, #mapContribs').unbind().click(function(event) {
- $('.mapContributors .tip').toggle()
event.stopPropagation()
- })
- $('.mapContributors .tip').unbind().click(function(event) {
- event.stopPropagation()
- })
+ },
+ open: function() {
+ var self = toExport
+ $('.mapInfoIcon div').addClass('hide')
+ $('.maptoExport').fadeIn(200, function() {
+ self.isOpen = true
+ })
+ },
+ close: function() {
+ var self = toExport
+ $('.mapInfoIcon div').removeClass('hide')
+ $('.mapInfoBox').fadeOut(200, function() {
+ self.isOpen = false
+ self.hidePermissionSelect()
+ $('.mapContributors .tip').hide()
+ })
+ },
+ load: function() {
+ var self = toExport
- $('.maptoExport').unbind('.hideTip').bind('click.hideTip', function() {
- $('.mapContributors .tip').hide()
- })
+ var m = map.Active.Map
- self.addTypeahead()
- },
- addTypeahead: function() {
- var self = toExport
+ var obj = m.pick('permission', 'topic_count', 'synapse_count')
- if (!map.Active.Map) return
+ var isCreator = m.authorizePermissionChange(map.Active.Mapper)
+ var canEdit = m.authorizeToEdit(map.Active.Mapper)
+ var relevantPeople = m.get('permission') === 'commons' ? map.DataModel.Mappers : map.DataModel.Collaborators
+ var shareable = m.get('permission') !== 'private'
- // for autocomplete
- var collaborators = {
- name: 'collaborators',
- limit: 9999,
- display: function(s) { return s.label },
- templates: {
- notFound: function(s) {
- return Hogan.compile($('#collaboratorSearchTemplate').html()).render({
- value: 'No results',
- label: 'No results',
- rtype: 'noresult',
- profile: self.userImageUrl
- })
- },
- suggestion: function(s) {
- return Hogan.compile($('#collaboratorSearchTemplate').html()).render(s)
+ obj['name'] = canEdit ? Hogan.compile(self.nameHTML).render({id: m.id, name: m.get('name')}) : m.get('name')
+ obj['desc'] = canEdit ? Hogan.compile(self.descHTML).render({id: m.id, desc: m.get('desc')}) : m.get('desc')
+ obj['map_creator_tip'] = isCreator ? self.changePermissionText : ''
+
+ obj['contributor_count'] = relevantPeople.length
+ obj['contributors_class'] = relevantPeople.length > 1 ? 'multiple' : ''
+ obj['contributors_class'] += relevantPeople.length === 2 ? ' mTwo' : ''
+ obj['contributor_image'] = relevantPeople.length > 0 ? relevantPeople.models[0].get('image') : self.userImageUrl
+ obj['contributor_list'] = self.createContributorList()
+
+ obj['user_name'] = isCreator ? 'You' : m.get('user_name')
+ obj['created_at'] = m.get('created_at_clean')
+ obj['updated_at'] = m.get('updated_at_clean')
+
+ self.generateBoxHTML = self.generateBoxHTML || Hogan.compile($('#mapInfoBoxTemplate').html())
+ self.html = self.generateBoxHTML.render(obj)
+ ReactApp.render()
+ self.attachEventListeners()
+ },
+ attachEventListeners: function() {
+ var self = toExport
+
+ $('.maptoExport.canEdit .best_in_place').best_in_place()
+
+ // because anyone who can edit the map can change the map title
+ 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]
+
+ $el.attr('maxlength', '140')
+
+ $('.mapInfoName').append('
')
+
+ var callback = function(data) {
+ $('.nameCounter.forMap').html(data.all + '/140')
}
- },
- source: new Bloodhound({
- datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
- queryTokenizer: Bloodhound.tokenizers.whitespace,
- remote: {
- url: '/search/mappers?term=%QUERY',
- wildcard: '%QUERY'
+ Countable.live(el, callback)
+ })
+ bipName.unbind('best_in_place:deactivate').bind('best_in_place:deactivate', function() {
+ $('.nameCounter.forMap').remove()
+ })
+
+ $('.mapInfoName .best_in_place_name').unbind('ajax:success').bind('ajax:success', function() {
+ var name = $(this).html()
+ map.Active.Map.set('name', name)
+ map.Active.Map.trigger('saved')
+ // mobile menu
+ $('#header_content').html(name)
+ $('.maptoExport').removeClass('mapRequestTitle')
+ document.title = `${name} | Metamaps`
+ window.history.replaceState('', `${name} | Metamaps`, window.location.pathname)
+ })
+
+ $('.mapInfoDesc .best_in_place_desc').unbind('ajax:success').bind('ajax:success', function() {
+ var desc = $(this).html()
+ map.Active.Map.set('desc', desc)
+ map.Active.Map.trigger('saved')
+ })
+
+ $('.mapInfoDesc .best_in_place_desc, .mapInfoName .best_in_place_name').unbind('keypress').keypress(function(e) {
+ const ENTER = 13
+ if (e.which === ENTER) {
+ $(this).data('bestInPlaceEditor').update()
}
})
- }
- // for adding map collaborators, who will have edit rights
- if (map.Active.Mapper && map.Active.Mapper.id === map.Active.Map.get('user_id')) {
- $('.collaboratorSearchField').typeahead(
- {
- highlight: false
- },
- [collaborators]
- )
- $('.collaboratorSearchField').bind('typeahead:select', self.handleResultClick)
- $('.mapContributors .removeCollaborator').click(function() {
- self.removeCollaborator(parseInt($(this).data('id')))
+ $('.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 .maptoExport
+ $('.maptoExport.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect)
+
+ $('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap)
+ $('.mapInfoThumbnail').unbind().click(self.updateThumbnail)
+
+ $('.mapContributors span, #mapContribs').unbind().click(function(event) {
+ $('.mapContributors .tip').toggle()
+ event.stopPropagation()
+ })
+ $('.mapContributors .tip').unbind().click(function(event) {
+ event.stopPropagation()
})
- }
- },
- removeCollaborator: function(collaboratorId) {
- var self = toExport
- map.DataModel.Collaborators.remove(map.DataModel.Collaborators.get(collaboratorId))
- var mapperIds = map.DataModel.Collaborators.models.map(function(mapper) { return mapper.id })
- $.post('/maps/' + map.Active.Map.id + '/access', { access: mapperIds })
- self.updateNumbers()
- },
- addCollaborator: function(newCollaboratorId) {
- var self = toExport
- if (map.DataModel.Collaborators.get(newCollaboratorId)) {
- GlobalUI.notifyUser('That user already has access')
- return
- }
+ $('.maptoExport').unbind('.hideTip').bind('click.hideTip', function() {
+ $('.mapContributors .tip').hide()
+ })
- function callback(mapper) {
- map.DataModel.Collaborators.add(mapper)
+ self.addTypeahead()
+ },
+ addTypeahead: function() {
+ var self = toExport
+
+ if (!map.Active.Map) return
+
+ // for autocomplete
+ var collaborators = {
+ name: 'collaborators',
+ limit: 9999,
+ display: function(s) { return s.label },
+ templates: {
+ notFound: function(s) {
+ return Hogan.compile($('#collaboratorSearchTemplate').html()).render({
+ value: 'No results',
+ label: 'No results',
+ rtype: 'noresult',
+ profile: self.userImageUrl
+ })
+ },
+ suggestion: function(s) {
+ return Hogan.compile($('#collaboratorSearchTemplate').html()).render(s)
+ }
+ },
+ source: new Bloodhound({
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ remote: {
+ url: '/search/mappers?term=%QUERY',
+ wildcard: '%QUERY'
+ }
+ })
+ }
+
+ // for adding map collaborators, who will have edit rights
+ if (map.Active.Mapper && map.Active.Mapper.id === map.Active.Map.get('user_id')) {
+ $('.collaboratorSearchField').typeahead(
+ {
+ highlight: false
+ },
+ [collaborators]
+ )
+ $('.collaboratorSearchField').bind('typeahead:select', self.handleResultClick)
+ $('.mapContributors .removeCollaborator').click(function() {
+ self.removeCollaborator(parseInt($(this).data('id')))
+ })
+ }
+ },
+ removeCollaborator: function(collaboratorId) {
+ var self = toExport
+ map.DataModel.Collaborators.remove(map.DataModel.Collaborators.get(collaboratorId))
var mapperIds = map.DataModel.Collaborators.models.map(function(mapper) { return mapper.id })
$.post('/maps/' + map.Active.Map.id + '/access', { access: mapperIds })
- var name = map.DataModel.Collaborators.get(newCollaboratorId).get('name')
- GlobalUI.notifyUser(name + ' will be notified')
self.updateNumbers()
- }
+ },
+ addCollaborator: function(newCollaboratorId) {
+ var self = toExport
- $.getJSON('/users/' + newCollaboratorId + '.json', callback)
- },
- handleResultClick: function(event, item) {
- var self = toExport
-
- self.addCollaborator(item.id)
- $('.collaboratorSearchField').typeahead('val', '')
- },
- updateNameDescPerm: function(name, desc, perm) {
- $('.maptoExport').removeClass('mapRequestTitle')
- $('.mapInfoName .best_in_place_name').html(name)
- $('.mapInfoDesc .best_in_place_desc').html(desc)
- $('.maptoExport .mapPermission').removeClass('commons public private').addClass(perm)
- },
- createContributorList: function() {
- var relevantPeople = map.Active.Map.get('permission') === 'commons' ? map.DataModel.Mappers : map.DataModel.Collaborators
- var activeMapperIsCreator = map.Active.Mapper && map.Active.Mapper.id === map.Active.Map.get('user_id')
- var string = ''
- string += ''
-
- if (activeMapperIsCreator) {
- string += '
'
- }
- return string
- },
- updateNumbers: function() {
- if (!map.Active.Map) return
-
- const self = toExport
-
- var relevantPeople = map.Active.Map.get('permission') === 'commons' ? map.DataModel.Mappers : map.DataModel.Collaborators
-
- let contributorsClass = ''
- if (relevantPeople.length === 2) {
- contributorsClass = 'multiple mTwo'
- } else if (relevantPeople.length > 2) {
- contributorsClass = 'multiple'
- }
-
- let contributorsImage = self.userImageUrl
- if (relevantPeople.length > 0) {
- // get the first contributor and use their image
- contributorsImage = relevantPeople.models[0].get('image')
- }
- $('.mapContributors img').attr('src', contributorsImage).removeClass('multiple mTwo').addClass(contributorsClass)
- $('.mapContributors span').text(relevantPeople.length)
- $('.mapContributors .tip').html(self.createContributorList())
- self.addTypeahead()
- $('.mapContributors .tip').unbind().click(function(event) {
- event.stopPropagation()
- })
- $('.mapTopics').text(map.DataModel.Topics.length)
- $('.mapSynapses').text(map.DataModel.Synapses.length)
-
- $('.mapEditedAt').html('Last edited: ' + Util.nowDateFormatted())
- },
- onPermissionClick: function(event) {
- var self = toExport
-
- if (!self.selectingPermission) {
- self.selectingPermission = true
- $(this).addClass('minimize') // this line flips the drop down arrow to a pull up arrow
- if ($(this).hasClass('commons')) {
- $(this).append('')
- } else if ($(this).hasClass('public')) {
- $(this).append('')
- } else if ($(this).hasClass('private')) {
- $(this).append('')
+ if (map.DataModel.Collaborators.get(newCollaboratorId)) {
+ GlobalUI.notifyUser('That user already has access')
+ return
}
- $('.mapPermission .permissionSelect li').click(self.selectPermission)
+
+ function callback(mapper) {
+ map.DataModel.Collaborators.add(mapper)
+ var mapperIds = map.DataModel.Collaborators.models.map(function(mapper) { return mapper.id })
+ $.post('/maps/' + map.Active.Map.id + '/access', { access: mapperIds })
+ var name = map.DataModel.Collaborators.get(newCollaboratorId).get('name')
+ GlobalUI.notifyUser(name + ' will be notified')
+ self.updateNumbers()
+ }
+
+ $.getJSON('/users/' + newCollaboratorId + '.json', callback)
+ },
+ handleResultClick: function(event, item) {
+ var self = toExport
+
+ self.addCollaborator(item.id)
+ $('.collaboratorSearchField').typeahead('val', '')
+ },
+ updateNameDescPerm: function(name, desc, perm) {
+ $('.maptoExport').removeClass('mapRequestTitle')
+ $('.mapInfoName .best_in_place_name').html(name)
+ $('.mapInfoDesc .best_in_place_desc').html(desc)
+ $('.maptoExport .mapPermission').removeClass('commons public private').addClass(perm)
+ },
+ createContributorList: function() {
+ var relevantPeople = map.Active.Map.get('permission') === 'commons' ? map.DataModel.Mappers : map.DataModel.Collaborators
+ var activeMapperIsCreator = map.Active.Mapper && map.Active.Mapper.id === map.Active.Map.get('user_id')
+ var string = ''
+ string += ''
+
+ if (activeMapperIsCreator) {
+ string += '
'
+ }
+ return string
+ },
+ updateNumbers: function() {
+ if (!map.Active.Map) return
+
+ const self = toExport
+
+ var relevantPeople = map.Active.Map.get('permission') === 'commons' ? map.DataModel.Mappers : map.DataModel.Collaborators
+
+ let contributorsClass = ''
+ if (relevantPeople.length === 2) {
+ contributorsClass = 'multiple mTwo'
+ } else if (relevantPeople.length > 2) {
+ contributorsClass = 'multiple'
+ }
+
+ let contributorsImage = self.userImageUrl
+ if (relevantPeople.length > 0) {
+ // get the first contributor and use their image
+ contributorsImage = relevantPeople.models[0].get('image')
+ }
+ $('.mapContributors img').attr('src', contributorsImage).removeClass('multiple mTwo').addClass(contributorsClass)
+ $('.mapContributors span').text(relevantPeople.length)
+ $('.mapContributors .tip').html(self.createContributorList())
+ self.addTypeahead()
+ $('.mapContributors .tip').unbind().click(function(event) {
+ event.stopPropagation()
+ })
+ $('.mapTopics').text(map.DataModel.Topics.length)
+ $('.mapSynapses').text(map.DataModel.Synapses.length)
+
+ $('.mapEditedAt').html('Last edited: ' + Util.nowDateFormatted())
+ },
+ onPermissionClick: function(event) {
+ var self = toExport
+
+ if (!self.selectingPermission) {
+ self.selectingPermission = true
+ $(this).addClass('minimize') // this line flips the drop down arrow to a pull up arrow
+ if ($(this).hasClass('commons')) {
+ $(this).append('')
+ } else if ($(this).hasClass('public')) {
+ $(this).append('')
+ } else if ($(this).hasClass('private')) {
+ $(this).append('')
+ }
+ $('.mapPermission .permissionSelect li').click(self.selectPermission)
+ event.stopPropagation()
+ }
+ },
+ hidePermissionSelect: function() {
+ var self = 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 = toExport
+
+ self.selectingPermission = false
+ var permission = $(this).attr('class')
+ map.Active.Map.save({
+ permission: permission
+ })
+ map.Active.Map.updateMapWrapper()
+ const shareable = permission === 'private' ? '' : 'shareable'
+ $('.mapPermission').removeClass('commons public private minimize').addClass(permission)
+ $('.mapPermission .permissionSelect').remove()
+ $('.maptoExport').removeClass('shareable').addClass(shareable)
event.stopPropagation()
- }
- },
- hidePermissionSelect: function() {
- var self = toExport
+ },
+ deleteActiveMap: function() {
+ var confirmString = 'Are you sure you want to delete this map? '
+ confirmString += 'This action is irreversible. It will not delete the topics and synapses on the map.'
- 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 = toExport
+ var doIt = window.confirm(confirmString)
+ var m = map.Active.Map
+ var mapper = map.Active.Mapper
+ var authorized = map.authorizePermissionChange(mapper)
- self.selectingPermission = false
- var permission = $(this).attr('class')
- map.Active.Map.save({
- permission: permission
- })
- map.Active.Map.updateMapWrapper()
- const shareable = permission === 'private' ? '' : 'shareable'
- $('.mapPermission').removeClass('commons public private minimize').addClass(permission)
- $('.mapPermission .permissionSelect').remove()
- $('.maptoExport').removeClass('shareable').addClass(shareable)
- event.stopPropagation()
- },
- deleteActiveMap: function() {
- var confirmString = 'Are you sure you want to delete this map? '
- confirmString += 'This action is irreversible. It will not delete the topics and synapses on the map.'
-
- var doIt = window.confirm(confirmString)
- var map = map.Active.Map
- var mapper = map.Active.Mapper
- var authorized = map.authorizePermissionChange(mapper)
-
- if (doIt && authorized) {
- toExport.close()
- DataModel.Maps.map.Active.remove(map)
- DataModel.Maps.Featured.remove(map)
- DataModel.Maps.Mine.remove(map)
- DataModel.Maps.Shared.remove(map)
- map.destroy()
- browserHistory.push('/')
- GlobalUI.notifyUser('Map eliminated')
- } else if (!authorized) {
- window.alert("Hey now. We can't just go around willy nilly deleting other people's maps now can we? Run off and find something constructive to do, eh?")
+ if (doIt && authorized) {
+ toExport.close()
+ DataModel.Maps.map.Active.remove(m)
+ DataModel.Maps.Featured.remove(m)
+ DataModel.Maps.Mine.remove(m)
+ DataModel.Maps.Shared.remove(m)
+ m.destroy()
+ browserHistory.push('/')
+ GlobalUI.notifyUser('Map eliminated')
+ } else if (!authorized) {
+ window.alert("Hey now. We can't just go around willy nilly deleting other people's maps now can we? Run off and find something constructive to do, eh?")
+ }
}
}
-}
-return toExport
+ return toExport
}
export default InfoBox
diff --git a/frontend/src/Metamaps/Map/JIT.js b/frontend/src/Metamaps/Map/JIT.js
index 6d2448c9..b1eea1d2 100644
--- a/frontend/src/Metamaps/Map/JIT.js
+++ b/frontend/src/Metamaps/Map/JIT.js
@@ -17,1951 +17,1950 @@ import Util from '../Util'
let panningInt
const JIT = (map) => {
+ const toExport = {
+ tempInit: false,
+ tempNode: null,
+ tempNode2: null,
+ mouseDownPix: {},
+ dragFlag: 0,
+ dragTolerance: 0,
+ virtualPointer: {},
+ vizData: [], // contains the visualization-compatible graph
+ /**
+ * convert our topic JSON into something JIT can use
+ */
+ convertModelsToJIT: function(topics, synapses) {
+ const jitReady = []
-return {
- tempInit: false,
- tempNode: null,
- tempNode2: null,
- mouseDownPix: {},
- dragFlag: 0,
- dragTolerance: 0,
- virtualPointer: {},
+ const synapsesToRemove = []
+ let mapping
+ let node
+ const nodes = {}
+ let existingEdge
+ let edge
+ const edges = []
- events: {
- topicDrag: 'Metamaps:JIT:events:topicDrag',
- pan: 'Metamaps:JIT:events:pan',
- zoom: 'Metamaps:JIT:events:zoom',
- animationDone: 'Metamaps:JIT:events:animationDone'
- },
- vizData: [], // contains the visualization-compatible graph
- /**
- * convert our topic JSON into something JIT can use
- */
- convertModelsToJIT: function(topics, synapses) {
- const jitReady = []
+ topics.each(function(t) {
+ node = t.createNode()
+ nodes[node.id] = node
+ })
+ synapses.each(function(s) {
+ edge = s.createEdge()
- const synapsesToRemove = []
- let mapping
- let node
- const nodes = {}
- let existingEdge
- let edge
- const edges = []
+ if (topics.get(s.get('topic1_id')) === undefined || topics.get(s.get('topic2_id')) === undefined) {
+ // this means it's an invalid synapse
+ synapsesToRemove.push(s)
+ } else if (nodes[edge.nodeFrom] && nodes[edge.nodeTo]) {
+ existingEdge = _.find(edges, {
+ nodeFrom: edge.nodeFrom,
+ nodeTo: edge.nodeTo
+ }) ||
+ _.find(edges, {
+ nodeFrom: edge.nodeTo,
+ nodeTo: edge.nodeFrom
+ })
- topics.each(function(t) {
- node = t.createNode()
- nodes[node.id] = node
- })
- synapses.each(function(s) {
- edge = s.createEdge()
-
- if (topics.get(s.get('topic1_id')) === undefined || topics.get(s.get('topic2_id')) === undefined) {
- // this means it's an invalid synapse
- synapsesToRemove.push(s)
- } else if (nodes[edge.nodeFrom] && nodes[edge.nodeTo]) {
- existingEdge = _.find(edges, {
- nodeFrom: edge.nodeFrom,
- nodeTo: edge.nodeTo
- }) ||
- _.find(edges, {
- nodeFrom: edge.nodeTo,
- nodeTo: edge.nodeFrom
- })
-
- if (existingEdge) {
- // for when you're dealing with multiple relationships between the same two topics
- if (map.Active.Map) {
- mapping = s.getMapping()
- existingEdge.data['$mappingIDs'].push(mapping.id)
+ if (existingEdge) {
+ // for when you're dealing with multiple relationships between the same two topics
+ if (map.Active.Map) {
+ mapping = s.getMapping()
+ existingEdge.data['$mappingIDs'].push(mapping.id)
+ }
+ existingEdge.data['$synapseIDs'].push(s.id)
+ } else {
+ // for when you're dealing with a topic that has relationships to many different nodes
+ nodes[edge.nodeFrom].adjacencies.push(edge)
+ edges.push(edge)
}
- existingEdge.data['$synapseIDs'].push(s.id)
- } else {
- // for when you're dealing with a topic that has relationships to many different nodes
- nodes[edge.nodeFrom].adjacencies.push(edge)
- edges.push(edge)
}
+ })
+
+ _.each(nodes, function(node) {
+ jitReady.push(node)
+ })
+
+ return [jitReady, synapsesToRemove]
+ },
+ prepareVizData: function() {
+ const self = toExport
+ let mapping
+ self.vizData = []
+ map.Visualize.loadLater = false
+ const results = self.convertModelsToJIT(map.DataModel.Topics, map.DataModel.Synapses)
+ self.vizData = results[0]
+ // clean up the synapses array in case of any faulty data
+ _.each(results[1], function(synapse) {
+ mapping = synapse.getMapping()
+ map.DataModel.Synapses.remove(synapse)
+ if (map.DataModel.Mappings) map.DataModel.Mappings.remove(mapping)
+ })
+ if (self.vizData.length === 0) {
+ map.Map.setHasLearnedTopicCreation(false)
+ map.Visualize.loadLater = true
+ } else {
+ map.Map.setHasLearnedTopicCreation(true)
}
- })
+ map.Visualize.render()
+ }, // prepareVizData
+ edgeRender: function(adj, canvas) {
+ // get nodes cartesian coordinates
+ const pos = adj.nodeFrom.pos.getc(true)
+ const posChild = adj.nodeTo.pos.getc(true)
- _.each(nodes, function(node) {
- jitReady.push(node)
- })
-
- return [jitReady, synapsesToRemove]
- },
- prepareVizData: function() {
- const self = JIT
- let mapping
- self.vizData = []
- map.Visualize.loadLater = false
- const results = self.convertModelsToJIT(map.DataModel.Topics, map.DataModel.Synapses)
- self.vizData = results[0]
- // clean up the synapses array in case of any faulty data
- _.each(results[1], function(synapse) {
- mapping = synapse.getMapping()
- map.DataModel.Synapses.remove(synapse)
- if (map.DataModel.Mappings) map.DataModel.Mappings.remove(mapping)
- })
- if (self.vizData.length === 0) {
- Map.setHasLearnedTopicCreation(false)
- map.Visualize.loadLater = true
- } else {
- Map.setHasLearnedTopicCreation(true)
- }
- map.Visualize.render()
- }, // prepareVizData
- edgeRender: function(adj, canvas) {
- // get nodes cartesian coordinates
- const pos = adj.nodeFrom.pos.getc(true)
- const posChild = adj.nodeTo.pos.getc(true)
-
- let synapse
- if (adj.getData('displayIndex')) {
- synapse = adj.getData('synapses')[adj.getData('displayIndex')]
- if (!synapse) {
- delete adj.data.$displayIndex
+ let synapse
+ if (adj.getData('displayIndex')) {
+ synapse = adj.getData('synapses')[adj.getData('displayIndex')]
+ if (!synapse) {
+ delete adj.data.$displayIndex
+ synapse = adj.getData('synapses')[0]
+ }
+ } else {
synapse = adj.getData('synapses')[0]
}
- } else {
- synapse = adj.getData('synapses')[0]
- }
- if (!synapse) return // this means there are no corresponding synapses for
- // this edge, don't render it
+ if (!synapse) return // this means there are no corresponding synapses for
+ // this edge, don't render it
- // label placement on edges
- if (canvas.denySelected) {
- const color = Settings.colors.synapses.normal
- canvas.getCtx().fillStyle = canvas.getCtx().strokeStyle = color
- }
- map.JIT.renderEdgeArrows($jit.Graph.Plot.edgeHelper, adj, synapse, canvas)
-
- // check for edge label in data
- let desc = synapse.get('desc')
-
- const showDesc = adj.getData('showDesc')
-
- const drawSynapseCount = function(context, x, y, count) {
- /*
- circle size: 16x16px
- positioning: overlay and center on top right corner of synapse label - 8px left and 8px down
- color: #dab539
- border color: #424242
- border size: 1.5px
- font: DIN medium
- font-size: 14pt
- font-color: #424242
- */
- context.beginPath()
- context.arc(x, y, 8, 0, 2 * Math.PI, false)
- context.fillStyle = '#DAB539'
- context.strokeStyle = '#424242'
- context.lineWidth = 1.5
- context.closePath()
- context.fill()
- context.stroke()
-
- // add the synapse count
- context.fillStyle = '#424242'
- context.textAlign = 'center'
- context.font = '14px din-medium'
-
- context.fillText(count, x, y + 5)
- }
-
- if (!canvas.denySelected && desc !== '' && showDesc) {
- // '&' to '&'
- desc = Util.decodeEntities(desc)
-
- // now adjust the label placement
- const ctx = canvas.getCtx()
- ctx.font = 'bold 14px arial'
- ctx.fillStyle = '#FFF'
- ctx.textBaseline = 'alphabetic'
-
- const arrayOfLabelLines = Util.splitLine(desc, 25).split('\n')
- let lineWidths = []
- for (let index = 0; index < arrayOfLabelLines.length; ++index) {
- lineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width)
+ // label placement on edges
+ if (canvas.denySelected) {
+ const color = Settings.colors.synapses.normal
+ canvas.getCtx().fillStyle = canvas.getCtx().strokeStyle = color
}
- const width = Math.max.apply(null, lineWidths) + 16
- const height = (16 * arrayOfLabelLines.length) + 8
+ map.JIT.renderEdgeArrows($jit.Graph.Plot.edgeHelper, adj, synapse, canvas)
- const x = (pos.x + posChild.x - width) / 2
- const y = ((pos.y + posChild.y) / 2) - height / 2
+ // check for edge label in data
+ let desc = synapse.get('desc')
- const radius = 5
+ const showDesc = adj.getData('showDesc')
- // render background
- ctx.beginPath()
- ctx.moveTo(x + radius, y)
- ctx.lineTo(x + width - radius, y)
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
- ctx.lineTo(x + width, y + height - radius)
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height)
- ctx.lineTo(x + radius, y + height)
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
- ctx.lineTo(x, y + radius)
- ctx.quadraticCurveTo(x, y, x + radius, y)
- ctx.closePath()
- ctx.fill()
+ const drawSynapseCount = function(context, x, y, count) {
+ /*
+ circle size: 16x16px
+ positioning: overlay and center on top right corner of synapse label - 8px left and 8px down
+ color: #dab539
+ border color: #424242
+ border size: 1.5px
+ font: DIN medium
+ font-size: 14pt
+ font-color: #424242
+ */
+ context.beginPath()
+ context.arc(x, y, 8, 0, 2 * Math.PI, false)
+ context.fillStyle = '#DAB539'
+ context.strokeStyle = '#424242'
+ context.lineWidth = 1.5
+ context.closePath()
+ context.fill()
+ context.stroke()
- // get number of synapses
- const synapseNum = adj.getData('synapses').length
+ // add the synapse count
+ context.fillStyle = '#424242'
+ context.textAlign = 'center'
+ context.font = '14px din-medium'
- // render text
- ctx.fillStyle = '#424242'
- ctx.textAlign = 'center'
- for (let index = 0; index < arrayOfLabelLines.length; ++index) {
- ctx.fillText(arrayOfLabelLines[index], x + (width / 2), y + 18 + (16 * index))
+ context.fillText(count, x, y + 5)
}
- if (synapseNum > 1) {
- drawSynapseCount(ctx, x + width, y, synapseNum)
- }
- } else if (!canvas.denySelected && showDesc) {
- // get number of synapses
- const synapseNum = adj.getData('synapses').length
+ if (!canvas.denySelected && desc !== '' && showDesc) {
+ // '&' to '&'
+ desc = Util.decodeEntities(desc)
- if (synapseNum > 1) {
+ // now adjust the label placement
const ctx = canvas.getCtx()
- const x = (pos.x + posChild.x) / 2
- const y = (pos.y + posChild.y) / 2
- drawSynapseCount(ctx, x, y, synapseNum)
- }
- }
- }, // edgeRender
- ForceDirected: {
- animateSavedLayout: {
- modes: ['linear'],
- // TODO fix tests so we don't need _.get
- transition: _.get($jit, 'Trans.Quad.easeInOut'),
- duration: 800,
- onComplete: function() {
- map.Visualize.mGraph.busy = false
- $(document).trigger(map.JIT.events.animationDone)
- }
- },
- animateFDLayout: {
- modes: ['linear'],
- // TODO fix tests so we don't need _.get
- transition: _.get($jit, 'Trans.Elastic.easeOut'),
- duration: 800,
- onComplete: function() {
- map.Visualize.mGraph.busy = false
- }
- },
- graphSettings: {
- // id of the visualization container
- injectInto: 'infovis',
- // Enable zooming and panning
- // by scrolling and DnD
- Navigation: {
- enable: true,
- // Enable panning events only if we're dragging the empty
- // canvas (and not a node).
- panning: 'avoid nodes',
- zooming: 28 // zoom speed. higher is more sensible
- },
- // Change node and edge styles such as
- // color and width.
- // These properties are also set per node
- // with dollar prefixed data-properties in the
- // JSON structure.
- Node: {
- overridable: true,
- color: '#2D6A5D',
- type: 'customNode',
- dim: 25
- },
- Edge: {
- overridable: true,
- color: Settings.colors.synapses.normal,
- type: 'customEdge',
- lineWidth: 2,
- alpha: 1
- },
- // Native canvas text styling
- Label: {
- type: 'Native', // Native or HTML
- size: 20,
- family: 'arial',
- textBaseline: 'alphabetic',
- color: Settings.colors.labels.text
- },
- // Add Tips
- Tips: {
- enable: false,
- onShow: function(tip, node) {}
- },
- // Add node events
- Events: {
- enable: true,
- enableForEdges: true,
- onMouseMove: function(node, eventInfo, e) {
- map.JIT.onMouseMoveHandler(node, eventInfo, e)
- // console.log('called mouse move handler')
- },
- // Update node positions when dragged
- onDragMove: function(node, eventInfo, e) {
- map.JIT.onDragMoveTopicHandler(node, eventInfo, e)
- // console.log('called drag move handler')
- },
- onDragEnd: function(node, eventInfo, e) {
- map.JIT.onDragEndTopicHandler(node, eventInfo, e, false)
- // console.log('called drag end handler')
- },
- onDragCancel: function(node, eventInfo, e) {
- map.JIT.onDragCancelHandler(node, eventInfo, e, false)
- },
- // Implement the same handler for touchscreens
- onTouchStart: function(node, eventInfo, e) {},
- // Implement the same handler for touchscreens
- onTouchMove: function(node, eventInfo, e) {
- map.JIT.onDragMoveTopicHandler(node, eventInfo, e)
- },
- // Implement the same handler for touchscreens
- onTouchEnd: function(node, eventInfo, e) {},
- // Implement the same handler for touchscreens
- onTouchCancel: function(node, eventInfo, e) {},
- // Add also a click handler to nodes
- onClick: function(node, eventInfo, e) {
- // remove the rightclickmenu
- $('.rightclickmenu').remove()
+ ctx.font = 'bold 14px arial'
+ ctx.fillStyle = '#FFF'
+ ctx.textBaseline = 'alphabetic'
- if (map.Mouse.boxStartCoordinates) {
- if (e.ctrlKey) {
- map.Visualize.mGraph.busy = false
- map.Mouse.boxEndCoordinates = eventInfo.getPos()
+ const arrayOfLabelLines = Util.splitLine(desc, 25).split('\n')
+ let lineWidths = []
+ for (let index = 0; index < arrayOfLabelLines.length; ++index) {
+ lineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width)
+ }
+ const width = Math.max.apply(null, lineWidths) + 16
+ const height = (16 * arrayOfLabelLines.length) + 8
+
+ const x = (pos.x + posChild.x - width) / 2
+ const y = ((pos.y + posChild.y) / 2) - height / 2
+
+ const radius = 5
+
+ // render background
+ ctx.beginPath()
+ ctx.moveTo(x + radius, y)
+ ctx.lineTo(x + width - radius, y)
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
+ ctx.lineTo(x + width, y + height - radius)
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height)
+ ctx.lineTo(x + radius, y + height)
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
+ ctx.lineTo(x, y + radius)
+ ctx.quadraticCurveTo(x, y, x + radius, y)
+ ctx.closePath()
+ ctx.fill()
+
+ // get number of synapses
+ const synapseNum = adj.getData('synapses').length
+
+ // render text
+ ctx.fillStyle = '#424242'
+ ctx.textAlign = 'center'
+ for (let index = 0; index < arrayOfLabelLines.length; ++index) {
+ ctx.fillText(arrayOfLabelLines[index], x + (width / 2), y + 18 + (16 * index))
+ }
+
+ if (synapseNum > 1) {
+ drawSynapseCount(ctx, x + width, y, synapseNum)
+ }
+ } else if (!canvas.denySelected && showDesc) {
+ // get number of synapses
+ const synapseNum = adj.getData('synapses').length
+
+ if (synapseNum > 1) {
+ const ctx = canvas.getCtx()
+ const x = (pos.x + posChild.x) / 2
+ const y = (pos.y + posChild.y) / 2
+ drawSynapseCount(ctx, x, y, synapseNum)
+ }
+ }
+ }, // edgeRender
+ ForceDirected: {
+ animateSavedLayout: {
+ modes: ['linear'],
+ // TODO fix tests so we don't need _.get
+ transition: _.get($jit, 'Trans.Quad.easeInOut'),
+ duration: 800,
+ onComplete: function() {
+ map.Visualize.mGraph.busy = false
+ $(document).trigger(JIT.events.animationDone)
+ }
+ },
+ animateFDLayout: {
+ modes: ['linear'],
+ // TODO fix tests so we don't need _.get
+ transition: _.get($jit, 'Trans.Elastic.easeOut'),
+ duration: 800,
+ onComplete: function() {
+ map.Visualize.mGraph.busy = false
+ }
+ },
+ graphSettings: {
+ // id of the visualization container
+ injectInto: 'infovis',
+ // Enable zooming and panning
+ // by scrolling and DnD
+ Navigation: {
+ enable: true,
+ // Enable panning events only if we're dragging the empty
+ // canvas (and not a node).
+ panning: 'avoid nodes',
+ zooming: 28 // zoom speed. higher is more sensible
+ },
+ // Change node and edge styles such as
+ // color and width.
+ // These properties are also set per node
+ // with dollar prefixed data-properties in the
+ // JSON structure.
+ Node: {
+ overridable: true,
+ color: '#2D6A5D',
+ type: 'customNode',
+ dim: 25
+ },
+ Edge: {
+ overridable: true,
+ color: Settings.colors.synapses.normal,
+ type: 'customEdge',
+ lineWidth: 2,
+ alpha: 1
+ },
+ // Native canvas text styling
+ Label: {
+ type: 'Native', // Native or HTML
+ size: 20,
+ family: 'arial',
+ textBaseline: 'alphabetic',
+ color: Settings.colors.labels.text
+ },
+ // Add Tips
+ Tips: {
+ enable: false,
+ onShow: function(tip, node) {}
+ },
+ // Add node events
+ Events: {
+ enable: true,
+ enableForEdges: true,
+ onMouseMove: function(node, eventInfo, e) {
+ map.JIT.onMouseMoveHandler(node, eventInfo, e)
+ // console.log('called mouse move handler')
+ },
+ // Update node positions when dragged
+ onDragMove: function(node, eventInfo, e) {
+ map.JIT.onDragMoveTopicHandler(node, eventInfo, e)
+ // console.log('called drag move handler')
+ },
+ onDragEnd: function(node, eventInfo, e) {
+ map.JIT.onDragEndTopicHandler(node, eventInfo, e, false)
+ // console.log('called drag end handler')
+ },
+ onDragCancel: function(node, eventInfo, e) {
+ map.JIT.onDragCancelHandler(node, eventInfo, e, false)
+ },
+ // Implement the same handler for touchscreens
+ onTouchStart: function(node, eventInfo, e) {},
+ // Implement the same handler for touchscreens
+ onTouchMove: function(node, eventInfo, e) {
+ map.JIT.onDragMoveTopicHandler(node, eventInfo, e)
+ },
+ // Implement the same handler for touchscreens
+ onTouchEnd: function(node, eventInfo, e) {},
+ // Implement the same handler for touchscreens
+ onTouchCancel: function(node, eventInfo, e) {},
+ // Add also a click handler to nodes
+ onClick: function(node, eventInfo, e) {
+ // remove the rightclickmenu
+ $('.rightclickmenu').remove()
+
+ if (map.Mouse.boxStartCoordinates) {
+ if (e.ctrlKey) {
+ map.Visualize.mGraph.busy = false
+ map.Mouse.boxEndCoordinates = eventInfo.getPos()
+
+ const bS = map.Mouse.boxStartCoordinates
+ const bE = map.Mouse.boxEndCoordinates
+ if (Math.abs(bS.x - bE.x) > 20 && Math.abs(bS.y - bE.y) > 20) {
+ map.JIT.zoomToBox(e)
+ return
+ } else {
+ map.Mouse.boxStartCoordinates = null
+ map.Mouse.boxEndCoordinates = null
+ }
+ }
+
+ if (e.shiftKey) {
+ map.Visualize.mGraph.busy = false
+ map.Mouse.boxEndCoordinates = eventInfo.getPos()
+ map.JIT.selectWithBox(e)
- const bS = map.Mouse.boxStartCoordinates
- const bE = map.Mouse.boxEndCoordinates
- if (Math.abs(bS.x - bE.x) > 20 && Math.abs(bS.y - bE.y) > 20) {
- map.JIT.zoomToBox(e)
return
- } else {
- map.Mouse.boxStartCoordinates = null
- map.Mouse.boxEndCoordinates = null
}
}
- if (e.shiftKey) {
+ if (e.target.id !== 'infovis-canvas') return false
+
+ // clicking on a edge, node, or clicking on blank part of canvas?
+ if (node.nodeFrom) {
+ map.JIT.selectEdgeOnClickHandler(node, e)
+ } else if (node && !node.nodeFrom) {
+ map.JIT.selectNodeOnClickHandler(node, e)
+ } else {
+ map.JIT.canvasClickHandler(eventInfo.getPos(), e)
+ } // if
+ },
+ // Add also a click handler to nodes
+ onRightClick: function(node, eventInfo, e) {
+ // remove the rightclickmenu
+ $('.rightclickmenu').remove()
+
+ if (map.Mouse.boxStartCoordinates) {
+ map.Create.newSynapse.hide()
+ map.Create.newTopic.hide()
map.Visualize.mGraph.busy = false
map.Mouse.boxEndCoordinates = eventInfo.getPos()
map.JIT.selectWithBox(e)
-
return
}
+
+ if (e.target.id !== 'infovis-canvas') return false
+
+ // clicking on a edge, node, or clicking on blank part of canvas?
+ if (node.nodeFrom) {
+ map.JIT.selectEdgeOnRightClickHandler(node, e)
+ } else if (node && !node.nodeFrom) {
+ map.JIT.selectNodeOnRightClickHandler(node, e)
+ } else {
+ // right click open space
+ map.Create.newSynapse.hide()
+ map.Create.newTopic.hide()
+ }
}
-
- if (e.target.id !== 'infovis-canvas') return false
-
- // clicking on a edge, node, or clicking on blank part of canvas?
- if (node.nodeFrom) {
- map.JIT.selectEdgeOnClickHandler(node, e)
- } else if (node && !node.nodeFrom) {
- map.JIT.selectNodeOnClickHandler(node, e)
- } else {
- map.JIT.canvasClickHandler(eventInfo.getPos(), e)
- } // if
},
- // Add also a click handler to nodes
- onRightClick: function(node, eventInfo, e) {
- // remove the rightclickmenu
- $('.rightclickmenu').remove()
+ // Number of iterations for the FD algorithm
+ iterations: 200,
+ // Edge length
+ levelDistance: 200
+ },
+ nodeSettings: {
+ 'customNode': {
+ 'render': function(node, canvas) {
+ const pos = node.pos.getc(true)
+ const dim = node.getData('dim')
+ const topic = node.getData('topic')
+ const metacode = topic ? topic.getMetacode() : false
+ const ctx = canvas.getCtx()
- if (map.Mouse.boxStartCoordinates) {
- map.Create.newSynapse.hide()
- map.Create.newTopic.hide()
- map.Visualize.mGraph.busy = false
- map.Mouse.boxEndCoordinates = eventInfo.getPos()
- map.JIT.selectWithBox(e)
- return
- }
+ // if the topic is selected draw a circle around it
+ if (!canvas.denySelected && node.selected) {
+ ctx.beginPath()
+ ctx.arc(pos.x, pos.y, dim + 3, 0, 2 * Math.PI, false)
+ ctx.strokeStyle = Settings.colors.topics.selected
+ ctx.lineWidth = 2
+ ctx.stroke()
+ }
- if (e.target.id !== 'infovis-canvas') return false
+ if (!metacode ||
+ !metacode.get('image') ||
+ !metacode.get('image').complete ||
+ (typeof metacode.get('image').naturalWidth !== 'undefined' &&
+ metacode.get('image').naturalWidth === 0)) {
+ ctx.beginPath()
+ ctx.arc(pos.x, pos.y, dim, 0, 2 * Math.PI, false)
+ ctx.fillStyle = '#B6B2FD'
+ ctx.fill()
+ } else {
+ ctx.drawImage(metacode.get('image'), pos.x - dim, pos.y - dim, dim * 2, dim * 2)
+ }
- // clicking on a edge, node, or clicking on blank part of canvas?
- if (node.nodeFrom) {
- map.JIT.selectEdgeOnRightClickHandler(node, e)
- } else if (node && !node.nodeFrom) {
- map.JIT.selectNodeOnRightClickHandler(node, e)
- } else {
- // right click open space
- map.Create.newSynapse.hide()
- map.Create.newTopic.hide()
+ // if the topic has a link, draw a small image to indicate that
+ const hasLink = topic && topic.get('link') !== '' && topic.get('link') !== null
+ const linkImage = JIT.topicLinkImage
+ const linkImageLoaded = linkImage.complete ||
+ (typeof linkImage.naturalWidth !== 'undefined' &&
+ linkImage.naturalWidth !== 0)
+ if (hasLink && linkImageLoaded) {
+ ctx.drawImage(linkImage, pos.x - dim - 8, pos.y - dim - 8, 16, 16)
+ }
+
+ // if the topic has a desc, draw a small image to indicate that
+ const hasDesc = topic && topic.get('desc') !== '' && topic.get('desc') !== null
+ const descImage = JIT.topicDescImage
+ const descImageLoaded = descImage.complete ||
+ (typeof descImage.naturalWidth !== 'undefined' &&
+ descImage.naturalWidth !== 0)
+ if (hasDesc && descImageLoaded) {
+ ctx.drawImage(descImage, pos.x + dim - 8, pos.y - dim - 8, 16, 16)
+ }
+ },
+ 'contains': function(node, pos) {
+ const npos = node.pos.getc(true)
+ const dim = node.getData('dim')
+ const arrayOfLabelLines = Util.splitLine(node.name, 25).split('\n')
+ const ctx = map.Visualize.mGraph.canvas.getCtx()
+
+ const height = 25 * arrayOfLabelLines.length
+
+ let lineWidths = []
+ for (let index = 0; index < arrayOfLabelLines.length; ++index) {
+ lineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width)
+ }
+ const width = Math.max.apply(null, lineWidths) + 8
+ const labely = npos.y + node.getData('height') + 5 + height / 2
+
+ const overLabel = this.nodeHelper.rectangle.contains({
+ x: npos.x,
+ y: labely
+ }, pos, width, height)
+
+ return this.nodeHelper.circle.contains(npos, pos, dim) || overLabel
}
}
},
- // Number of iterations for the FD algorithm
- iterations: 200,
- // Edge length
+ edgeSettings: {
+ 'customEdge': {
+ 'render': function(adj, canvas) {
+ map.JIT.edgeRender(adj, canvas)
+ },
+ 'contains': function(adj, pos) {
+ const from = adj.nodeFrom.pos.getc()
+ const to = adj.nodeTo.pos.getc()
+
+ // this fixes an issue where when edges are perfectly horizontal or perfectly vertical
+ // it becomes incredibly difficult to hover over them
+ if (-1 < pos.x && pos.x < 1) pos.x = 0
+ if (-1 < pos.y && pos.y < 1) pos.y = 0
+
+ return $jit.Graph.Plot.edgeHelper.line.contains(from, to, pos, adj.Edge.epsilon + 5)
+ }
+ }
+ }
+ }, // ForceDirected
+ ForceDirected3D: {
+ animate: {
+ modes: ['linear'],
+ // TODO fix tests so we don't need _.get
+ transition: _.get($jit, 'Trans.Elastic.easeOut'),
+ duration: 2500,
+ onComplete: function() {
+ map.Visualize.mGraph.busy = false
+ }
+ },
+ graphSettings: {
+ // id of the visualization container
+ injectInto: 'infovis',
+ type: '3D',
+ Scene: {
+ Lighting: {
+ enable: false,
+ ambient: [0.5, 0.5, 0.5],
+ directional: {
+ direction: {
+ x: 1,
+ y: 0,
+ z: -1
+ },
+ color: [0.9, 0.9, 0.9]
+ }
+ }
+ },
+ // Enable zooming and panning
+ // by scrolling and DnD
+ Navigation: {
+ enable: false,
+ // Enable panning events only if we're dragging the empty
+ // canvas (and not a node).
+ panning: 'avoid nodes',
+ zooming: 10 // zoom speed. higher is more sensible
+ },
+ // Change node and edge styles such as
+ // color and width.
+ // These properties are also set per node
+ // with dollar prefixed data-properties in the
+ // JSON structure.
+ Node: {
+ overridable: true,
+ type: 'sphere',
+ dim: 15,
+ color: '#ffffff'
+ },
+ Edge: {
+ overridable: false,
+ type: 'tube',
+ color: '#111',
+ lineWidth: 3
+ },
+ // Native canvas text styling
+ Label: {
+ type: 'HTML', // Native or HTML
+ size: 10,
+ style: 'bold'
+ },
+ // Add node events
+ Events: {
+ enable: true,
+ type: 'Native',
+ i: 0,
+ onMouseMove: function(node, eventInfo, e) {
+ // if(this.i++ % 3) return
+ const pos = eventInfo.getPos()
+ map.Visualize.cameraPosition.x += (pos.x - map.Visualize.cameraPosition.x) * 0.5
+ map.Visualize.cameraPosition.y += (-pos.y - map.Visualize.cameraPosition.y) * 0.5
+ map.Visualize.mGraph.plot()
+ },
+ onMouseWheel: function(delta) {
+ map.Visualize.cameraPosition.z += -delta * 20
+ map.Visualize.mGraph.plot()
+ },
+ onClick: function() {}
+ },
+ // Number of iterations for the FD algorithm
+ iterations: 200,
+ // Edge length
+ levelDistance: 100
+ },
+ nodeSettings: {
+
+ },
+ edgeSettings: {
+
+ }
+ }, // ForceDirected3D
+ RGraph: {
+ animate: {
+ modes: ['polar'],
+ duration: 800,
+ onComplete: function() {
+ map.Visualize.mGraph.busy = false
+ }
+ },
+ // this will just be used to patch the ForceDirected graphsettings with the few things which actually differ
+ background: {
+ levelDistance: 200,
+ numberOfCircles: 4,
+ CanvasStyles: {
+ strokeStyle: '#333',
+ lineWidth: 1.5
+ }
+ },
levelDistance: 200
},
- nodeSettings: {
- 'customNode': {
- 'render': function(node, canvas) {
- const pos = node.pos.getc(true)
- const dim = node.getData('dim')
- const topic = node.getData('topic')
- const metacode = topic ? topic.getMetacode() : false
- const ctx = canvas.getCtx()
+ onMouseEnter: function(edge) {
+ const filtered = edge.getData('alpha') === 0
- // if the topic is selected draw a circle around it
- if (!canvas.denySelected && node.selected) {
- ctx.beginPath()
- ctx.arc(pos.x, pos.y, dim + 3, 0, 2 * Math.PI, false)
- ctx.strokeStyle = Settings.colors.topics.selected
- ctx.lineWidth = 2
- ctx.stroke()
- }
+ // don't do anything if the edge is filtered
+ // or if the canvas is animating
+ if (filtered || map.Visualize.mGraph.busy) return
- if (!metacode ||
- !metacode.get('image') ||
- !metacode.get('image').complete ||
- (typeof metacode.get('image').naturalWidth !== 'undefined' &&
- metacode.get('image').naturalWidth === 0)) {
- ctx.beginPath()
- ctx.arc(pos.x, pos.y, dim, 0, 2 * Math.PI, false)
- ctx.fillStyle = '#B6B2FD'
- ctx.fill()
- } else {
- ctx.drawImage(metacode.get('image'), pos.x - dim, pos.y - dim, dim * 2, dim * 2)
- }
-
- // if the topic has a link, draw a small image to indicate that
- const hasLink = topic && topic.get('link') !== '' && topic.get('link') !== null
- const linkImage = map.JIT.topicLinkImage
- const linkImageLoaded = linkImage.complete ||
- (typeof linkImage.naturalWidth !== 'undefined' &&
- linkImage.naturalWidth !== 0)
- if (hasLink && linkImageLoaded) {
- ctx.drawImage(linkImage, pos.x - dim - 8, pos.y - dim - 8, 16, 16)
- }
-
- // if the topic has a desc, draw a small image to indicate that
- const hasDesc = topic && topic.get('desc') !== '' && topic.get('desc') !== null
- const descImage = map.JIT.topicDescImage
- const descImageLoaded = descImage.complete ||
- (typeof descImage.naturalWidth !== 'undefined' &&
- descImage.naturalWidth !== 0)
- if (hasDesc && descImageLoaded) {
- ctx.drawImage(descImage, pos.x + dim - 8, pos.y - dim - 8, 16, 16)
- }
- },
- 'contains': function(node, pos) {
- const npos = node.pos.getc(true)
- const dim = node.getData('dim')
- const arrayOfLabelLines = Util.splitLine(node.name, 25).split('\n')
- const ctx = map.Visualize.mGraph.canvas.getCtx()
-
- const height = 25 * arrayOfLabelLines.length
-
- let lineWidths = []
- for (let index = 0; index < arrayOfLabelLines.length; ++index) {
- lineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width)
- }
- const width = Math.max.apply(null, lineWidths) + 8
- const labely = npos.y + node.getData('height') + 5 + height / 2
-
- const overLabel = this.nodeHelper.rectangle.contains({
- x: npos.x,
- y: labely
- }, pos, width, height)
-
- return this.nodeHelper.circle.contains(npos, pos, dim) || overLabel
- }
- }
- },
- edgeSettings: {
- 'customEdge': {
- 'render': function(adj, canvas) {
- map.JIT.edgeRender(adj, canvas)
- },
- 'contains': function(adj, pos) {
- const from = adj.nodeFrom.pos.getc()
- const to = adj.nodeTo.pos.getc()
-
- // this fixes an issue where when edges are perfectly horizontal or perfectly vertical
- // it becomes incredibly difficult to hover over them
- if (-1 < pos.x && pos.x < 1) pos.x = 0
- if (-1 < pos.y && pos.y < 1) pos.y = 0
-
- return $jit.Graph.Plot.edgeHelper.line.contains(from, to, pos, adj.Edge.epsilon + 5)
- }
- }
- }
- }, // ForceDirected
- ForceDirected3D: {
- animate: {
- modes: ['linear'],
- // TODO fix tests so we don't need _.get
- transition: _.get($jit, 'Trans.Elastic.easeOut'),
- duration: 2500,
- onComplete: function() {
- map.Visualize.mGraph.busy = false
- }
- },
- graphSettings: {
- // id of the visualization container
- injectInto: 'infovis',
- type: '3D',
- Scene: {
- Lighting: {
- enable: false,
- ambient: [0.5, 0.5, 0.5],
- directional: {
- direction: {
- x: 1,
- y: 0,
- z: -1
- },
- color: [0.9, 0.9, 0.9]
- }
- }
- },
- // Enable zooming and panning
- // by scrolling and DnD
- Navigation: {
- enable: false,
- // Enable panning events only if we're dragging the empty
- // canvas (and not a node).
- panning: 'avoid nodes',
- zooming: 10 // zoom speed. higher is more sensible
- },
- // Change node and edge styles such as
- // color and width.
- // These properties are also set per node
- // with dollar prefixed data-properties in the
- // JSON structure.
- Node: {
- overridable: true,
- type: 'sphere',
- dim: 15,
- color: '#ffffff'
- },
- Edge: {
- overridable: false,
- type: 'tube',
- color: '#111',
- lineWidth: 3
- },
- // Native canvas text styling
- Label: {
- type: 'HTML', // Native or HTML
- size: 10,
- style: 'bold'
- },
- // Add node events
- Events: {
- enable: true,
- type: 'Native',
- i: 0,
- onMouseMove: function(node, eventInfo, e) {
- // if(this.i++ % 3) return
- const pos = eventInfo.getPos()
- map.Visualize.cameraPosition.x += (pos.x - map.Visualize.cameraPosition.x) * 0.5
- map.Visualize.cameraPosition.y += (-pos.y - map.Visualize.cameraPosition.y) * 0.5
- map.Visualize.mGraph.plot()
- },
- onMouseWheel: function(delta) {
- map.Visualize.cameraPosition.z += -delta * 20
- map.Visualize.mGraph.plot()
- },
- onClick: function() {}
- },
- // Number of iterations for the FD algorithm
- iterations: 200,
- // Edge length
- levelDistance: 100
- },
- nodeSettings: {
-
- },
- edgeSettings: {
-
- }
- }, // ForceDirected3D
- RGraph: {
- animate: {
- modes: ['polar'],
- duration: 800,
- onComplete: function() {
- map.Visualize.mGraph.busy = false
- }
- },
- // this will just be used to patch the ForceDirected graphsettings with the few things which actually differ
- background: {
- levelDistance: 200,
- numberOfCircles: 4,
- CanvasStyles: {
- strokeStyle: '#333',
- lineWidth: 1.5
- }
- },
- levelDistance: 200
- },
- onMouseEnter: function(edge) {
- const filtered = edge.getData('alpha') === 0
-
- // don't do anything if the edge is filtered
- // or if the canvas is animating
- if (filtered || map.Visualize.mGraph.busy) return
-
- $('canvas').css('cursor', 'pointer')
- const edgeIsSelected = map.Selected.Edges.indexOf(edge)
- // following if statement only executes if the edge being hovered over is not selected
- if (edgeIsSelected === -1) {
- edge.setData('showDesc', true, 'current')
- }
-
- edge.setDataset('end', {
- lineWidth: 4
- })
- map.Visualize.mGraph.fx.animate({
- modes: ['edge-property:lineWidth'],
- duration: 100
- })
- map.Visualize.mGraph.plot()
- }, // onMouseEnter
- onMouseLeave: function(edge) {
- if (edge.getData('alpha') === 0) return // don't do anything if the edge is filtered
- $('canvas').css('cursor', 'default')
- const edgeIsSelected = map.Selected.Edges.indexOf(edge)
- // following if statement only executes if the edge being hovered over is not selected
- if (edgeIsSelected === -1) {
- edge.setData('showDesc', false, 'current')
- }
-
- edge.setDataset('end', {
- lineWidth: 2
- })
- map.Visualize.mGraph.fx.animate({
- modes: ['edge-property:lineWidth'],
- duration: 100
- })
- map.Visualize.mGraph.plot()
- }, // onMouseLeave
- onMouseMoveHandler: function(_node, eventInfo, e) {
- const self = JIT
-
- if (map.Visualize.mGraph.busy) return
-
- const node = eventInfo.getNode()
- const edge = eventInfo.getEdge()
-
- // if we're on top of a node object, act like there aren't edges under it
- if (node !== false) {
- if (map.Mouse.edgeHoveringOver) {
- self.onMouseLeave(map.Mouse.edgeHoveringOver)
- }
$('canvas').css('cursor', 'pointer')
- return
- }
+ const edgeIsSelected = map.Selected.Edges.indexOf(edge)
+ // following if statement only executes if the edge being hovered over is not selected
+ if (edgeIsSelected === -1) {
+ edge.setData('showDesc', true, 'current')
+ }
- if (edge === false && map.Mouse.edgeHoveringOver !== false) {
- // mouse not on an edge, but we were on an edge previously
- self.onMouseLeave(map.Mouse.edgeHoveringOver)
- } else if (edge !== false && map.Mouse.edgeHoveringOver === false) {
- // mouse is on an edge, but there isn't a stored edge
- self.onMouseEnter(edge)
- } else if (edge !== false && map.Mouse.edgeHoveringOver !== edge) {
- // mouse is on an edge, but a different edge is stored
- self.onMouseLeave(map.Mouse.edgeHoveringOver)
- self.onMouseEnter(edge)
- }
-
- // could be false
- map.Mouse.edgeHoveringOver = edge
-
- if (!node && !edge) {
+ edge.setDataset('end', {
+ lineWidth: 4
+ })
+ map.Visualize.mGraph.fx.animate({
+ modes: ['edge-property:lineWidth'],
+ duration: 100
+ })
+ map.Visualize.mGraph.plot()
+ }, // onMouseEnter
+ onMouseLeave: function(edge) {
+ if (edge.getData('alpha') === 0) return // don't do anything if the edge is filtered
$('canvas').css('cursor', 'default')
- }
- }, // onMouseMoveHandler
- enterKeyHandler: function() {
- const creatingMap = GlobalUI.lightbox
- if (creatingMap === 'newmap' || creatingMap === 'forkmap') {
- GlobalUI.CreateMap.submit()
- } else if (map.Create.newTopic.beingCreated) {
- Topic.createTopicLocally()
- } else if (map.Create.newSynapse.beingCreated) {
- map.Synapse.createSynapseLocally()
- }
- }, // enterKeyHandler
- escKeyHandler: function() {
- map.Control.deselectAllEdges()
- map.Control.deselectAllNodes()
- }, // escKeyHandler
- onDragMoveTopicHandler: function(node, eventInfo, e) {
- var self = JIT
+ const edgeIsSelected = map.Selected.Edges.indexOf(edge)
+ // following if statement only executes if the edge being hovered over is not selected
+ if (edgeIsSelected === -1) {
+ edge.setData('showDesc', false, 'current')
+ }
- var authorized = map.Active.Map && map.Active.Map.authorizeToEdit(map.Active.Mapper)
+ edge.setDataset('end', {
+ lineWidth: 2
+ })
+ map.Visualize.mGraph.fx.animate({
+ modes: ['edge-property:lineWidth'],
+ duration: 100
+ })
+ map.Visualize.mGraph.plot()
+ }, // onMouseLeave
+ onMouseMoveHandler: function(_node, eventInfo, e) {
+ const self = toExport
- if (node && !node.nodeFrom) {
- self.handleSelectionBeforeDragging(node, e)
+ if (map.Visualize.mGraph.busy) return
- const pos = eventInfo.getPos()
- const EDGE_THICKNESS = 30
- const SHIFT = 2 / map.Visualize.mGraph.canvas.scaleOffsetX
- const PERIOD = 5
+ const node = eventInfo.getNode()
+ const edge = eventInfo.getEdge()
- // self.virtualPointer = pos;
-
- // if it's a left click, or a touch, move the node
- if (e.touches || (e.button === 0 && !e.altKey && !e.ctrlKey && (e.buttons === 0 || e.buttons === 1 || e.buttons === undefined))) {
- const width = map.Visualize.mGraph.canvas.getSize().width
- const height = map.Visualize.mGraph.canvas.getSize().height
- const xPix = Util.coordsToPixels(map.Visualize.mGraph, pos).x
- const yPix = Util.coordsToPixels(map.Visualize.mGraph, pos).y
-
- if (self.dragFlag === 0) {
- self.mouseDownPix = Util.coordsToPixels(map.Visualize.mGraph, eventInfo.getPos())
- self.dragFlag = 1
+ // if we're on top of a node object, act like there aren't edges under it
+ if (node !== false) {
+ if (map.Mouse.edgeHoveringOver) {
+ self.onMouseLeave(map.Mouse.edgeHoveringOver)
}
+ $('canvas').css('cursor', 'pointer')
+ return
+ }
- if (Util.getDistance(Util.coordsToPixels(map.Visualize.mGraph, pos), self.mouseDownPix) > 2 && !self.dragTolerance) {
- self.dragTolerance = 1
- }
+ if (edge === false && map.Mouse.edgeHoveringOver !== false) {
+ // mouse not on an edge, but we were on an edge previously
+ self.onMouseLeave(map.Mouse.edgeHoveringOver)
+ } else if (edge !== false && map.Mouse.edgeHoveringOver === false) {
+ // mouse is on an edge, but there isn't a stored edge
+ self.onMouseEnter(edge)
+ } else if (edge !== false && map.Mouse.edgeHoveringOver !== edge) {
+ // mouse is on an edge, but a different edge is stored
+ self.onMouseLeave(map.Mouse.edgeHoveringOver)
+ self.onMouseEnter(edge)
+ }
- if (xPix < EDGE_THICKNESS && self.dragTolerance) {
- clearInterval(self.dragLeftEdge)
- clearInterval(self.dragRightEdge)
- clearInterval(self.dragTopEdge)
- clearInterval(self.dragBottomEdge)
- self.virtualPointer = { x: Util.pixelsToCoords(map.Visualize.mGraph, { x: EDGE_THICKNESS, y: yPix }).x - SHIFT, y: pos.y }
- map.Visualize.mGraph.canvas.translate(SHIFT, 0)
- self.updateTopicPositions(node, self.virtualPointer)
- map.Visualize.mGraph.plot()
+ // could be false
+ map.Mouse.edgeHoveringOver = edge
- self.dragLeftEdge = setInterval(function() {
+ if (!node && !edge) {
+ $('canvas').css('cursor', 'default')
+ }
+ }, // onMouseMoveHandler
+ enterKeyHandler: function() {
+ const creatingMap = GlobalUI.lightbox
+ if (creatingMap === 'newmap' || creatingMap === 'forkmap') {
+ GlobalUI.CreateMap.submit()
+ } else if (map.Create.newTopic.beingCreated) {
+ Topic.createTopicLocally()
+ } else if (map.Create.newSynapse.beingCreated) {
+ map.Synapse.createSynapseLocally()
+ }
+ }, // enterKeyHandler
+ escKeyHandler: function() {
+ map.Control.deselectAllEdges()
+ map.Control.deselectAllNodes()
+ }, // escKeyHandler
+ onDragMoveTopicHandler: function(node, eventInfo, e) {
+ var self = toExport
+
+ var authorized = map.Active.Map && map.Active.Map.authorizeToEdit(map.Active.Mapper)
+
+ if (node && !node.nodeFrom) {
+ self.handleSelectionBeforeDragging(node, e)
+
+ const pos = eventInfo.getPos()
+ const EDGE_THICKNESS = 30
+ const SHIFT = 2 / map.Visualize.mGraph.canvas.scaleOffsetX
+ const PERIOD = 5
+
+ // self.virtualPointer = pos;
+
+ // if it's a left click, or a touch, move the node
+ if (e.touches || (e.button === 0 && !e.altKey && !e.ctrlKey && (e.buttons === 0 || e.buttons === 1 || e.buttons === undefined))) {
+ const width = map.Visualize.mGraph.canvas.getSize().width
+ const height = map.Visualize.mGraph.canvas.getSize().height
+ const xPix = Util.coordsToPixels(map.Visualize.mGraph, pos).x
+ const yPix = Util.coordsToPixels(map.Visualize.mGraph, pos).y
+
+ if (self.dragFlag === 0) {
+ self.mouseDownPix = Util.coordsToPixels(map.Visualize.mGraph, eventInfo.getPos())
+ self.dragFlag = 1
+ }
+
+ if (Util.getDistance(Util.coordsToPixels(map.Visualize.mGraph, pos), self.mouseDownPix) > 2 && !self.dragTolerance) {
+ self.dragTolerance = 1
+ }
+
+ if (xPix < EDGE_THICKNESS && self.dragTolerance) {
+ clearInterval(self.dragLeftEdge)
+ clearInterval(self.dragRightEdge)
+ clearInterval(self.dragTopEdge)
+ clearInterval(self.dragBottomEdge)
self.virtualPointer = { x: Util.pixelsToCoords(map.Visualize.mGraph, { x: EDGE_THICKNESS, y: yPix }).x - SHIFT, y: pos.y }
map.Visualize.mGraph.canvas.translate(SHIFT, 0)
self.updateTopicPositions(node, self.virtualPointer)
map.Visualize.mGraph.plot()
- }, PERIOD)
- }
- if (width - xPix < EDGE_THICKNESS && self.dragTolerance) {
- clearInterval(self.dragLeftEdge)
- clearInterval(self.dragRightEdge)
- clearInterval(self.dragTopEdge)
- clearInterval(self.dragBottomEdge)
- self.virtualPointer = { x: Util.pixelsToCoords(map.Visualize.mGraph, { x: width - EDGE_THICKNESS, y: yPix }).x + SHIFT, y: pos.y }
- map.Visualize.mGraph.canvas.translate(-SHIFT, 0)
- self.updateTopicPositions(node, self.virtualPointer)
- map.Visualize.mGraph.plot()
- self.dragRightEdge = setInterval(function() {
+ self.dragLeftEdge = setInterval(function() {
+ self.virtualPointer = { x: Util.pixelsToCoords(map.Visualize.mGraph, { x: EDGE_THICKNESS, y: yPix }).x - SHIFT, y: pos.y }
+ map.Visualize.mGraph.canvas.translate(SHIFT, 0)
+ self.updateTopicPositions(node, self.virtualPointer)
+ map.Visualize.mGraph.plot()
+ }, PERIOD)
+ }
+ if (width - xPix < EDGE_THICKNESS && self.dragTolerance) {
+ clearInterval(self.dragLeftEdge)
+ clearInterval(self.dragRightEdge)
+ clearInterval(self.dragTopEdge)
+ clearInterval(self.dragBottomEdge)
self.virtualPointer = { x: Util.pixelsToCoords(map.Visualize.mGraph, { x: width - EDGE_THICKNESS, y: yPix }).x + SHIFT, y: pos.y }
map.Visualize.mGraph.canvas.translate(-SHIFT, 0)
self.updateTopicPositions(node, self.virtualPointer)
map.Visualize.mGraph.plot()
- }, PERIOD)
- }
- if (yPix < EDGE_THICKNESS && self.dragTolerance) {
- clearInterval(self.dragLeftEdge)
- clearInterval(self.dragRightEdge)
- clearInterval(self.dragTopEdge)
- clearInterval(self.dragBottomEdge)
- self.virtualPointer = { x: pos.x, y: Util.pixelsToCoords(map.Visualize.mGraph, { x: xPix, y: EDGE_THICKNESS }).y - SHIFT }
- map.Visualize.mGraph.canvas.translate(0, SHIFT)
- self.updateTopicPositions(node, self.virtualPointer)
- map.Visualize.mGraph.plot()
- self.dragTopEdge = setInterval(function() {
+ self.dragRightEdge = setInterval(function() {
+ self.virtualPointer = { x: Util.pixelsToCoords(map.Visualize.mGraph, { x: width - EDGE_THICKNESS, y: yPix }).x + SHIFT, y: pos.y }
+ map.Visualize.mGraph.canvas.translate(-SHIFT, 0)
+ self.updateTopicPositions(node, self.virtualPointer)
+ map.Visualize.mGraph.plot()
+ }, PERIOD)
+ }
+ if (yPix < EDGE_THICKNESS && self.dragTolerance) {
+ clearInterval(self.dragLeftEdge)
+ clearInterval(self.dragRightEdge)
+ clearInterval(self.dragTopEdge)
+ clearInterval(self.dragBottomEdge)
self.virtualPointer = { x: pos.x, y: Util.pixelsToCoords(map.Visualize.mGraph, { x: xPix, y: EDGE_THICKNESS }).y - SHIFT }
map.Visualize.mGraph.canvas.translate(0, SHIFT)
self.updateTopicPositions(node, self.virtualPointer)
map.Visualize.mGraph.plot()
- }, PERIOD)
- }
- if (height - yPix < EDGE_THICKNESS && self.dragTolerance) {
- clearInterval(self.dragLeftEdge)
- clearInterval(self.dragRightEdge)
- clearInterval(self.dragTopEdge)
- clearInterval(self.dragBottomEdge)
- self.virtualPointer = { x: pos.x, y: Util.pixelsToCoords(map.Visualize.mGraph, { x: xPix, y: height - EDGE_THICKNESS }).y + SHIFT }
- map.Visualize.mGraph.canvas.translate(0, -SHIFT)
- self.updateTopicPositions(node, self.virtualPointer)
- map.Visualize.mGraph.plot()
- self.dragBottomEdge = setInterval(function() {
+ self.dragTopEdge = setInterval(function() {
+ self.virtualPointer = { x: pos.x, y: Util.pixelsToCoords(map.Visualize.mGraph, { x: xPix, y: EDGE_THICKNESS }).y - SHIFT }
+ map.Visualize.mGraph.canvas.translate(0, SHIFT)
+ self.updateTopicPositions(node, self.virtualPointer)
+ map.Visualize.mGraph.plot()
+ }, PERIOD)
+ }
+ if (height - yPix < EDGE_THICKNESS && self.dragTolerance) {
+ clearInterval(self.dragLeftEdge)
+ clearInterval(self.dragRightEdge)
+ clearInterval(self.dragTopEdge)
+ clearInterval(self.dragBottomEdge)
self.virtualPointer = { x: pos.x, y: Util.pixelsToCoords(map.Visualize.mGraph, { x: xPix, y: height - EDGE_THICKNESS }).y + SHIFT }
map.Visualize.mGraph.canvas.translate(0, -SHIFT)
self.updateTopicPositions(node, self.virtualPointer)
map.Visualize.mGraph.plot()
- }, PERIOD)
- }
- if (xPix >= EDGE_THICKNESS && width - xPix >= EDGE_THICKNESS && yPix >= EDGE_THICKNESS && height - yPix >= EDGE_THICKNESS) {
- clearInterval(self.dragLeftEdge)
- clearInterval(self.dragRightEdge)
- clearInterval(self.dragTopEdge)
- clearInterval(self.dragBottomEdge)
+ self.dragBottomEdge = setInterval(function() {
+ self.virtualPointer = { x: pos.x, y: Util.pixelsToCoords(map.Visualize.mGraph, { x: xPix, y: height - EDGE_THICKNESS }).y + SHIFT }
+ map.Visualize.mGraph.canvas.translate(0, -SHIFT)
+ self.updateTopicPositions(node, self.virtualPointer)
+ map.Visualize.mGraph.plot()
+ }, PERIOD)
+ }
- self.updateTopicPositions(node, pos)
- map.Visualize.mGraph.plot()
- }
- } else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && authorized) {
- // if it's a right click or holding down alt, start synapse creation ->third option is for firefox
- if (map.JIT.tempInit === false) {
- map.JIT.tempNode = node
- map.JIT.tempInit = true
+ if (xPix >= EDGE_THICKNESS && width - xPix >= EDGE_THICKNESS && yPix >= EDGE_THICKNESS && height - yPix >= EDGE_THICKNESS) {
+ clearInterval(self.dragLeftEdge)
+ clearInterval(self.dragRightEdge)
+ clearInterval(self.dragTopEdge)
+ clearInterval(self.dragBottomEdge)
- map.Create.newTopic.hide()
- map.Create.newSynapse.hide()
- // set the draw synapse start positions
- var l = map.Selected.Nodes.length
- if (l > 0) {
- for (let i = l - 1; i >= 0; i -= 1) {
- const n = map.Selected.Nodes[i]
- map.Mouse.synapseStartCoordinates.push({
- x: n.pos.getc().x,
- y: n.pos.getc().y
- })
+ self.updateTopicPositions(node, pos)
+ map.Visualize.mGraph.plot()
+ }
+ } else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && authorized) {
+ // if it's a right click or holding down alt, start synapse creation ->third option is for firefox
+ if (map.JIT.tempInit === false) {
+ map.JIT.tempNode = node
+ map.JIT.tempInit = true
+
+ map.Create.newTopic.hide()
+ map.Create.newSynapse.hide()
+ // set the draw synapse start positions
+ var l = map.Selected.Nodes.length
+ if (l > 0) {
+ for (let i = l - 1; i >= 0; i -= 1) {
+ const n = map.Selected.Nodes[i]
+ map.Mouse.synapseStartCoordinates.push({
+ x: n.pos.getc().x,
+ y: n.pos.getc().y
+ })
+ }
+ } else {
+ map.Mouse.synapseStartCoordinates = [{
+ x: map.JIT.tempNode.pos.getc().x,
+ y: map.JIT.tempNode.pos.getc().y
+ }]
+ }
+ map.Mouse.synapseEndCoordinates = {
+ x: pos.x,
+ y: pos.y
}
- } else {
- map.Mouse.synapseStartCoordinates = [{
- x: map.JIT.tempNode.pos.getc().x,
- y: map.JIT.tempNode.pos.getc().y
- }]
}
- map.Mouse.synapseEndCoordinates = {
- x: pos.x,
- y: pos.y
+ //
+ let temp = eventInfo.getNode()
+ if (temp !== false && temp.id !== node.id && map.Selected.Nodes.indexOf(temp) === -1) { // this means a Node has been returned
+ map.JIT.tempNode2 = temp
+
+ map.Mouse.synapseEndCoordinates = {
+ x: map.JIT.tempNode2.pos.getc().x,
+ y: map.JIT.tempNode2.pos.getc().y
+ }
+
+ // before making the highlighted one bigger, make sure all the others are regular size
+ map.Visualize.mGraph.graph.eachNode(function(n) {
+ n.setData('dim', 25, 'current')
+ })
+ temp.setData('dim', 35, 'current')
+ map.Visualize.mGraph.plot()
+ } else if (!temp) {
+ map.JIT.tempNode2 = null
+ map.Visualize.mGraph.graph.eachNode(function(n) {
+ n.setData('dim', 25, 'current')
+ })
+ // pop up node creation :)
+ var myX = e.clientX - 110
+ var myY = e.clientY - 30
+ $('#new_topic').css('left', myX + 'px')
+ $('#new_topic').css('top', myY + 'px')
+ map.Create.newTopic.x = eventInfo.getPos().x
+ map.Create.newTopic.y = eventInfo.getPos().y
+ map.Visualize.mGraph.plot()
+
+ map.Mouse.synapseEndCoordinates = {
+ x: pos.x,
+ y: pos.y
+ }
}
+ } else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && map.Active.Topic) {
+ GlobalUI.notifyUser('Cannot create in Topic view.')
+ } else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && !authorized) {
+ GlobalUI.notifyUser('Cannot edit this map.')
}
- //
- let temp = eventInfo.getNode()
- if (temp !== false && temp.id !== node.id && map.Selected.Nodes.indexOf(temp) === -1) { // this means a Node has been returned
- map.JIT.tempNode2 = temp
-
- map.Mouse.synapseEndCoordinates = {
- x: map.JIT.tempNode2.pos.getc().x,
- y: map.JIT.tempNode2.pos.getc().y
- }
-
- // before making the highlighted one bigger, make sure all the others are regular size
- map.Visualize.mGraph.graph.eachNode(function(n) {
- n.setData('dim', 25, 'current')
- })
- temp.setData('dim', 35, 'current')
- map.Visualize.mGraph.plot()
- } else if (!temp) {
- map.JIT.tempNode2 = null
- map.Visualize.mGraph.graph.eachNode(function(n) {
- n.setData('dim', 25, 'current')
- })
- // pop up node creation :)
- var myX = e.clientX - 110
- var myY = e.clientY - 30
- $('#new_topic').css('left', myX + 'px')
- $('#new_topic').css('top', myY + 'px')
- map.Create.newTopic.x = eventInfo.getPos().x
- map.Create.newTopic.y = eventInfo.getPos().y
- map.Visualize.mGraph.plot()
-
- map.Mouse.synapseEndCoordinates = {
- x: pos.x,
- y: pos.y
- }
- }
- } else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && map.Active.Topic) {
- GlobalUI.notifyUser('Cannot create in Topic view.')
- } else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && !authorized) {
- GlobalUI.notifyUser('Cannot edit this map.')
}
- }
- }, // onDragMoveTopicHandler
- onDragCancelHandler: function(node, eventInfo, e) {
- map.JIT.tempNode = null
- if (map.JIT.tempNode2) map.JIT.tempNode2.setData('dim', 25, 'current')
- map.JIT.tempNode2 = null
- map.JIT.tempInit = false
- // reset the draw synapse positions to false
- map.Mouse.synapseStartCoordinates = []
- map.Mouse.synapseEndCoordinates = null
- map.Visualize.mGraph.plot()
- }, // onDragCancelHandler
- onDragEndTopicHandler: function(node, eventInfo, e) {
- const self = JIT
- const midpoint = {}
- let pixelPos
- let mapping
-
- clearInterval(self.dragLeftEdge)
- clearInterval(self.dragRightEdge)
- clearInterval(self.dragTopEdge)
- clearInterval(self.dragBottomEdge)
-
- delete self.dragLeftEdge
- delete self.dragRightEdge
- delete self.dragTopEdge
- delete self.dragBottomEdge
-
- self.dragFlag = 0
- self.dragTolerance = 0
-
- if (map.JIT.tempInit && map.JIT.tempNode2 === null) {
- // this means you want to add a new topic, and then a synapse
- map.Create.newTopic.addSynapse = true
- map.Create.newTopic.open()
- } else if (map.JIT.tempInit && map.JIT.tempNode2 !== null) {
- // this means you want to create a synapse between two existing topics
- map.Create.newTopic.addSynapse = false
- map.Create.newSynapse.topic1id = map.JIT.tempNode.getData('topic').id
- map.Create.newSynapse.topic2id = map.JIT.tempNode2.getData('topic').id
- map.JIT.tempNode2.setData('dim', 25, 'current')
- map.Visualize.mGraph.plot()
- midpoint.x = map.JIT.tempNode.pos.getc().x + (map.JIT.tempNode2.pos.getc().x - map.JIT.tempNode.pos.getc().x) / 2
- midpoint.y = map.JIT.tempNode.pos.getc().y + (map.JIT.tempNode2.pos.getc().y - map.JIT.tempNode.pos.getc().y) / 2
- pixelPos = Util.coordsToPixels(map.Visualize.mGraph, midpoint)
- $('#new_synapse').css('left', pixelPos.x + 'px')
- $('#new_synapse').css('top', pixelPos.y + 'px')
- map.Create.newSynapse.open()
+ }, // onDragMoveTopicHandler
+ onDragCancelHandler: function(node, eventInfo, e) {
map.JIT.tempNode = null
+ if (map.JIT.tempNode2) map.JIT.tempNode2.setData('dim', 25, 'current')
map.JIT.tempNode2 = null
map.JIT.tempInit = false
- } else if (!map.JIT.tempInit && node && !node.nodeFrom) {
- // this means you dragged an existing node, autosave that to the database
-
- // check whether to save mappings
- const checkWhetherToSave = function() {
- const map = map.Active.Map
- if (!map) return false
- return map.authorizeToEdit(map.Active.Mapper)
- }
-
- if (checkWhetherToSave()) {
- if (map.Active.Mapper.get('follow_map_on_contributed')) {
- map.Active.Mapper.followMap(map.Active.Map.id)
- }
- mapping = node.getData('mapping')
- mapping.save({
- xloc: node.getPos().x,
- yloc: node.getPos().y
- })
- // also save any other selected nodes that also got dragged along
- const l = map.Selected.Nodes.length
- for (var i = l - 1; i >= 0; i -= 1) {
- const n = map.Selected.Nodes[i]
- if (n !== node) {
- mapping = n.getData('mapping')
- mapping.save({
- xloc: n.getPos().x,
- yloc: n.getPos().y
- })
- }
- }
- }
- }
- }, // onDragEndTopicHandler
- canvasClickHandler: function(canvasLoc, e) {
- // grab the location and timestamp of the click
- const storedTime = map.Mouse.lastCanvasClick
- const now = Date.now() // not compatible with IE8 FYI
- map.Mouse.lastCanvasClick = now
-
- const authorized = map.Active.Map && map.Active.Map.authorizeToEdit(map.Active.Mapper)
-
- if (now - storedTime < map.Mouse.DOUBLE_CLICK_TOLERANCE && !map.Mouse.didPan) {
- if (map.Active.Map && !authorized) {
- GlobalUI.notifyUser('Cannot edit Public map.')
- return
- } else if (map.Active.Topic) {
- GlobalUI.notifyUser('Cannot create in Topic view.')
- return
- }
- // DOUBLE CLICK
- // pop up node creation :)
- map.Create.newTopic.addSynapse = false
- map.Create.newTopic.x = canvasLoc.x
- map.Create.newTopic.y = canvasLoc.y
- $('#new_topic').css('left', e.clientX + 'px')
- $('#new_topic').css('top', e.clientY + 'px')
- map.Create.newTopic.open()
- } else if (!map.Mouse.didPan) {
- // SINGLE CLICK, no pan
- map.TopicCard.hideCard()
- map.SynapseCard.hideCard()
- map.Create.newTopic.hide()
- $('.rightclickmenu').remove()
// reset the draw synapse positions to false
map.Mouse.synapseStartCoordinates = []
map.Mouse.synapseEndCoordinates = null
- map.JIT.tempInit = false
- map.JIT.tempNode = null
- map.JIT.tempNode2 = null
- if (!e.ctrlKey && !e.shiftKey) {
- map.Control.deselectAllEdges()
- map.Control.deselectAllNodes()
+ map.Visualize.mGraph.plot()
+ }, // onDragCancelHandler
+ onDragEndTopicHandler: function(node, eventInfo, e) {
+ const self = toExport
+ const midpoint = {}
+ let pixelPos
+ let mapping
+
+ clearInterval(self.dragLeftEdge)
+ clearInterval(self.dragRightEdge)
+ clearInterval(self.dragTopEdge)
+ clearInterval(self.dragBottomEdge)
+
+ delete self.dragLeftEdge
+ delete self.dragRightEdge
+ delete self.dragTopEdge
+ delete self.dragBottomEdge
+
+ self.dragFlag = 0
+ self.dragTolerance = 0
+
+ if (map.JIT.tempInit && map.JIT.tempNode2 === null) {
+ // this means you want to add a new topic, and then a synapse
+ map.Create.newTopic.addSynapse = true
+ map.Create.newTopic.open()
+ } else if (map.JIT.tempInit && map.JIT.tempNode2 !== null) {
+ // this means you want to create a synapse between two existing topics
+ map.Create.newTopic.addSynapse = false
+ map.Create.newSynapse.topic1id = map.JIT.tempNode.getData('topic').id
+ map.Create.newSynapse.topic2id = map.JIT.tempNode2.getData('topic').id
+ map.JIT.tempNode2.setData('dim', 25, 'current')
+ map.Visualize.mGraph.plot()
+ midpoint.x = map.JIT.tempNode.pos.getc().x + (map.JIT.tempNode2.pos.getc().x - map.JIT.tempNode.pos.getc().x) / 2
+ midpoint.y = map.JIT.tempNode.pos.getc().y + (map.JIT.tempNode2.pos.getc().y - map.JIT.tempNode.pos.getc().y) / 2
+ pixelPos = Util.coordsToPixels(map.Visualize.mGraph, midpoint)
+ $('#new_synapse').css('left', pixelPos.x + 'px')
+ $('#new_synapse').css('top', pixelPos.y + 'px')
+ map.Create.newSynapse.open()
+ map.JIT.tempNode = null
+ map.JIT.tempNode2 = null
+ map.JIT.tempInit = false
+ } else if (!map.JIT.tempInit && node && !node.nodeFrom) {
+ // this means you dragged an existing node, autosave that to the database
+
+ // check whether to save mappings
+ const checkWhetherToSave = function() {
+ const map = map.Active.Map
+ if (!map) return false
+ return map.authorizeToEdit(map.Active.Mapper)
+ }
+
+ if (checkWhetherToSave()) {
+ if (map.Active.Mapper.get('follow_map_on_contributed')) {
+ map.Active.Mapper.followMap(map.Active.Map.id)
+ }
+ mapping = node.getData('mapping')
+ mapping.save({
+ xloc: node.getPos().x,
+ yloc: node.getPos().y
+ })
+ // also save any other selected nodes that also got dragged along
+ const l = map.Selected.Nodes.length
+ for (var i = l - 1; i >= 0; i -= 1) {
+ const n = map.Selected.Nodes[i]
+ if (n !== node) {
+ mapping = n.getData('mapping')
+ mapping.save({
+ xloc: n.getPos().x,
+ yloc: n.getPos().y
+ })
+ }
+ }
+ }
}
- } else {
- // SINGLE CLICK, resulting from pan
- map.Create.newTopic.hide()
- }
- }, // canvasClickHandler
- updateTopicPositions: function(node, pos) {
- const len = map.Selected.Nodes.length
- // this is used to send nodes that are moving to
- // other realtime collaborators on the same map
- const positionsToSend = {}
+ }, // onDragEndTopicHandler
+ canvasClickHandler: function(canvasLoc, e) {
+ // grab the location and timestamp of the click
+ const storedTime = map.Mouse.lastCanvasClick
+ const now = Date.now() // not compatible with IE8 FYI
+ map.Mouse.lastCanvasClick = now
- // first define offset for each node
- var xOffset = []
- var yOffset = []
- for (let i = 0; i < len; i += 1) {
- const n = map.Selected.Nodes[i]
- xOffset[i] = n.pos.getc().x - node.pos.getc().x
- yOffset[i] = n.pos.getc().y - node.pos.getc().y
- } // for
+ const authorized = map.Active.Map && map.Active.Map.authorizeToEdit(map.Active.Mapper)
- for (let i = 0; i < len; i += 1) {
- const n = map.Selected.Nodes[i]
- const x = pos.x + xOffset[i]
- const y = pos.y + yOffset[i]
- if (n.pos.rho || n.pos.rho === 0) {
- // this means we're in topic view
- const rho = Math.sqrt(x * x + y * y)
- const theta = Math.atan2(y, x)
- n.pos.setp(theta, rho)
+ if (now - storedTime < map.Mouse.DOUBLE_CLICK_TOLERANCE && !map.Mouse.didPan) {
+ if (map.Active.Map && !authorized) {
+ GlobalUI.notifyUser('Cannot edit Public map.')
+ return
+ } else if (map.Active.Topic) {
+ GlobalUI.notifyUser('Cannot create in Topic view.')
+ return
+ }
+ // DOUBLE CLICK
+ // pop up node creation :)
+ map.Create.newTopic.addSynapse = false
+ map.Create.newTopic.x = canvasLoc.x
+ map.Create.newTopic.y = canvasLoc.y
+ $('#new_topic').css('left', e.clientX + 'px')
+ $('#new_topic').css('top', e.clientY + 'px')
+ map.Create.newTopic.open()
+ } else if (!map.Mouse.didPan) {
+ // SINGLE CLICK, no pan
+ map.TopicCard.hideCard()
+ map.SynapseCard.hideCard()
+ map.Create.newTopic.hide()
+ $('.rightclickmenu').remove()
+ // reset the draw synapse positions to false
+ map.Mouse.synapseStartCoordinates = []
+ map.Mouse.synapseEndCoordinates = null
+ map.JIT.tempInit = false
+ map.JIT.tempNode = null
+ map.JIT.tempNode2 = null
+ if (!e.ctrlKey && !e.shiftKey) {
+ map.Control.deselectAllEdges()
+ map.Control.deselectAllNodes()
+ }
} else {
- n.pos.setc(x, y)
+ // SINGLE CLICK, resulting from pan
+ map.Create.newTopic.hide()
}
+ }, // canvasClickHandler
+ updateTopicPositions: function(node, pos) {
+ const len = map.Selected.Nodes.length
+ // this is used to send nodes that are moving to
+ // other realtime collaborators on the same map
+ const positionsToSend = {}
+
+ // first define offset for each node
+ var xOffset = []
+ var yOffset = []
+ for (let i = 0; i < len; i += 1) {
+ const n = map.Selected.Nodes[i]
+ xOffset[i] = n.pos.getc().x - node.pos.getc().x
+ yOffset[i] = n.pos.getc().y - node.pos.getc().y
+ } // for
+
+ for (let i = 0; i < len; i += 1) {
+ const n = map.Selected.Nodes[i]
+ const x = pos.x + xOffset[i]
+ const y = pos.y + yOffset[i]
+ if (n.pos.rho || n.pos.rho === 0) {
+ // this means we're in topic view
+ const rho = Math.sqrt(x * x + y * y)
+ const theta = Math.atan2(y, x)
+ n.pos.setp(theta, rho)
+ } else {
+ n.pos.setc(x, y)
+ }
+
+ if (map.Active.Map) {
+ const topic = n.getData('topic')
+ // we use the topic ID not the node id
+ // because we can't depend on the node id
+ // to be the same as on other collaborators
+ // maps
+ positionsToSend[topic.id] = n.pos
+ }
+ } // for
if (map.Active.Map) {
- const topic = n.getData('topic')
- // we use the topic ID not the node id
- // because we can't depend on the node id
- // to be the same as on other collaborators
- // maps
- positionsToSend[topic.id] = n.pos
+ $(document).trigger(JIT.events.topicDrag, [positionsToSend])
}
- } // for
+ },
- if (map.Active.Map) {
- $(document).trigger(map.JIT.events.topicDrag, [positionsToSend])
- }
- },
+ nodeDoubleClickHandler: function(node, e) {
+ map.TopicCard.showCard(node)
+ }, // nodeDoubleClickHandler
+ edgeDoubleClickHandler: function(adj, e) {
+ map.SynapseCard.showCard(adj, e)
+ }, // nodeDoubleClickHandler
+ nodeWasDoubleClicked: function() {
+ // grab the timestamp of the click
+ const storedTime = map.Mouse.lastNodeClick
+ const now = Date.now() // not compatible with IE8 FYI
+ map.Mouse.lastNodeClick = now
- nodeDoubleClickHandler: function(node, e) {
- map.TopicCard.showCard(node)
- }, // nodeDoubleClickHandler
- edgeDoubleClickHandler: function(adj, e) {
- map.SynapseCard.showCard(adj, e)
- }, // nodeDoubleClickHandler
- nodeWasDoubleClicked: function() {
- // grab the timestamp of the click
- const storedTime = map.Mouse.lastNodeClick
- const now = Date.now() // not compatible with IE8 FYI
- map.Mouse.lastNodeClick = now
-
- if (now - storedTime < map.Mouse.DOUBLE_CLICK_TOLERANCE) {
- return true
- } else {
- return false
- }
- }, // nodeWasDoubleClicked
- handleSelectionBeforeDragging: function(node, e) {
- if (map.Selected.Nodes.length === 0) {
- map.Control.selectNode(node, e)
- }
- if (map.Selected.Nodes.indexOf(node) === -1) {
- if (e.shiftKey) {
- map.Control.selectNode(node, e)
+ if (now - storedTime < map.Mouse.DOUBLE_CLICK_TOLERANCE) {
+ return true
} else {
- map.Control.deselectAllEdges()
- map.Control.deselectAllNodes()
+ return false
+ }
+ }, // nodeWasDoubleClicked
+ handleSelectionBeforeDragging: function(node, e) {
+ if (map.Selected.Nodes.length === 0) {
map.Control.selectNode(node, e)
}
- }
- }, // handleSelectionBeforeDragging
- getNodeXY: function(node) {
- if (typeof node.pos.x === 'number' && typeof node.pos.y === 'number') {
- return node.pos
- } else if (typeof node.pos.theta === 'number' && typeof node.pos.rho === 'number') {
- return new $jit.Polar(node.pos.theta, node.pos.rho).getc(true)
- } else {
- console.error('getNodeXY: unrecognized node pos format')
- return {}
- }
- },
- selectWithBox: function(e) {
- const self = this
- let sX = map.Mouse.boxStartCoordinates.x
- let sY = map.Mouse.boxStartCoordinates.y
- let eX = map.Mouse.boxEndCoordinates.x
- let eY = map.Mouse.boxEndCoordinates.y
-
- if (!e.shiftKey) {
- map.Control.deselectAllNodes()
- map.Control.deselectAllEdges()
- }
-
- // select all nodes that are within the box
- map.Visualize.mGraph.graph.eachNode(function(n) {
- const pos = self.getNodeXY(n)
- const x = pos.x
- const y = pos.y
-
- // depending on which way the person dragged the box, check that
- // x and y are between the start and end values of the box
- if ((sX < x && x < eX && sY < y && y < eY) ||
- (sX > x && x > eX && sY > y && y > eY) ||
- (sX > x && x > eX && sY < y && y < eY) ||
- (sX < x && x < eX && sY > y && y > eY)) {
+ if (map.Selected.Nodes.indexOf(node) === -1) {
if (e.shiftKey) {
- if (n.selected) {
- map.Control.deselectNode(n)
+ map.Control.selectNode(node, e)
+ } else {
+ map.Control.deselectAllEdges()
+ map.Control.deselectAllNodes()
+ map.Control.selectNode(node, e)
+ }
+ }
+ }, // handleSelectionBeforeDragging
+ getNodeXY: function(node) {
+ if (typeof node.pos.x === 'number' && typeof node.pos.y === 'number') {
+ return node.pos
+ } else if (typeof node.pos.theta === 'number' && typeof node.pos.rho === 'number') {
+ return new $jit.Polar(node.pos.theta, node.pos.rho).getc(true)
+ } else {
+ console.error('getNodeXY: unrecognized node pos format')
+ return {}
+ }
+ },
+ selectWithBox: function(e) {
+ const self = this
+ let sX = map.Mouse.boxStartCoordinates.x
+ let sY = map.Mouse.boxStartCoordinates.y
+ let eX = map.Mouse.boxEndCoordinates.x
+ let eY = map.Mouse.boxEndCoordinates.y
+
+ if (!e.shiftKey) {
+ map.Control.deselectAllNodes()
+ map.Control.deselectAllEdges()
+ }
+
+ // select all nodes that are within the box
+ map.Visualize.mGraph.graph.eachNode(function(n) {
+ const pos = self.getNodeXY(n)
+ const x = pos.x
+ const y = pos.y
+
+ // depending on which way the person dragged the box, check that
+ // x and y are between the start and end values of the box
+ if ((sX < x && x < eX && sY < y && y < eY) ||
+ (sX > x && x > eX && sY > y && y > eY) ||
+ (sX > x && x > eX && sY < y && y < eY) ||
+ (sX < x && x < eX && sY > y && y > eY)) {
+ if (e.shiftKey) {
+ if (n.selected) {
+ map.Control.deselectNode(n)
+ } else {
+ map.Control.selectNode(n, e)
+ }
} else {
map.Control.selectNode(n, e)
}
- } else {
- map.Control.selectNode(n, e)
}
- }
- })
-
- // Convert selection box coordinates to traditional coordinates (+,+) in upper right
- sY = -1 * sY
- eY = -1 * eY
-
- const edgesToToggle = []
- map.DataModel.Synapses.each(function(synapse) {
- const e = synapse.get('edge')
- if (edgesToToggle.indexOf(e) === -1) {
- edgesToToggle.push(e)
- }
- })
- edgesToToggle.forEach(function(edge) {
- const fromNodePos = self.getNodeXY(edge.nodeFrom)
- const fromNodeX = fromNodePos.x
- const fromNodeY = -1 * fromNodePos.y
- const toNodePos = self.getNodeXY(edge.nodeTo)
- const toNodeX = toNodePos.x
- const toNodeY = -1 * toNodePos.y
-
- let maxX = fromNodeX
- let maxY = fromNodeY
- let minX = fromNodeX
- let minY = fromNodeY
-
- // Correct maxX, MaxY values
- ;(toNodeX > maxX) ? (maxX = toNodeX) : (minX = toNodeX)
- ;(toNodeY > maxY) ? (maxY = toNodeY) : (minY = toNodeY)
-
- let maxBoxX = sX
- let maxBoxY = sY
- let minBoxX = sX
- let minBoxY = sY
-
- // Correct maxBoxX, maxBoxY values
- ;(eX > maxBoxX) ? (maxBoxX = eX) : (minBoxX = eX)
- ;(eY > maxBoxY) ? (maxBoxY = eY) : (minBoxY = eY)
-
- // Find the slopes from the synapse fromNode to the 4 corners of the selection box
- const slopes = []
- slopes.push((sY - fromNodeY) / (sX - fromNodeX))
- slopes.push((sY - fromNodeY) / (eX - fromNodeX))
- slopes.push((eY - fromNodeY) / (eX - fromNodeX))
- slopes.push((eY - fromNodeY) / (sX - fromNodeX))
-
- let minSlope = slopes[0]
- let maxSlope = slopes[0]
- slopes.forEach(function(entry) {
- if (entry > maxSlope) maxSlope = entry
- if (entry < minSlope) minSlope = entry
})
- // Find synapse-in-question's slope
- const synSlope = (toNodeY - fromNodeY) / (toNodeX - fromNodeX)
- const b = fromNodeY - synSlope * fromNodeX
+ // Convert selection box coordinates to traditional coordinates (+,+) in upper right
+ sY = -1 * sY
+ eY = -1 * eY
- // Use the selection box edges as test cases for synapse intersection
- let testX = sX
- let testY = synSlope * testX + b
+ const edgesToToggle = []
+ map.DataModel.Synapses.each(function(synapse) {
+ const e = synapse.get('edge')
+ if (edgesToToggle.indexOf(e) === -1) {
+ edgesToToggle.push(e)
+ }
+ })
+ edgesToToggle.forEach(function(edge) {
+ const fromNodePos = self.getNodeXY(edge.nodeFrom)
+ const fromNodeX = fromNodePos.x
+ const fromNodeY = -1 * fromNodePos.y
+ const toNodePos = self.getNodeXY(edge.nodeTo)
+ const toNodeX = toNodePos.x
+ const toNodeY = -1 * toNodePos.y
- let selectTest
+ let maxX = fromNodeX
+ let maxY = fromNodeY
+ let minX = fromNodeX
+ let minY = fromNodeY
- if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testY >= minBoxY && testY <= maxBoxY) {
- selectTest = true
- }
+ // Correct maxX, MaxY values
+ ;(toNodeX > maxX) ? (maxX = toNodeX) : (minX = toNodeX)
+ ;(toNodeY > maxY) ? (maxY = toNodeY) : (minY = toNodeY)
- testX = eX
- testY = synSlope * testX + b
+ let maxBoxX = sX
+ let maxBoxY = sY
+ let minBoxX = sX
+ let minBoxY = sY
- if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testY >= minBoxY && testY <= maxBoxY) {
- selectTest = true
- }
+ // Correct maxBoxX, maxBoxY values
+ ;(eX > maxBoxX) ? (maxBoxX = eX) : (minBoxX = eX)
+ ;(eY > maxBoxY) ? (maxBoxY = eY) : (minBoxY = eY)
- testY = sY
- testX = (testY - b) / synSlope
+ // Find the slopes from the synapse fromNode to the 4 corners of the selection box
+ const slopes = []
+ slopes.push((sY - fromNodeY) / (sX - fromNodeX))
+ slopes.push((sY - fromNodeY) / (eX - fromNodeX))
+ slopes.push((eY - fromNodeY) / (eX - fromNodeX))
+ slopes.push((eY - fromNodeY) / (sX - fromNodeX))
- if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testX >= minBoxX && testX <= maxBoxX) {
- selectTest = true
- }
+ let minSlope = slopes[0]
+ let maxSlope = slopes[0]
+ slopes.forEach(function(entry) {
+ if (entry > maxSlope) maxSlope = entry
+ if (entry < minSlope) minSlope = entry
+ })
- testY = eY
- testX = (testY - b) / synSlope
+ // Find synapse-in-question's slope
+ const synSlope = (toNodeY - fromNodeY) / (toNodeX - fromNodeX)
+ const b = fromNodeY - synSlope * fromNodeX
- if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testX >= minBoxX && testX <= maxBoxX) {
- selectTest = true
- }
+ // Use the selection box edges as test cases for synapse intersection
+ let testX = sX
+ let testY = synSlope * testX + b
- // Case where the synapse is wholly enclosed in the seldction box
- if (fromNodeX >= minBoxX && fromNodeX <= maxBoxX && fromNodeY >= minBoxY && fromNodeY <= maxBoxY && toNodeX >= minBoxX && toNodeX <= maxBoxX && toNodeY >= minBoxY && toNodeY <= maxBoxY) {
- selectTest = true
- }
+ let selectTest
- // The test synapse was selected!
+ if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testY >= minBoxY && testY <= maxBoxY) {
+ selectTest = true
+ }
- if (selectTest) {
- // shiftKey = toggleSelect, otherwise
- if (e.shiftKey) {
- if (map.Selected.Edges.indexOf(edge) !== -1) {
- map.Control.deselectEdge(edge)
+ testX = eX
+ testY = synSlope * testX + b
+
+ if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testY >= minBoxY && testY <= maxBoxY) {
+ selectTest = true
+ }
+
+ testY = sY
+ testX = (testY - b) / synSlope
+
+ if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testX >= minBoxX && testX <= maxBoxX) {
+ selectTest = true
+ }
+
+ testY = eY
+ testX = (testY - b) / synSlope
+
+ if (testX >= minX && testX <= maxX && testY >= minY && testY <= maxY && testX >= minBoxX && testX <= maxBoxX) {
+ selectTest = true
+ }
+
+ // Case where the synapse is wholly enclosed in the seldction box
+ if (fromNodeX >= minBoxX && fromNodeX <= maxBoxX && fromNodeY >= minBoxY && fromNodeY <= maxBoxY && toNodeX >= minBoxX && toNodeX <= maxBoxX && toNodeY >= minBoxY && toNodeY <= maxBoxY) {
+ selectTest = true
+ }
+
+ // The test synapse was selected!
+
+ if (selectTest) {
+ // shiftKey = toggleSelect, otherwise
+ if (e.shiftKey) {
+ if (map.Selected.Edges.indexOf(edge) !== -1) {
+ map.Control.deselectEdge(edge)
+ } else {
+ map.Control.selectEdge(edge)
+ }
} else {
map.Control.selectEdge(edge)
}
- } else {
- map.Control.selectEdge(edge)
}
+ })
+ map.Mouse.boxStartCoordinates = false
+ map.Mouse.boxEndCoordinates = false
+ map.Visualize.mGraph.plot()
+ }, // selectWithBox
+ drawSelectBox: function(eventInfo, e) {
+ const ctx = map.Visualize.mGraph.canvas.getCtx()
+
+ const startX = map.Mouse.boxStartCoordinates.x
+ const startY = map.Mouse.boxStartCoordinates.y
+ const currX = eventInfo.getPos().x
+ const currY = eventInfo.getPos().y
+
+ map.Visualize.mGraph.canvas.clear()
+ map.Visualize.mGraph.plot()
+
+ ctx.beginPath()
+ ctx.moveTo(startX, startY)
+ ctx.lineTo(startX, currY)
+ ctx.lineTo(currX, currY)
+ ctx.lineTo(currX, startY)
+ ctx.lineTo(startX, startY)
+ ctx.strokeStyle = 'black'
+ ctx.stroke()
+ }, // drawSelectBox
+ selectNodeOnClickHandler: function(node, e) {
+ if (map.Visualize.mGraph.busy) return
+
+ const self = toExport
+
+ // Copy topic title to clipboard
+ if (e.button === 1 && e.ctrlKey) clipboard.copy(node.name)
+
+ // catch right click on mac, which is often like ctrl+click
+ if (navigator.platform.indexOf('Mac') !== -1 && e.ctrlKey) {
+ self.selectNodeOnRightClickHandler(node, e)
+ return
}
- })
- map.Mouse.boxStartCoordinates = false
- map.Mouse.boxEndCoordinates = false
- map.Visualize.mGraph.plot()
- }, // selectWithBox
- drawSelectBox: function(eventInfo, e) {
- const ctx = map.Visualize.mGraph.canvas.getCtx()
- const startX = map.Mouse.boxStartCoordinates.x
- const startY = map.Mouse.boxStartCoordinates.y
- const currX = eventInfo.getPos().x
- const currY = eventInfo.getPos().y
+ // if on a topic page, let alt+click center you on a new topic
+ if (map.Active.Topic && e.altKey) {
+ map.JIT.RGraph.centerOn(node.id)
+ return
+ }
- map.Visualize.mGraph.canvas.clear()
- map.Visualize.mGraph.plot()
+ const check = self.nodeWasDoubleClicked()
+ if (check) {
+ self.nodeDoubleClickHandler(node, e)
+ return
+ } else {
+ // wait a certain length of time, then check again, then run this code
+ setTimeout(function() {
+ if (!map.JIT.nodeWasDoubleClicked()) {
+ var nodeAlreadySelected = node.selected
- ctx.beginPath()
- ctx.moveTo(startX, startY)
- ctx.lineTo(startX, currY)
- ctx.lineTo(currX, currY)
- ctx.lineTo(currX, startY)
- ctx.lineTo(startX, startY)
- ctx.strokeStyle = 'black'
- ctx.stroke()
- }, // drawSelectBox
- selectNodeOnClickHandler: function(node, e) {
- if (map.Visualize.mGraph.busy) return
+ if (e.button !== 1) {
+ if (!e.shiftKey) {
+ map.Control.deselectAllNodes()
+ map.Control.deselectAllEdges()
+ }
- const self = JIT
+ if (nodeAlreadySelected) {
+ map.Control.deselectNode(node)
+ } else {
+ map.Control.selectNode(node, e)
+ }
- // Copy topic title to clipboard
- if (e.button === 1 && e.ctrlKey) clipboard.copy(node.name)
+ // trigger animation to final styles
+ map.Visualize.mGraph.fx.animate({
+ modes: ['edge-property:lineWidth:color:alpha'],
+ duration: 500
+ })
+ map.Visualize.mGraph.plot()
+ } else {
+ if (!e.ctrlKey) {
+ var len = map.Selected.Nodes.length
- // catch right click on mac, which is often like ctrl+click
- if (navigator.platform.indexOf('Mac') !== -1 && e.ctrlKey) {
- self.selectNodeOnRightClickHandler(node, e)
- return
- }
+ for (let i = 0; i < len; i += 1) {
+ let n = map.Selected.Nodes[i]
+ let result = Util.openLink(map.DataModel.Topics.get(n.id).attributes.link)
- // if on a topic page, let alt+click center you on a new topic
- if (map.Active.Topic && e.altKey) {
- map.JIT.RGraph.centerOn(node.id)
- return
- }
+ if (!result) { // if link failed to open
+ break
+ }
+ }
- const check = self.nodeWasDoubleClicked()
- if (check) {
- self.nodeDoubleClickHandler(node, e)
- return
- } else {
- // wait a certain length of time, then check again, then run this code
- setTimeout(function() {
- if (!map.JIT.nodeWasDoubleClicked()) {
- var nodeAlreadySelected = node.selected
+ if (!node.selected) {
+ Util.openLink(map.DataModel.Topics.get(node.id).attributes.link)
+ }
+ }
+ }
+ }
+ }, map.Mouse.DOUBLE_CLICK_TOLERANCE)
+ }
+ }, // selectNodeOnClickHandler
+ selectNodeOnRightClickHandler: function(node, e) {
+ // the 'node' variable is a JIT node, the one that was clicked on
+ // the 'e' variable is the click event
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if (map.Visualize.mGraph.busy) return
+
+ // select the node
+ map.Control.selectNode(node, e)
+
+ // delete old right click menu
+ $('.rightclickmenu').remove()
+ // create new menu for clicked on node
+ const rightclickmenu = document.createElement('div')
+ rightclickmenu.className = 'rightclickmenu'
+ // prevent the custom context menu from immediately opening the default context menu as well
+ rightclickmenu.setAttribute('oncontextmenu', 'return false')
+
+ // add the proper options to the menu
+ let menustring = ''
+
+ const authorized = map.Active.Map && map.Active.Map.authorizeToEdit(map.Active.Mapper)
+
+ const disabled = authorized ? '' : 'disabled'
+
+ if (map.Active.Map) menustring += '
Hide until refreshCtrl+H
'
+ if (map.Active.Map && map.Active.Mapper) menustring += '
Remove from mapCtrl+M
'
+ if (map.Active.Topic) menustring += '
Remove from viewCtrl+M
'
+ if (map.Active.Map && map.Active.Mapper) menustring += '
DeleteCtrl+D
'
+
+ if (map.Active.Topic) {
+ menustring += '
Center this topicAlt+E
'
+ }
+
+ menustring += '
Open in new tab '
+
+ if (map.Active.Mapper) {
+ const options = outdent`
+
+
commons
+
public
+
private
+ `
+
+ menustring += ' '
+
+ menustring += outdent`
+
+
+ Change permissions
+ ${options}
+
+ `
+
+ menustring += '
Change metacode
'
+ }
+ if (map.Active.Topic) {
+ if (!map.Active.Mapper) {
+ menustring += ' '
+ }
+
+ // set up the get sibling menu as a "lazy load"
+ // only fill in the submenu when they hover over the get siblings list item
+ const siblingMenu = outdent`
+ `
+ menustring += '
Reveal siblings' + siblingMenu + '
'
+ }
+
+ menustring += ' '
+ rightclickmenu.innerHTML = menustring
+
+ // position the menu where the click happened
+ const position = {}
+ const RIGHTCLICK_WIDTH = 300
+ const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static
+ const SUBMENUS_WIDTH = 256
+ const MAX_SUBMENU_HEIGHT = 270
+ const windowWidth = $(window).width()
+ const windowHeight = $(window).height()
+
+ if (windowWidth - e.clientX < SUBMENUS_WIDTH) {
+ position.right = windowWidth - e.clientX
+ $(rightclickmenu).addClass('moveMenusToLeft')
+ } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) {
+ position.right = windowWidth - e.clientX
+ } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) {
+ position.left = e.clientX
+ $(rightclickmenu).addClass('moveMenusToLeft')
+ } else {
+ position.left = e.clientX
+ }
+
+ if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) {
+ position.bottom = windowHeight - e.clientY
+ $(rightclickmenu).addClass('moveMenusUp')
+ } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) {
+ position.top = e.clientY
+ $(rightclickmenu).addClass('moveMenusUp')
+ } else {
+ position.top = e.clientY
+ }
+
+ $(rightclickmenu).css(position)
+ // add the menu to the page
+ $('#wrapper').append(rightclickmenu)
+
+ ReactDOM.render(
+ React.createElement(MetacodeSelect, {
+ onMetacodeSelect: metacodeId => {
+ if (map.Selected.Nodes.length > 1) {
+ // batch update multiple topics
+ map.Control.updateSelectedMetacodes(metacodeId)
+ } else {
+ const topic = map.DataModel.Topics.get(node.id)
+ topic.save({
+ metacode_id: metacodeId
+ })
+ }
+ $(rightclickmenu).remove()
+ },
+ metacodeSets: ReactApp.metacodeSets
+ }),
+ document.getElementById('metacodeOptionsWrapper')
+ )
+
+ // attach events to clicks on the list items
+
+ // delete the selected things from the database
+ if (authorized) {
+ $('.rc-delete').click(function() {
+ $('.rightclickmenu').remove()
+ map.Control.deleteSelected()
+ })
+ }
+
+ // remove the selected things from the map
+ if (map.Active.Topic || authorized) {
+ $('.rc-remove').click(function() {
+ $('.rightclickmenu').remove()
+ map.Control.removeSelectedEdges()
+ map.Control.removeSelectedNodes()
+ })
+ }
+
+ // hide selected nodes and synapses until refresh
+ $('.rc-hide').click(function() {
+ $('.rightclickmenu').remove()
+ map.Control.hideSelectedEdges()
+ map.Control.hideSelectedNodes()
+ })
+
+ // when in radial, center on the topic you picked
+ $('.rc-center').click(function() {
+ $('.rightclickmenu').remove()
+ map.Topic.centerOn(node.id)
+ })
+
+ // open the entity in a new tab
+ $('.rc-popout').click(function() {
+ $('.rightclickmenu').remove()
+ const win = window.open('/topics/' + node.id, '_blank')
+ win.focus()
+ })
+
+ // change the permission of all the selected nodes and synapses that you were the originator of
+ $('.rc-permission li').click(function() {
+ $('.rightclickmenu').remove()
+ // $(this).text() will be 'commons' 'public' or 'private'
+ map.Control.updateSelectedPermissions($(this).text())
+ })
+
+ // fetch relatives
+ let fetchSent = false
+ $('.rc-siblings').hover(function() {
+ if (!fetchSent) {
+ map.JIT.populateRightClickSiblings(node)
+ fetchSent = true
+ }
+ })
+ $('.rc-siblings .fetchAll').click(function() {
+ $('.rightclickmenu').remove()
+ // data-id is a metacode id
+ map.Topic.fetchRelatives(node)
+ })
+ }, // selectNodeOnRightClickHandler,
+ populateRightClickSiblings: function(node) {
+ // depending on how many topics are selected, do different things
+ const topic = node.getData('topic')
+
+ // add a loading icon for now
+ const loader = new CanvasLoader('loadingSiblings')
+ loader.setColor('#4FC059') // default is '#000000'
+ loader.setDiameter(15) // default is 40
+ loader.setDensity(41) // default is 40
+ loader.setRange(0.9) // default is 1.3
+ loader.show() // Hidden by default
+
+ const topics = map.DataModel.Topics.map(function(t) { return t.id })
+ const topicsString = topics.join()
+
+ const successCallback = function(data) {
+ $('#loadingSiblings').remove()
+
+ for (var key in data) {
+ const string = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})`
+ $('#fetchSiblingList').append(`${string} `)
+ }
+
+ $('.rc-siblings .getSiblings').click(function() {
+ $('.rightclickmenu').remove()
+ // data-id is a metacode id
+ map.Topic.fetchRelatives(node, $(this).attr('data-id'))
+ })
+ }
+
+ $.ajax({
+ type: 'GET',
+ url: '/topics/' + topic.id + '/relative_numbers.json?network=' + topicsString,
+ success: successCallback,
+ error: function() {}
+ })
+ },
+ selectEdgeOnClickHandler: function(adj, e) {
+ if (map.Visualize.mGraph.busy) return
+
+ const self = toExport
+ var synapseText = adj.data.$synapses[0].attributes.desc
+ // Copy synapse label to clipboard
+ if (e.button === 1 && e.ctrlKey && synapseText !== '') clipboard.copy(synapseText)
+
+ // catch right click on mac, which is often like ctrl+click
+ if (navigator.platform.indexOf('Mac') !== -1 && e.ctrlKey) {
+ self.selectEdgeOnRightClickHandler(adj, e)
+ return
+ }
+
+ const check = self.nodeWasDoubleClicked()
+ if (check) {
+ self.edgeDoubleClickHandler(adj, e)
+ return
+ } else {
+ // wait a certain length of time, then check again, then run this code
+ setTimeout(function() {
+ if (!map.JIT.nodeWasDoubleClicked()) {
+ const edgeAlreadySelected = map.Selected.Edges.indexOf(adj) !== -1
- if (e.button !== 1) {
if (!e.shiftKey) {
map.Control.deselectAllNodes()
map.Control.deselectAllEdges()
}
- if (nodeAlreadySelected) {
- map.Control.deselectNode(node)
+ if (edgeAlreadySelected) {
+ map.Control.deselectEdge(adj)
} else {
- map.Control.selectNode(node, e)
+ map.Control.selectEdge(adj)
}
- // trigger animation to final styles
- map.Visualize.mGraph.fx.animate({
- modes: ['edge-property:lineWidth:color:alpha'],
- duration: 500
- })
map.Visualize.mGraph.plot()
- } else {
- if (!e.ctrlKey) {
- var len = map.Selected.Nodes.length
-
- for (let i = 0; i < len; i += 1) {
- let n = map.Selected.Nodes[i]
- let result = Util.openLink(map.DataModel.Topics.get(n.id).attributes.link)
-
- if (!result) { // if link failed to open
- break
- }
- }
-
- if (!node.selected) {
- Util.openLink(map.DataModel.Topics.get(node.id).attributes.link)
- }
- }
}
- }
- }, map.Mouse.DOUBLE_CLICK_TOLERANCE)
- }
- }, // selectNodeOnClickHandler
- selectNodeOnRightClickHandler: function(node, e) {
- // the 'node' variable is a JIT node, the one that was clicked on
- // the 'e' variable is the click event
+ }, map.Mouse.DOUBLE_CLICK_TOLERANCE)
+ }
+ }, // selectEdgeOnClickHandler
+ selectEdgeOnRightClickHandler: function(adj, e) {
+ // the 'node' variable is a JIT node, the one that was clicked on
+ // the 'e' variable is the click event
- e.preventDefault()
- e.stopPropagation()
+ if (adj.getData('alpha') === 0) return // don't do anything if the edge is filtered
- if (map.Visualize.mGraph.busy) return
+ e.preventDefault()
+ e.stopPropagation()
- // select the node
- map.Control.selectNode(node, e)
+ if (map.Visualize.mGraph.busy) return
- // delete old right click menu
- $('.rightclickmenu').remove()
- // create new menu for clicked on node
- const rightclickmenu = document.createElement('div')
- rightclickmenu.className = 'rightclickmenu'
- // prevent the custom context menu from immediately opening the default context menu as well
- rightclickmenu.setAttribute('oncontextmenu', 'return false')
+ map.Control.selectEdge(adj)
- // add the proper options to the menu
- let menustring = ''
+ // delete old right click menu
+ $('.rightclickmenu').remove()
+ // create new menu for clicked on node
+ const rightclickmenu = document.createElement('div')
+ rightclickmenu.className = 'rightclickmenu'
+ // prevent the custom context menu from immediately opening the default context menu as well
+ rightclickmenu.setAttribute('oncontextmenu', 'return false')
- const authorized = map.Active.Map && map.Active.Map.authorizeToEdit(map.Active.Mapper)
+ // add the proper options to the menu
+ let menustring = ''
- const disabled = authorized ? '' : 'disabled'
+ const authorized = map.Active.Map && map.Active.Map.authorizeToEdit(map.Active.Mapper)
- if (map.Active.Map) menustring += '
Hide until refreshCtrl+H
'
- if (map.Active.Map && map.Active.Mapper) menustring += '
Remove from mapCtrl+M
'
- if (map.Active.Topic) menustring += '
Remove from viewCtrl+M
'
- if (map.Active.Map && map.Active.Mapper) menustring += '
DeleteCtrl+D
'
+ const disabled = authorized ? '' : 'disabled'
- if (map.Active.Topic) {
- menustring += '
Center this topicAlt+E
'
- }
+ if (map.Active.Map) menustring += '
Hide until refreshCtrl+H
'
+ if (map.Active.Map && map.Active.Mapper) menustring += '
Remove from mapCtrl+M
'
+ if (map.Active.Topic) menustring += '
Remove from viewCtrl+M
'
+ if (map.Active.Map && map.Active.Mapper) menustring += '
DeleteCtrl+D
'
- menustring += '
Open in new tab '
+ if (map.Active.Map && map.Active.Mapper) menustring += ' '
- if (map.Active.Mapper) {
- const options = outdent`
-
-
commons
-
public
-
private
- `
+ if (map.Active.Mapper) {
+ const permOptions = outdent`
+
+
commons
+
public
private `
- menustring += ' '
-
- menustring += outdent`
-
-
- Change permissions
- ${options}
-
- `
-
- menustring += '
Change metacode
'
- }
- if (map.Active.Topic) {
- if (!map.Active.Mapper) {
- menustring += ' '
+ menustring += '
Change permissions' + permOptions + '
'
}
- // set up the get sibling menu as a "lazy load"
- // only fill in the submenu when they hover over the get siblings list item
- const siblingMenu = outdent`
- `
- menustring += '
Reveal siblings' + siblingMenu + '
'
- }
+ menustring += ' '
+ rightclickmenu.innerHTML = menustring
- menustring += ' '
- rightclickmenu.innerHTML = menustring
+ // position the menu where the click happened
+ const position = {}
+ const RIGHTCLICK_WIDTH = 300
+ const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static
+ const SUBMENUS_WIDTH = 256
+ const MAX_SUBMENU_HEIGHT = 270
+ const windowWidth = $(window).width()
+ const windowHeight = $(window).height()
- // position the menu where the click happened
- const position = {}
- const RIGHTCLICK_WIDTH = 300
- const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static
- const SUBMENUS_WIDTH = 256
- const MAX_SUBMENU_HEIGHT = 270
- const windowWidth = $(window).width()
- const windowHeight = $(window).height()
+ if (windowWidth - e.clientX < SUBMENUS_WIDTH) {
+ position.right = windowWidth - e.clientX
+ $(rightclickmenu).addClass('moveMenusToLeft')
+ } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) {
+ position.right = windowWidth - e.clientX
+ } else position.left = e.clientX
- if (windowWidth - e.clientX < SUBMENUS_WIDTH) {
- position.right = windowWidth - e.clientX
- $(rightclickmenu).addClass('moveMenusToLeft')
- } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) {
- position.right = windowWidth - e.clientX
- } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) {
- position.left = e.clientX
- $(rightclickmenu).addClass('moveMenusToLeft')
- } else {
- position.left = e.clientX
- }
+ if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) {
+ position.bottom = windowHeight - e.clientY
+ $(rightclickmenu).addClass('moveMenusUp')
+ } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) {
+ position.top = e.clientY
+ $(rightclickmenu).addClass('moveMenusUp')
+ } else position.top = e.clientY
- if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) {
- position.bottom = windowHeight - e.clientY
- $(rightclickmenu).addClass('moveMenusUp')
- } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) {
- position.top = e.clientY
- $(rightclickmenu).addClass('moveMenusUp')
- } else {
- position.top = e.clientY
- }
+ $(rightclickmenu).css(position)
- $(rightclickmenu).css(position)
- // add the menu to the page
- $('#wrapper').append(rightclickmenu)
+ // add the menu to the page
+ $('#wrapper').append(rightclickmenu)
- ReactDOM.render(
- React.createElement(MetacodeSelect, {
- onMetacodeSelect: metacodeId => {
- if (map.Selected.Nodes.length > 1) {
- // batch update multiple topics
- map.Control.updateSelectedMetacodes(metacodeId)
- } else {
- const topic = map.DataModel.Topics.get(node.id)
- topic.save({
- metacode_id: metacodeId
- })
- }
- $(rightclickmenu).remove()
- },
- metacodeSets: ReactApp.metacodeSets
- }),
- document.getElementById('metacodeOptionsWrapper')
- )
+ // attach events to clicks on the list items
- // attach events to clicks on the list items
-
- // delete the selected things from the database
- if (authorized) {
- $('.rc-delete').click(function() {
- $('.rightclickmenu').remove()
- map.Control.deleteSelected()
- })
- }
-
- // remove the selected things from the map
- if (map.Active.Topic || authorized) {
- $('.rc-remove').click(function() {
- $('.rightclickmenu').remove()
- map.Control.removeSelectedEdges()
- map.Control.removeSelectedNodes()
- })
- }
-
- // hide selected nodes and synapses until refresh
- $('.rc-hide').click(function() {
- $('.rightclickmenu').remove()
- map.Control.hideSelectedEdges()
- map.Control.hideSelectedNodes()
- })
-
- // when in radial, center on the topic you picked
- $('.rc-center').click(function() {
- $('.rightclickmenu').remove()
- map.Topic.centerOn(node.id)
- })
-
- // open the entity in a new tab
- $('.rc-popout').click(function() {
- $('.rightclickmenu').remove()
- const win = window.open('/topics/' + node.id, '_blank')
- win.focus()
- })
-
- // change the permission of all the selected nodes and synapses that you were the originator of
- $('.rc-permission li').click(function() {
- $('.rightclickmenu').remove()
- // $(this).text() will be 'commons' 'public' or 'private'
- map.Control.updateSelectedPermissions($(this).text())
- })
-
- // fetch relatives
- let fetchSent = false
- $('.rc-siblings').hover(function() {
- if (!fetchSent) {
- map.JIT.populateRightClickSiblings(node)
- fetchSent = true
- }
- })
- $('.rc-siblings .fetchAll').click(function() {
- $('.rightclickmenu').remove()
- // data-id is a metacode id
- map.Topic.fetchRelatives(node)
- })
- }, // selectNodeOnRightClickHandler,
- populateRightClickSiblings: function(node) {
- // depending on how many topics are selected, do different things
- const topic = node.getData('topic')
-
- // add a loading icon for now
- const loader = new CanvasLoader('loadingSiblings')
- loader.setColor('#4FC059') // default is '#000000'
- loader.setDiameter(15) // default is 40
- loader.setDensity(41) // default is 40
- loader.setRange(0.9) // default is 1.3
- loader.show() // Hidden by default
-
- const topics = map.DataModel.Topics.map(function(t) { return t.id })
- const topicsString = topics.join()
-
- const successCallback = function(data) {
- $('#loadingSiblings').remove()
-
- for (var key in data) {
- const string = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})`
- $('#fetchSiblingList').append(`${string} `)
+ // delete the selected things from the database
+ if (authorized) {
+ $('.rc-delete').click(function() {
+ $('.rightclickmenu').remove()
+ map.Control.deleteSelected()
+ })
}
- $('.rc-siblings .getSiblings').click(function() {
+ // remove the selected things from the map
+ if (authorized) {
+ $('.rc-remove').click(function() {
+ $('.rightclickmenu').remove()
+ map.Control.removeSelectedEdges()
+ map.Control.removeSelectedNodes()
+ })
+ }
+
+ // hide selected nodes and synapses until refresh
+ $('.rc-hide').click(function() {
$('.rightclickmenu').remove()
- // data-id is a metacode id
- map.Topic.fetchRelatives(node, $(this).attr('data-id'))
+ map.Control.hideSelectedEdges()
+ map.Control.hideSelectedNodes()
})
- }
- $.ajax({
- type: 'GET',
- url: '/topics/' + topic.id + '/relative_numbers.json?network=' + topicsString,
- success: successCallback,
- error: function() {}
- })
- },
- selectEdgeOnClickHandler: function(adj, e) {
- if (map.Visualize.mGraph.busy) return
-
- const self = JIT
- var synapseText = adj.data.$synapses[0].attributes.desc
- // Copy synapse label to clipboard
- if (e.button === 1 && e.ctrlKey && synapseText !== '') clipboard.copy(synapseText)
-
- // catch right click on mac, which is often like ctrl+click
- if (navigator.platform.indexOf('Mac') !== -1 && e.ctrlKey) {
- self.selectEdgeOnRightClickHandler(adj, e)
- return
- }
-
- const check = self.nodeWasDoubleClicked()
- if (check) {
- self.edgeDoubleClickHandler(adj, e)
- return
- } else {
- // wait a certain length of time, then check again, then run this code
- setTimeout(function() {
- if (!map.JIT.nodeWasDoubleClicked()) {
- const edgeAlreadySelected = map.Selected.Edges.indexOf(adj) !== -1
-
- if (!e.shiftKey) {
- map.Control.deselectAllNodes()
- map.Control.deselectAllEdges()
- }
-
- if (edgeAlreadySelected) {
- map.Control.deselectEdge(adj)
- } else {
- map.Control.selectEdge(adj)
- }
-
- map.Visualize.mGraph.plot()
- }
- }, map.Mouse.DOUBLE_CLICK_TOLERANCE)
- }
- }, // selectEdgeOnClickHandler
- selectEdgeOnRightClickHandler: function(adj, e) {
- // the 'node' variable is a JIT node, the one that was clicked on
- // the 'e' variable is the click event
-
- if (adj.getData('alpha') === 0) return // don't do anything if the edge is filtered
-
- e.preventDefault()
- e.stopPropagation()
-
- if (map.Visualize.mGraph.busy) return
-
- map.Control.selectEdge(adj)
-
- // delete old right click menu
- $('.rightclickmenu').remove()
- // create new menu for clicked on node
- const rightclickmenu = document.createElement('div')
- rightclickmenu.className = 'rightclickmenu'
- // prevent the custom context menu from immediately opening the default context menu as well
- rightclickmenu.setAttribute('oncontextmenu', 'return false')
-
- // add the proper options to the menu
- let menustring = ''
-
- const authorized = map.Active.Map && map.Active.Map.authorizeToEdit(map.Active.Mapper)
-
- const disabled = authorized ? '' : 'disabled'
-
- if (map.Active.Map) menustring += '
Hide until refreshCtrl+H
'
- if (map.Active.Map && map.Active.Mapper) menustring += '
Remove from mapCtrl+M
'
- if (map.Active.Topic) menustring += '
Remove from viewCtrl+M
'
- if (map.Active.Map && map.Active.Mapper) menustring += '
DeleteCtrl+D
'
-
- if (map.Active.Map && map.Active.Mapper) menustring += ' '
-
- if (map.Active.Mapper) {
- const permOptions = outdent`
-
-
commons
-
public
private `
-
- menustring += '
Change permissions' + permOptions + '
'
- }
-
- menustring += ' '
- rightclickmenu.innerHTML = menustring
-
- // position the menu where the click happened
- const position = {}
- const RIGHTCLICK_WIDTH = 300
- const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static
- const SUBMENUS_WIDTH = 256
- const MAX_SUBMENU_HEIGHT = 270
- const windowWidth = $(window).width()
- const windowHeight = $(window).height()
-
- if (windowWidth - e.clientX < SUBMENUS_WIDTH) {
- position.right = windowWidth - e.clientX
- $(rightclickmenu).addClass('moveMenusToLeft')
- } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) {
- position.right = windowWidth - e.clientX
- } else position.left = e.clientX
-
- if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) {
- position.bottom = windowHeight - e.clientY
- $(rightclickmenu).addClass('moveMenusUp')
- } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) {
- position.top = e.clientY
- $(rightclickmenu).addClass('moveMenusUp')
- } else position.top = e.clientY
-
- $(rightclickmenu).css(position)
-
- // add the menu to the page
- $('#wrapper').append(rightclickmenu)
-
- // attach events to clicks on the list items
-
- // delete the selected things from the database
- if (authorized) {
- $('.rc-delete').click(function() {
+ // change the permission of all the selected nodes and synapses that you were the originator of
+ $('.rc-permission li').click(function() {
$('.rightclickmenu').remove()
- map.Control.deleteSelected()
+ // $(this).text() will be 'commons' 'public' or 'private'
+ map.Control.updateSelectedPermissions($(this).text())
})
- }
+ }, // selectEdgeOnRightClickHandler
+ SmoothPanning: function() {
+ const sx = map.Visualize.mGraph.canvas.scaleOffsetX
+ const sy = map.Visualize.mGraph.canvas.scaleOffsetY
+ const yVelocity = map.Mouse.changeInY // initial y velocity
+ const xVelocity = map.Mouse.changeInX // initial x velocity
+ let easing = 1 // frictional value
- // remove the selected things from the map
- if (authorized) {
- $('.rc-remove').click(function() {
- $('.rightclickmenu').remove()
- map.Control.removeSelectedEdges()
- map.Control.removeSelectedNodes()
- })
- }
+ window.clearInterval(panningInt)
+ panningInt = setInterval(function() {
+ myTimer()
+ }, 1)
- // hide selected nodes and synapses until refresh
- $('.rc-hide').click(function() {
- $('.rightclickmenu').remove()
- map.Control.hideSelectedEdges()
- map.Control.hideSelectedNodes()
- })
+ function myTimer() {
+ map.Visualize.mGraph.canvas.translate(xVelocity * easing * 1 / sx, yVelocity * easing * 1 / sy)
+ $(document).trigger(JIT.events.pan)
+ easing = easing * 0.75
- // change the permission of all the selected nodes and synapses that you were the originator of
- $('.rc-permission li').click(function() {
- $('.rightclickmenu').remove()
- // $(this).text() will be 'commons' 'public' or 'private'
- map.Control.updateSelectedPermissions($(this).text())
- })
- }, // selectEdgeOnRightClickHandler
- SmoothPanning: function() {
- const sx = map.Visualize.mGraph.canvas.scaleOffsetX
- const sy = map.Visualize.mGraph.canvas.scaleOffsetY
- const yVelocity = map.Mouse.changeInY // initial y velocity
- const xVelocity = map.Mouse.changeInX // initial x velocity
- let easing = 1 // frictional value
+ if (easing < 0.1) window.clearInterval(panningInt)
+ }
+ }, // SmoothPanning
+ renderMidArrow: function(from, to, dim, swap, canvas, placement, newSynapse) {
+ const ctx = canvas.getCtx()
+ // invert edge direction
+ if (swap) {
+ const tmp = from
+ from = to
+ to = tmp
+ }
+ // vect represents a line from tip to tail of the arrow
+ const vect = new $jit.Complex(to.x - from.x, to.y - from.y)
+ // scale it
+ vect.$scale(dim / vect.norm())
+ // compute the midpoint of the edge line
+ const newX = (to.x - from.x) * placement + from.x
+ const newY = (to.y - from.y) * placement + from.y
+ const midPoint = new $jit.Complex(newX, newY)
- window.clearInterval(panningInt)
- panningInt = setInterval(function() {
- myTimer()
- }, 1)
+ // move midpoint by half the "length" of the arrow so the arrow is centered on the midpoint
+ const arrowPoint = new $jit.Complex((vect.x / 0.7) + midPoint.x, (vect.y / 0.7) + midPoint.y)
+ // compute the tail intersection point with the edge line
+ const intermediatePoint = new $jit.Complex(arrowPoint.x - vect.x, arrowPoint.y - vect.y)
+ // vector perpendicular to vect
+ const normal = new $jit.Complex(-vect.y / 2, vect.x / 2)
+ const v1 = intermediatePoint.add(normal)
+ const v2 = intermediatePoint.$add(normal.$scale(-1))
- function myTimer() {
- map.Visualize.mGraph.canvas.translate(xVelocity * easing * 1 / sx, yVelocity * easing * 1 / sy)
- $(document).trigger(map.JIT.events.pan)
- easing = easing * 0.75
+ if (newSynapse) {
+ ctx.strokeStyle = '#4fc059'
+ ctx.lineWidth = 2
+ ctx.globalAlpha = 1
+ }
+ ctx.beginPath()
+ ctx.moveTo(from.x, from.y)
+ ctx.lineTo(to.x, to.y)
+ ctx.stroke()
+ ctx.beginPath()
+ ctx.moveTo(v1.x, v1.y)
+ ctx.lineTo(arrowPoint.x, arrowPoint.y)
+ ctx.lineTo(v2.x, v2.y)
+ ctx.stroke()
+ }, // renderMidArrow
+ renderEdgeArrows: function(edgeHelper, adj, synapse, canvas) {
+ const self = toExport
- if (easing < 0.1) window.clearInterval(panningInt)
- }
- }, // SmoothPanning
- renderMidArrow: function(from, to, dim, swap, canvas, placement, newSynapse) {
- const ctx = canvas.getCtx()
- // invert edge direction
- if (swap) {
- const tmp = from
- from = to
- to = tmp
- }
- // vect represents a line from tip to tail of the arrow
- const vect = new $jit.Complex(to.x - from.x, to.y - from.y)
- // scale it
- vect.$scale(dim / vect.norm())
- // compute the midpoint of the edge line
- const newX = (to.x - from.x) * placement + from.x
- const newY = (to.y - from.y) * placement + from.y
- const midPoint = new $jit.Complex(newX, newY)
+ const directionCat = synapse.get('category')
+ const direction = synapse.getDirection()
- // move midpoint by half the "length" of the arrow so the arrow is centered on the midpoint
- const arrowPoint = new $jit.Complex((vect.x / 0.7) + midPoint.x, (vect.y / 0.7) + midPoint.y)
- // compute the tail intersection point with the edge line
- const intermediatePoint = new $jit.Complex(arrowPoint.x - vect.x, arrowPoint.y - vect.y)
- // vector perpendicular to vect
- const normal = new $jit.Complex(-vect.y / 2, vect.x / 2)
- const v1 = intermediatePoint.add(normal)
- const v2 = intermediatePoint.$add(normal.$scale(-1))
+ const pos = adj.nodeFrom.pos.getc(true)
+ const posChild = adj.nodeTo.pos.getc(true)
- if (newSynapse) {
- ctx.strokeStyle = '#4fc059'
- ctx.lineWidth = 2
- ctx.globalAlpha = 1
- }
- ctx.beginPath()
- ctx.moveTo(from.x, from.y)
- ctx.lineTo(to.x, to.y)
- ctx.stroke()
- ctx.beginPath()
- ctx.moveTo(v1.x, v1.y)
- ctx.lineTo(arrowPoint.x, arrowPoint.y)
- ctx.lineTo(v2.x, v2.y)
- ctx.stroke()
- }, // renderMidArrow
- renderEdgeArrows: function(edgeHelper, adj, synapse, canvas) {
- const self = JIT
+ // plot arrow edge
+ if (!direction) {
+ // render nothing for this arrow if the direction couldn't be retrieved
+ } else if (directionCat === 'none') {
+ edgeHelper.line.render({
+ x: pos.x,
+ y: pos.y
+ }, {
+ x: posChild.x,
+ y: posChild.y
+ }, canvas)
+ } else if (directionCat === 'both') {
+ self.renderMidArrow({
+ x: pos.x,
+ y: pos.y
+ }, {
+ x: posChild.x,
+ y: posChild.y
+ }, 13, true, canvas, 0.7)
+ self.renderMidArrow({
+ x: pos.x,
+ y: pos.y
+ }, {
+ x: posChild.x,
+ y: posChild.y
+ }, 13, false, canvas, 0.7)
+ } else if (directionCat === 'from-to') {
+ const inv = (direction[0] !== adj.nodeFrom.id)
+ self.renderMidArrow({
+ x: pos.x,
+ y: pos.y
+ }, {
+ x: posChild.x,
+ y: posChild.y
+ }, 13, inv, canvas, 0.7)
+ self.renderMidArrow({
+ x: pos.x,
+ y: pos.y
+ }, {
+ x: posChild.x,
+ y: posChild.y
+ }, 13, inv, canvas, 0.3)
+ }
+ }, // renderEdgeArrows
+ zoomIn: function(event) {
+ map.Visualize.mGraph.canvas.scale(1.25, 1.25)
+ $(document).trigger(JIT.events.zoom, [event])
+ },
+ zoomOut: function(event) {
+ map.Visualize.mGraph.canvas.scale(0.8, 0.8)
+ $(document).trigger(JIT.events.zoom, [event])
+ },
+ centerMap: function(canvas) {
+ const offsetScale = canvas.scaleOffsetX
- const directionCat = synapse.get('category')
- const direction = synapse.getDirection()
+ canvas.scale(1 / offsetScale, 1 / offsetScale)
- const pos = adj.nodeFrom.pos.getc(true)
- const posChild = adj.nodeTo.pos.getc(true)
+ const offsetX = canvas.translateOffsetX
+ const offsetY = canvas.translateOffsetY
- // plot arrow edge
- if (!direction) {
- // render nothing for this arrow if the direction couldn't be retrieved
- } else if (directionCat === 'none') {
- edgeHelper.line.render({
- x: pos.x,
- y: pos.y
- }, {
- x: posChild.x,
- y: posChild.y
- }, canvas)
- } else if (directionCat === 'both') {
- self.renderMidArrow({
- x: pos.x,
- y: pos.y
- }, {
- x: posChild.x,
- y: posChild.y
- }, 13, true, canvas, 0.7)
- self.renderMidArrow({
- x: pos.x,
- y: pos.y
- }, {
- x: posChild.x,
- y: posChild.y
- }, 13, false, canvas, 0.7)
- } else if (directionCat === 'from-to') {
- const inv = (direction[0] !== adj.nodeFrom.id)
- self.renderMidArrow({
- x: pos.x,
- y: pos.y
- }, {
- x: posChild.x,
- y: posChild.y
- }, 13, inv, canvas, 0.7)
- self.renderMidArrow({
- x: pos.x,
- y: pos.y
- }, {
- x: posChild.x,
- y: posChild.y
- }, 13, inv, canvas, 0.3)
- }
- }, // renderEdgeArrows
- zoomIn: function(event) {
- map.Visualize.mGraph.canvas.scale(1.25, 1.25)
- $(document).trigger(map.JIT.events.zoom, [event])
- },
- zoomOut: function(event) {
- map.Visualize.mGraph.canvas.scale(0.8, 0.8)
- $(document).trigger(map.JIT.events.zoom, [event])
- },
- centerMap: function(canvas) {
- const offsetScale = canvas.scaleOffsetX
+ canvas.translate(-1 * offsetX, -1 * offsetY)
+ },
+ zoomToBox: function(event) {
+ const sX = map.Mouse.boxStartCoordinates.x
+ const sY = map.Mouse.boxStartCoordinates.y
+ const eX = map.Mouse.boxEndCoordinates.x
+ const eY = map.Mouse.boxEndCoordinates.y
- canvas.scale(1 / offsetScale, 1 / offsetScale)
+ let canvas = map.Visualize.mGraph.canvas
+ map.JIT.centerMap(canvas)
- const offsetX = canvas.translateOffsetX
- const offsetY = canvas.translateOffsetY
+ let height = $(document).height()
+ let width = $(document).width()
- canvas.translate(-1 * offsetX, -1 * offsetY)
- },
- zoomToBox: function(event) {
- const sX = map.Mouse.boxStartCoordinates.x
- const sY = map.Mouse.boxStartCoordinates.y
- const eX = map.Mouse.boxEndCoordinates.x
- const eY = map.Mouse.boxEndCoordinates.y
+ let spanX = Math.abs(sX - eX)
+ let spanY = Math.abs(sY - eY)
+ let ratioX = width / spanX
+ let ratioY = height / spanY
- let canvas = map.Visualize.mGraph.canvas
- map.JIT.centerMap(canvas)
+ let newRatio = Math.min(ratioX, ratioY)
- let height = $(document).height()
- let width = $(document).width()
+ if (canvas.scaleOffsetX * newRatio <= 5 && canvas.scaleOffsetX * newRatio >= 0.2) {
+ canvas.scale(newRatio, newRatio)
+ } else if (canvas.scaleOffsetX * newRatio > 5) {
+ newRatio = 5 / canvas.scaleOffsetX
+ canvas.scale(newRatio, newRatio)
+ } else {
+ newRatio = 0.2 / canvas.scaleOffsetX
+ canvas.scale(newRatio, newRatio)
+ }
- let spanX = Math.abs(sX - eX)
- let spanY = Math.abs(sY - eY)
- let ratioX = width / spanX
- let ratioY = height / spanY
-
- let newRatio = Math.min(ratioX, ratioY)
-
- if (canvas.scaleOffsetX * newRatio <= 5 && canvas.scaleOffsetX * newRatio >= 0.2) {
- canvas.scale(newRatio, newRatio)
- } else if (canvas.scaleOffsetX * newRatio > 5) {
- newRatio = 5 / canvas.scaleOffsetX
- canvas.scale(newRatio, newRatio)
- } else {
- newRatio = 0.2 / canvas.scaleOffsetX
- canvas.scale(newRatio, newRatio)
- }
-
- const cogX = (sX + eX) / 2
- const cogY = (sY + eY) / 2
-
- canvas.translate(-1 * cogX, -1 * cogY)
- $(document).trigger(map.JIT.events.zoom, [event])
-
- map.Mouse.boxStartCoordinates = false
- map.Mouse.boxEndCoordinates = false
- map.Visualize.mGraph.plot()
- },
- zoomExtents: function(event, canvas, denySelected) {
- map.JIT.centerMap(canvas)
- let height = canvas.getSize().height
- let width = canvas.getSize().width
- let maxX
- let maxY
- let minX
- let minY
- let counter = 0
-
- let nodes
- if (!denySelected && map.Selected.Nodes.length > 0) {
- nodes = map.Selected.Nodes
- } else {
- nodes = _.values(map.Visualize.mGraph.graph.nodes)
- }
-
- if (nodes.length > 1) {
- nodes.forEach(function(n) {
- let x = n.pos.x
- let y = n.pos.y
-
- if (counter === 0 && n.getData('alpha') === 1) {
- maxX = x
- minX = x
- maxY = y
- minY = y
- }
-
- let arrayOfLabelLines = Util.splitLine(n.name, 25).split('\n')
- let dim = n.getData('dim')
- let ctx = canvas.getCtx()
-
- let height = 25 * arrayOfLabelLines.length
-
- let lineWidths = []
- for (let index = 0; index < arrayOfLabelLines.length; ++index) {
- lineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width)
- }
- let width = Math.max.apply(null, lineWidths) + 8
-
- // only adjust these values if the node is not filtered
- if (n.getData('alpha') === 1) {
- maxX = Math.max(x + width / 2, maxX)
- maxY = Math.max(y + n.getData('height') + 5 + height, maxY)
- minX = Math.min(x - width / 2, minX)
- minY = Math.min(y - dim, minY)
-
- counter++
- }
- })
-
- let spanX = maxX - minX
- let spanY = maxY - minY
- let ratioX = spanX / width
- let ratioY = spanY / height
-
- let cogX = (maxX + minX) / 2
- let cogY = (maxY + minY) / 2
+ const cogX = (sX + eX) / 2
+ const cogY = (sY + eY) / 2
canvas.translate(-1 * cogX, -1 * cogY)
+ $(document).trigger(JIT.events.zoom, [event])
- let newRatio = Math.max(ratioX, ratioY)
- let scaleMultiplier = 1 / newRatio * 0.9
+ map.Mouse.boxStartCoordinates = false
+ map.Mouse.boxEndCoordinates = false
+ map.Visualize.mGraph.plot()
+ },
+ zoomExtents: function(event, canvas, denySelected) {
+ map.JIT.centerMap(canvas)
+ let height = canvas.getSize().height
+ let width = canvas.getSize().width
+ let maxX
+ let maxY
+ let minX
+ let minY
+ let counter = 0
- if (canvas.scaleOffsetX * scaleMultiplier <= 3 && canvas.scaleOffsetX * scaleMultiplier >= 0.2) {
- canvas.scale(scaleMultiplier, scaleMultiplier)
- } else if (canvas.scaleOffsetX * scaleMultiplier > 3) {
- scaleMultiplier = 3 / canvas.scaleOffsetX
- canvas.scale(scaleMultiplier, scaleMultiplier)
+ let nodes
+ if (!denySelected && map.Selected.Nodes.length > 0) {
+ nodes = map.Selected.Nodes
} else {
- scaleMultiplier = 0.2 / canvas.scaleOffsetX
- canvas.scale(scaleMultiplier, scaleMultiplier)
+ nodes = _.values(map.Visualize.mGraph.graph.nodes)
}
- $(document).trigger(map.JIT.events.zoom, [event])
- } else if (nodes.length === 1) {
- nodes.forEach(function(n) {
- const x = n.pos.x
- const y = n.pos.y
+ if (nodes.length > 1) {
+ nodes.forEach(function(n) {
+ let x = n.pos.x
+ let y = n.pos.y
- canvas.translate(-1 * x, -1 * y)
- $(document).trigger(map.JIT.events.zoom, [event])
- })
+ if (counter === 0 && n.getData('alpha') === 1) {
+ maxX = x
+ minX = x
+ maxY = y
+ minY = y
+ }
+
+ let arrayOfLabelLines = Util.splitLine(n.name, 25).split('\n')
+ let dim = n.getData('dim')
+ let ctx = canvas.getCtx()
+
+ let height = 25 * arrayOfLabelLines.length
+
+ let lineWidths = []
+ for (let index = 0; index < arrayOfLabelLines.length; ++index) {
+ lineWidths.push(ctx.measureText(arrayOfLabelLines[index]).width)
+ }
+ let width = Math.max.apply(null, lineWidths) + 8
+
+ // only adjust these values if the node is not filtered
+ if (n.getData('alpha') === 1) {
+ maxX = Math.max(x + width / 2, maxX)
+ maxY = Math.max(y + n.getData('height') + 5 + height, maxY)
+ minX = Math.min(x - width / 2, minX)
+ minY = Math.min(y - dim, minY)
+
+ counter++
+ }
+ })
+
+ let spanX = maxX - minX
+ let spanY = maxY - minY
+ let ratioX = spanX / width
+ let ratioY = spanY / height
+
+ let cogX = (maxX + minX) / 2
+ let cogY = (maxY + minY) / 2
+
+ canvas.translate(-1 * cogX, -1 * cogY)
+
+ let newRatio = Math.max(ratioX, ratioY)
+ let scaleMultiplier = 1 / newRatio * 0.9
+
+ if (canvas.scaleOffsetX * scaleMultiplier <= 3 && canvas.scaleOffsetX * scaleMultiplier >= 0.2) {
+ canvas.scale(scaleMultiplier, scaleMultiplier)
+ } else if (canvas.scaleOffsetX * scaleMultiplier > 3) {
+ scaleMultiplier = 3 / canvas.scaleOffsetX
+ canvas.scale(scaleMultiplier, scaleMultiplier)
+ } else {
+ scaleMultiplier = 0.2 / canvas.scaleOffsetX
+ canvas.scale(scaleMultiplier, scaleMultiplier)
+ }
+
+ $(document).trigger(JIT.events.zoom, [event])
+ } else if (nodes.length === 1) {
+ nodes.forEach(function(n) {
+ const x = n.pos.x
+ const y = n.pos.y
+
+ canvas.translate(-1 * x, -1 * y)
+ $(document).trigger(JIT.events.zoom, [event])
+ })
+ }
}
}
+ return toExport
}
+JIT.events = {
+ topicDrag: 'Metamaps:JIT:events:topicDrag',
+ pan: 'Metamaps:JIT:events:pan',
+ zoom: 'Metamaps:JIT:events:zoom',
+ animationDone: 'Metamaps:JIT:events:animationDone'
}
-map.JIT.init = function(serverData) {
- map.JIT.topicDescImage = new Image()
- map.JIT.topicDescImage.src = serverData['topic_description_signifier.png']
+JIT.init = function(serverData) {
+ JIT.topicDescImage = new Image()
+ JIT.topicDescImage.src = serverData['topic_description_signifier.png']
- map.JIT.topicLinkImage = new Image()
- map.JIT.topicLinkImage.src = serverData['topic_link_signifier.png']
+ JIT.topicLinkImage = new Image()
+ JIT.topicLinkImage.src = serverData['topic_link_signifier.png']
}
export default JIT
diff --git a/frontend/src/Metamaps/Map/Realtime/index.js b/frontend/src/Metamaps/Map/Realtime/index.js
index 32735939..8331e5db 100644
--- a/frontend/src/Metamaps/Map/Realtime/index.js
+++ b/frontend/src/Metamaps/Map/Realtime/index.js
@@ -3,8 +3,12 @@
import SimpleWebRTC from 'simplewebrtc'
import SocketIoConnection from 'simplewebrtc/socketioconnection'
+import MessageCollection from '../../DataModel/MessageCollection'
import Util from '../../Util'
import Views from '../Views'
+import ChatView from '../ChatView'
+import { ReactApp } from '../../GlobalUI'
+import JIT from '../JIT'
import {
JUNTO_UPDATED,
@@ -25,7 +29,6 @@ import {
} from './events'
import {
- juntoUpdated,
invitedToCall,
invitedToJoin,
callAccepted,
@@ -59,49 +62,63 @@ import {
} from './sendable'
const Realtime = (map) => {
-const toExport = {
- videoId: 'video-wrapper',
- socket: null,
- webrtc: null,
- readyToCall: false,
- mappersOnMap: {},
- disconnected: false,
- chatOpen: false,
- soundId: null,
- broadcastingStatus: false,
- inConversation: false,
- localVideo: null,
- 'junto_spinner_darkgrey.gif': '',
- init: function(serverData) {
- var self = toExport
-
- self.addJuntoListeners()
-
- self.socket = new SocketIoConnection({
- url: serverData['REALTIME_SERVER'],
- socketio: {
- // don't poll forever if in development
- reconnectionAttempts: serverData.RAILS_ENV === 'development' ? 5 : Infinity
- }
- })
- self['junto_spinner_darkgrey.gif'] = serverData['junto_spinner_darkgrey.gif']
-
- self.socket.on('connect', function() {
- console.log('connected')
- if (map.Active.Map && map.Active.Mapper && map.Active.Map.authorizeToEdit(map.Active.Mapper)) {
- self.checkForCall()
- self.joinMap()
- }
- subscribeToEvents(self, self.socket)
- self.disconnected = false
- })
- self.socket.on('disconnect', function() {
- self.disconnected = true
- })
-
- if (map.Active.Mapper) {
+ const toExport = {
+ videoId: 'video-wrapper',
+ webrtc: null,
+ readyToCall: false,
+ mappersOnMap: {},
+ chatOpen: false,
+ soundId: null,
+ broadcastingStatus: false,
+ inConversation: false,
+ localVideo: null,
+ onSocketConnect: () => {},
+ startActiveMap: function() {
+ var self = toExport
+ if (map.Active.Map.authorizeToEdit(map.Active.Mapper)) {
+ self.addJuntoListeners()
+ if (Realtime.socket && !Realtime.socket.disconnected) self.onSocketConnect = self._onSocketConnect
+ else self._onSocketConnect()
+ }
+ if (map.Active.Mapper) {
+ self.setupChat() // chat can happen on public maps too
+ map.Cable.subscribeToMap(map.Active.Map.id) // people with viewing rights can still see live updates
+ }
+ },
+ endActiveMap: function() {
+ var self = toExport
+ $(document).off('.map')
+ // leave the appropriate rooms to leave
+ if (self.inConversation) self.leaveCall()
+ self.leaveMap()
+ $('.collabCompass').remove()
+ if (self.room) self.room.leave()
+ if (!Realtime.socket.disconnected) self.unsubscribeFromEvents()
+ map.Cable.unsubscribeFromMap()
+ },
+ _onSocketConnect: function() {
+ console.log('testing')
+ const self = toExport
+ const sendables = [
+ ['joinMap', joinMap],
+ ['leaveMap', leaveMap],
+ ['checkForCall', checkForCall],
+ ['acceptCall', acceptCall],
+ ['denyCall', denyCall],
+ ['denyInvite', denyInvite],
+ ['inviteToJoin', inviteToJoin],
+ ['inviteACall', inviteACall],
+ ['joinCall', joinCall],
+ ['leaveCall', leaveCall],
+ ['sendMapperInfo', sendMapperInfo],
+ ['sendCoords', sendCoords],
+ ['dragTopic', dragTopic]
+ ]
+ sendables.forEach(sendable => {
+ toExport[sendable[0]] = sendable[1](Realtime.socket, toExport, map)
+ })
self.webrtc = new SimpleWebRTC({
- connection: self.socket,
+ connection: Realtime.socket,
localVideoEl: self.videoId,
remoteVideosEl: '',
debug: true,
@@ -127,7 +144,6 @@ const toExport = {
console.log('remote ice failure', peer)
// remote ice failure
})
-
var $video = $(' ').attr('id', self.videoId)
self.localVideo = {
$video: $video,
@@ -136,291 +152,291 @@ const toExport = {
avatar: map.Active.Mapper ? map.Active.Mapper.get('image') : ''
})
}
-
self.room = new Views.Room({
webrtc: self.webrtc,
- socket: self.socket,
- room: 'global',
+ room: 'map-' + map.Active.Map.id,
$video: self.localVideo.$video,
myVideoView: self.localVideo.view,
config: { DOUBLE_CLICK_TOLERANCE: 200 }
})
self.room.videoAdded(self.handleVideoAdded)
- } // if map.Active.Mapper
- },
- addJuntoListeners: function() {
- var self = toExport
-
- $(document).on(ChatView.events.openTray, function() {
- $('.main').addClass('compressed')
- self.chatOpen = true
- self.positionPeerIcons()
- })
- $(document).on(ChatView.events.closeTray, function() {
- $('.main').removeClass('compressed')
- self.chatOpen = false
- self.positionPeerIcons()
- })
- $(document).on(ChatView.events.videosOn, function() {
- $('#wrapper').removeClass('hideVideos')
- })
- $(document).on(ChatView.events.videosOff, function() {
- $('#wrapper').addClass('hideVideos')
- })
- $(document).on(ChatView.events.cursorsOn, function() {
- $('#wrapper').removeClass('hideCursors')
- })
- $(document).on(ChatView.events.cursorsOff, function() {
- $('#wrapper').addClass('hideCursors')
- })
- },
- startActiveMap: function() {
- var self = toExport
- if (map.Active.Map && map.Active.Mapper) {
- if (map.Active.Map.authorizeToEdit(map.Active.Mapper)) {
- self.turnOn()
- self.checkForCall()
- self.joinMap()
- }
- self.setupChat() // chat can happen on public maps too
- map.Cable.subscribeToMap(map.Active.Map.id) // people with edit rights can still see live updates
- }
- },
- endActiveMap: function() {
- var self = toExport
- $(document).off('.map')
- // leave the appropriate rooms to leave
- if (self.inConversation) self.leaveCall()
- self.leaveMap()
- $('.collabCompass').remove()
- if (self.room) self.room.leave()
- map.Cable.unsubscribeFromMap()
- },
- turnOn: function(notify) {
- var self = toExport
- $('.collabCompass').show()
- self.room.room = 'map-' + map.Active.Map.id
- self.activeMapper = {
- id: map.Active.Mapper.id,
- name: map.Active.Mapper.get('name'),
- username: map.Active.Mapper.get('name'),
- image: map.Active.Mapper.get('image'),
- color: Util.getPastelColor(),
- self: true
- }
- self.localVideo.view.$container.find('.video-cutoff').css({
- border: '4px solid ' + self.activeMapper.color
- })
- self.setupLocalEvents()
- },
- setupChat: function() {
- const self = toExport
- map.ChatView.setNewMap()
- map.ChatView.addParticipant(self.activeMapper)
- map.ChatView.addMessages(new DataModel.MessageCollection(map.DataModel.Messages), true)
- },
- setupLocalEvents: function() {
- var self = toExport
- // local event listeners that trigger events
- $(document).on(map.JIT.events.zoom + '.map', self.positionPeerIcons)
- $(document).on(map.JIT.events.pan + '.map', self.positionPeerIcons)
- $(document).on('mousemove.map', function(event) {
- var pixels = {
- x: event.pageX,
- y: event.pageY
- }
- var coords = Util.pixelsToCoords(map.Visualize.mGraph, pixels)
- self.sendCoords(coords)
- })
- $(document).on(map.JIT.events.topicDrag + '.map', function(event, positions) {
- self.dragTopic(positions)
- })
- },
- countOthersInConversation: function() {
- var self = toExport
- var count = 0
- for (var key in self.mappersOnMap) {
- if (self.mappersOnMap[key].inConversation) count++
- }
- return count
- },
- handleVideoAdded: function(v, id) {
- var self = toExport
- self.positionVideos()
- v.setParent($('#wrapper'))
- v.$container.find('.video-cutoff').css({
- border: '4px solid ' + self.mappersOnMap[id].color
- })
- $('#wrapper').append(v.$container)
- },
- positionVideos: function() {
- var self = toExport
- var videoIds = Object.keys(self.room.videos)
- // var numOfVideos = videoIds.length
- // var numOfVideosToPosition = _.filter(videoIds, function(id) {
- // return !self.room.videos[id].manuallyPositioned
- // }).length
-
- var screenHeight = $(document).height()
- var topExtraPadding = 20
- var topPadding = 30
- var leftPadding = 30
- var videoHeight = 150
- var videoWidth = 180
- var column = 0
- var row = 0
- var yFormula = function() {
- var y = topExtraPadding + (topPadding + videoHeight) * row + topPadding
- if (y + videoHeight > screenHeight) {
- row = 0
- column += 1
- y = yFormula()
- }
- row++
- return y
- }
- var xFormula = function() {
- var x = (leftPadding + videoWidth) * column + leftPadding
- return x
- }
-
- // do self first
- var myVideo = toExport.localVideo.view
- if (!myVideo.manuallyPositioned) {
- myVideo.$container.css({
- top: yFormula() + 'px',
- left: xFormula() + 'px'
+ self.subscribeToEvents()
+ self.turnOn()
+ self.checkForCall()
+ self.joinMap()
+ },
+ addJuntoListeners: function() {
+ var self = toExport
+ $(document).on(ChatView.events.openTray, function() {
+ $('.main').addClass('compressed')
+ self.chatOpen = true
+ self.positionPeerIcons()
})
- }
- videoIds.forEach(function(id) {
- var video = self.room.videos[id]
- if (!video.manuallyPositioned) {
- video.$container.css({
+ $(document).on(ChatView.events.closeTray, function() {
+ $('.main').removeClass('compressed')
+ self.chatOpen = false
+ self.positionPeerIcons()
+ })
+ $(document).on(ChatView.events.videosOn, function() {
+ $('#wrapper').removeClass('hideVideos')
+ })
+ $(document).on(ChatView.events.videosOff, function() {
+ $('#wrapper').addClass('hideVideos')
+ })
+ $(document).on(ChatView.events.cursorsOn, function() {
+ $('#wrapper').removeClass('hideCursors')
+ })
+ $(document).on(ChatView.events.cursorsOff, function() {
+ $('#wrapper').addClass('hideCursors')
+ })
+ },
+ turnOn: function(notify) {
+ var self = toExport
+ $('.collabCompass').show()
+ self.activeMapper = {
+ id: map.Active.Mapper.id,
+ name: map.Active.Mapper.get('name'),
+ username: map.Active.Mapper.get('name'),
+ image: map.Active.Mapper.get('image'),
+ color: Util.getPastelColor(),
+ self: true
+ }
+ self.localVideo.view.$container.find('.video-cutoff').css({
+ border: '4px solid ' + self.activeMapper.color
+ })
+ self.setupLocalEvents()
+ },
+ setupChat: function() {
+ const self = toExport
+ map.ChatView.setNewMap()
+ map.ChatView.addParticipant(self.activeMapper)
+ map.ChatView.addMessages(new MessageCollection(map.DataModel.Messages), true)
+ },
+ setupLocalEvents: function() {
+ 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)
+ $(document).on('mousemove.map', function(event) {
+ var pixels = {
+ x: event.pageX,
+ y: event.pageY
+ }
+ var coords = Util.pixelsToCoords(map.Visualize.mGraph, pixels)
+ self.sendCoords(coords)
+ })
+ $(document).on(JIT.events.topicDrag + '.map', function(event, positions) {
+ self.dragTopic(positions)
+ })
+ },
+ subscribeToEvents: function() {
+ // todo scope these event listeners to map
+ const socket = Realtime.socket
+ socket.on(INVITED_TO_CALL, invitedToCall(toExport, map))
+ socket.on(INVITED_TO_JOIN, invitedToJoin(toExport, map))
+ socket.on(CALL_ACCEPTED, callAccepted(toExport, map))
+ socket.on(CALL_DENIED, callDenied(toExport, map))
+ socket.on(INVITE_DENIED, inviteDenied(toExport, map))
+ socket.on(CALL_IN_PROGRESS, callInProgress(toExport, map))
+ socket.on(CALL_STARTED, callStarted(toExport, map))
+ socket.on(MAPPER_LIST_UPDATED, mapperListUpdated(toExport, map))
+ socket.on(MAPPER_JOINED_CALL, mapperJoinedCall(toExport, map))
+ socket.on(MAPPER_LEFT_CALL, mapperLeftCall(toExport, map))
+ socket.on(PEER_COORDS_UPDATED, peerCoordsUpdated(toExport, map))
+ socket.on(NEW_MAPPER, newMapper(toExport, map))
+ socket.on(LOST_MAPPER, lostMapper(toExport, map))
+ socket.on(TOPIC_DRAGGED, topicDragged(toExport, map))
+ },
+ unsubscribeFromEvents: function() {
+ // todo scope these event listeners to map
+ const socket = Realtime.socket
+ socket.off(INVITED_TO_JOIN)
+ socket.off(CALL_ACCEPTED)
+ socket.off(INVITED_TO_CALL)
+ socket.off(CALL_DENIED)
+ socket.off(INVITE_DENIED)
+ socket.off(CALL_IN_PROGRESS)
+ socket.off(CALL_STARTED)
+ socket.off(MAPPER_LIST_UPDATED)
+ socket.off(MAPPER_JOINED_CALL)
+ socket.off(MAPPER_LEFT_CALL)
+ socket.off(PEER_COORDS_UPDATED)
+ socket.off(NEW_MAPPER)
+ socket.off(LOST_MAPPER)
+ socket.off(TOPIC_DRAGGED)
+ },
+ countOthersInConversation: function() {
+ var self = toExport
+ var count = 0
+ for (var key in self.mappersOnMap) {
+ if (self.mappersOnMap[key].inConversation) count++
+ }
+ return count
+ },
+ handleVideoAdded: function(v, id) {
+ var self = toExport
+ self.positionVideos()
+ v.setParent($('#wrapper'))
+ v.$container.find('.video-cutoff').css({
+ border: '4px solid ' + self.mappersOnMap[id].color
+ })
+ $('#wrapper').append(v.$container)
+ },
+ positionVideos: function() {
+ var self = toExport
+ var videoIds = Object.keys(self.room.videos)
+ // var numOfVideos = videoIds.length
+ // var numOfVideosToPosition = _.filter(videoIds, function(id) {
+ // return !self.room.videos[id].manuallyPositioned
+ // }).length
+
+ var screenHeight = $(document).height()
+ var topExtraPadding = 20
+ var topPadding = 30
+ var leftPadding = 30
+ var videoHeight = 150
+ var videoWidth = 180
+ var column = 0
+ var row = 0
+ var yFormula = function() {
+ var y = topExtraPadding + (topPadding + videoHeight) * row + topPadding
+ if (y + videoHeight > screenHeight) {
+ row = 0
+ column += 1
+ y = yFormula()
+ }
+ row++
+ return y
+ }
+ var xFormula = function() {
+ var x = (leftPadding + videoWidth) * column + leftPadding
+ return x
+ }
+
+ // do self first
+ var myVideo = toExport.localVideo.view
+ if (!myVideo.manuallyPositioned) {
+ myVideo.$container.css({
top: yFormula() + 'px',
left: xFormula() + 'px'
})
}
- })
- },
- callEnded: function() {
- var self = toExport
-
- map.ChatView.conversationEnded()
- self.room.leaveVideoOnly()
- self.inConversation = false
- self.localVideo.view.$container.hide().css({
- top: '72px',
- left: '30px'
- })
- self.localVideo.view.audioOn()
- self.localVideo.view.videoOn()
- },
- createCompass: function(name, id, image, color) {
- var str = '' + name + '
'
- str += '
'
- $('#compass' + id).remove()
- $('
', {
- id: 'compass' + id,
- class: 'collabCompass'
- }).html(str).appendTo('#wrapper')
- $('#compass' + id + ' img').css({
- 'border': '2px solid ' + color
- })
- $('#compass' + id + ' p').css({
- 'background-color': color
- })
- },
- positionPeerIcons: function() {
- var self = toExport
- for (var key in self.mappersOnMap) {
- self.positionPeerIcon(key)
- }
- },
- positionPeerIcon: function(id) {
- var self = toExport
- var mapper = self.mappersOnMap[id]
-
- var origPixels = Util.coordsToPixels(map.Visualize.mGraph, mapper.coords)
- var pixels = self.limitPixelsToScreen(origPixels)
- $('#compass' + id).css({
- left: pixels.x + 'px',
- top: pixels.y + 'px'
- })
- /* showing the arrow if the collaborator is off of the viewport screen */
- if (origPixels.x !== pixels.x || origPixels.y !== pixels.y) {
- var dy = origPixels.y - pixels.y // opposite
- var dx = origPixels.x - pixels.x // adjacent
- var angle = Math.atan2(dy, dx)
-
- $('#compassArrow' + id).show().css({
- transform: 'rotate(' + angle + 'rad)',
- '-webkit-transform': 'rotate(' + angle + 'rad)'
+ videoIds.forEach(function(id) {
+ var video = self.room.videos[id]
+ if (!video.manuallyPositioned) {
+ video.$container.css({
+ top: yFormula() + 'px',
+ left: xFormula() + 'px'
+ })
+ }
})
+ },
+ callEnded: function() {
+ var self = toExport
- if (dx > 0) {
- $('#compass' + id).addClass('labelLeft')
+ map.ChatView.conversationEnded()
+ self.room.leaveVideoOnly()
+ self.inConversation = false
+ self.localVideo.view.$container.hide().css({
+ top: '72px',
+ left: '30px'
+ })
+ self.localVideo.view.audioOn()
+ self.localVideo.view.videoOn()
+ },
+ createCompass: function(name, id, image, color) {
+ var str = '' + name + '
'
+ str += '
'
+ $('#compass' + id).remove()
+ $('
', {
+ id: 'compass' + id,
+ class: 'collabCompass'
+ }).html(str).appendTo('#wrapper')
+ $('#compass' + id + ' img').css({
+ 'border': '2px solid ' + color
+ })
+ $('#compass' + id + ' p').css({
+ 'background-color': color
+ })
+ },
+ positionPeerIcons: function() {
+ var self = toExport
+ for (var key in self.mappersOnMap) {
+ self.positionPeerIcon(key)
}
- } else {
- $('#compassArrow' + id).hide()
- $('#compass' + id).removeClass('labelLeft')
+ },
+ positionPeerIcon: function(id) {
+ var self = toExport
+ var mapper = self.mappersOnMap[id]
+
+ var origPixels = Util.coordsToPixels(map.Visualize.mGraph, mapper.coords)
+ var pixels = self.limitPixelsToScreen(origPixels)
+ $('#compass' + id).css({
+ left: pixels.x + 'px',
+ top: pixels.y + 'px'
+ })
+ /* showing the arrow if the collaborator is off of the viewport screen */
+ if (origPixels.x !== pixels.x || origPixels.y !== pixels.y) {
+ var dy = origPixels.y - pixels.y // opposite
+ var dx = origPixels.x - pixels.x // adjacent
+ var angle = Math.atan2(dy, dx)
+
+ $('#compassArrow' + id).show().css({
+ transform: 'rotate(' + angle + 'rad)',
+ '-webkit-transform': 'rotate(' + angle + 'rad)'
+ })
+
+ if (dx > 0) {
+ $('#compass' + id).addClass('labelLeft')
+ }
+ } else {
+ $('#compassArrow' + id).hide()
+ $('#compass' + id).removeClass('labelLeft')
+ }
+ },
+ limitPixelsToScreen: function(pixels) {
+ var self = toExport
+
+ var boundary = self.chatOpen ? '#wrapper' : document
+ var xLimit, yLimit
+ var xMax = $(boundary).width()
+ var yMax = $(boundary).height()
+ var compassDiameter = 56
+ var compassArrowSize = 24
+
+ xLimit = Math.max(0 + compassArrowSize, pixels.x)
+ xLimit = Math.min(xLimit, xMax - compassDiameter)
+ yLimit = Math.max(0 + compassArrowSize, pixels.y)
+ yLimit = Math.min(yLimit, yMax - compassDiameter)
+
+ return {x: xLimit, y: yLimit}
}
- },
- limitPixelsToScreen: function(pixels) {
- var self = toExport
-
- var boundary = self.chatOpen ? '#wrapper' : document
- var xLimit, yLimit
- var xMax = $(boundary).width()
- var yMax = $(boundary).height()
- var compassDiameter = 56
- var compassArrowSize = 24
-
- xLimit = Math.max(0 + compassArrowSize, pixels.x)
- xLimit = Math.min(xLimit, xMax - compassDiameter)
- yLimit = Math.max(0 + compassArrowSize, pixels.y)
- yLimit = Math.min(yLimit, yMax - compassDiameter)
-
- return {x: xLimit, y: yLimit}
}
+ return toExport
}
-const sendables = [
- ['joinMap', joinMap],
- ['leaveMap', leaveMap],
- ['checkForCall', checkForCall],
- ['acceptCall', acceptCall],
- ['denyCall', denyCall],
- ['denyInvite', denyInvite],
- ['inviteToJoin', inviteToJoin],
- ['inviteACall', inviteACall],
- ['joinCall', joinCall],
- ['leaveCall', leaveCall],
- ['sendMapperInfo', sendMapperInfo],
- ['sendCoords', sendCoords],
- ['dragTopic', dragTopic]
-]
-sendables.forEach(sendable => {
- toExport[sendable[0]] = sendable[1](toExport, map)
-})
-
-const subscribeToEvents = (toExport, socket) => {
- socket.on(JUNTO_UPDATED, juntoUpdated(toExport, map))
- socket.on(INVITED_TO_CALL, invitedToCall(toExport, map))
- socket.on(INVITED_TO_JOIN, invitedToJoin(toExport, map))
- socket.on(CALL_ACCEPTED, callAccepted(toExport, map))
- socket.on(CALL_DENIED, callDenied(toExport, map))
- socket.on(INVITE_DENIED, inviteDenied(toExport, map))
- socket.on(CALL_IN_PROGRESS, callInProgress(toExport, map))
- socket.on(CALL_STARTED, callStarted(toExport, map))
- socket.on(MAPPER_LIST_UPDATED, mapperListUpdated(toExport, map))
- socket.on(MAPPER_JOINED_CALL, mapperJoinedCall(toExport, map))
- socket.on(MAPPER_LEFT_CALL, mapperLeftCall(toExport, map))
- socket.on(PEER_COORDS_UPDATED, peerCoordsUpdated(toExport, map))
- socket.on(NEW_MAPPER, newMapper(toExport, map))
- socket.on(LOST_MAPPER, lostMapper(toExport, map))
- socket.on(TOPIC_DRAGGED, topicDragged(toExport, map))
-}
-return toExport
+Realtime.init = function(serverData) {
+ var self = Realtime
+ self.socket = new SocketIoConnection({
+ url: serverData['REALTIME_SERVER'],
+ socketio: {
+ // don't poll forever if in development
+ reconnectionAttempts: serverData.RAILS_ENV === 'development' ? 5 : Infinity
+ }
+ })
+ self['junto_spinner_darkgrey.gif'] = serverData['junto_spinner_darkgrey.gif']
+ self.socket.on('connect', function() {
+ console.log('connected')
+ self.socket.on(JUNTO_UPDATED, (state) => {
+ ReactApp.juntoState = state
+ ReactApp.render()
+ })
+ self.disconnected = false
+ ReactApp.openMap && ReactApp.openMap.Realtime.onSocketConnect()
+ })
+ self.socket.on('disconnect', function() {
+ self.disconnected = true
+ })
}
diff --git a/frontend/src/Metamaps/Map/Realtime/receivable.js b/frontend/src/Metamaps/Map/Realtime/receivable.js
index 7fbe4075..b68f4c91 100644
--- a/frontend/src/Metamaps/Map/Realtime/receivable.js
+++ b/frontend/src/Metamaps/Map/Realtime/receivable.js
@@ -4,16 +4,9 @@
everthing in this file happens as a result of websocket events
*/
-import { JUNTO_UPDATED } from './events'
-
-import GlobalUI, { ReactApp } from '../../GlobalUI'
+import GlobalUI from '../../GlobalUI'
import Util from '../../Util'
-export const juntoUpdated = (self, map) => state => {
- ReactApp.juntoState = state
- $(document).trigger(JUNTO_UPDATED)
-}
-
/* All the following events are received through the nodejs realtime server
and are done this way because they are transient data, not persisted to the server */
export const topicDragged = (self, map) => positions => {
diff --git a/frontend/src/Metamaps/Map/Realtime/sendable.js b/frontend/src/Metamaps/Map/Realtime/sendable.js
index bafd353c..f8322bed 100644
--- a/frontend/src/Metamaps/Map/Realtime/sendable.js
+++ b/frontend/src/Metamaps/Map/Realtime/sendable.js
@@ -18,8 +18,8 @@ import {
DRAG_TOPIC
} from './events'
-export const joinMap = (self, map) => () => {
- self.socket.emit(JOIN_MAP, {
+export const joinMap = (socket, self, map) => () => {
+ socket.emit(JOIN_MAP, {
userid: map.Active.Mapper.id,
username: map.Active.Mapper.get('name'),
avatar: map.Active.Mapper.get('image'),
@@ -28,15 +28,15 @@ export const joinMap = (self, map) => () => {
})
}
-export const leaveMap = (self, map) => () => {
- self.socket.emit(LEAVE_MAP)
+export const leaveMap = (socket, self, map) => () => {
+ socket.emit(LEAVE_MAP)
}
-export const checkForCall = (self, map) => () => {
- self.socket.emit(CHECK_FOR_CALL, { room: self.room.room, mapid: map.Active.Map.id })
+export const checkForCall = (socket, self, map) => () => {
+ socket.emit(CHECK_FOR_CALL, { room: self.room.room, mapid: map.Active.Map.id })
}
-export const sendMapperInfo = (self, map) => userid => {
+export const sendMapperInfo = (socket, self, map) => userid => {
// send this new mapper back your details, and the awareness that you've loaded the map
var update = {
userToNotify: userid,
@@ -46,10 +46,10 @@ export const sendMapperInfo = (self, map) => userid => {
userinconversation: self.inConversation,
mapid: map.Active.Map.id
}
- self.socket.emit(SEND_MAPPER_INFO, update)
+ socket.emit(SEND_MAPPER_INFO, update)
}
-export const joinCall = (self, map) => () => {
+export const joinCall = (socket, self, map) => () => {
self.webrtc.off('readyToCall')
self.webrtc.once('readyToCall', function() {
self.videoInitialized = true
@@ -64,7 +64,7 @@ export const joinCall = (self, map) => () => {
map.ChatView.conversationInProgress(true)
})
self.inConversation = true
- self.socket.emit(JOIN_CALL, {
+ socket.emit(JOIN_CALL, {
mapid: map.Active.Map.id,
id: map.Active.Mapper.id
})
@@ -73,8 +73,8 @@ export const joinCall = (self, map) => () => {
map.ChatView.mapperJoinedCall(map.Active.Mapper.id)
}
-export const leaveCall = (self, map) => () => {
- self.socket.emit(LEAVE_CALL, {
+export const leaveCall = (socket, self, map) => () => {
+ socket.emit(LEAVE_CALL, {
mapid: map.Active.Map.id,
id: map.Active.Mapper.id
})
@@ -92,9 +92,9 @@ export const leaveCall = (self, map) => () => {
}
}
-export const acceptCall = (self, map) => userid => {
+export const acceptCall = (socket, self, map) => userid => {
map.ChatView.sound.stop(self.soundId)
- self.socket.emit(ACCEPT_CALL, {
+ socket.emit(ACCEPT_CALL, {
mapid: map.Active.Map.id,
invited: map.Active.Mapper.id,
inviter: userid
@@ -104,9 +104,9 @@ export const acceptCall = (self, map) => userid => {
GlobalUI.clearNotify()
}
-export const denyCall = (self, map) => userid => {
+export const denyCall = (socket, self, map) => userid => {
map.ChatView.sound.stop(self.soundId)
- self.socket.emit(DENY_CALL, {
+ socket.emit(DENY_CALL, {
mapid: map.Active.Map.id,
invited: map.Active.Mapper.id,
inviter: userid
@@ -114,9 +114,9 @@ export const denyCall = (self, map) => userid => {
GlobalUI.clearNotify()
}
-export const denyInvite = (self, map) => userid => {
+export const denyInvite = (socket, self, map) => userid => {
map.ChatView.sound.stop(self.soundId)
- self.socket.emit(DENY_INVITE, {
+ socket.emit(DENY_INVITE, {
mapid: map.Active.Map.id,
invited: map.Active.Mapper.id,
inviter: userid
@@ -124,8 +124,8 @@ export const denyInvite = (self, map) => userid => {
GlobalUI.clearNotify()
}
-export const inviteACall = (self, map) => userid => {
- self.socket.emit(INVITE_A_CALL, {
+export const inviteACall = (socket, self, map) => userid => {
+ socket.emit(INVITE_A_CALL, {
mapid: map.Active.Map.id,
inviter: map.Active.Mapper.id,
invited: userid
@@ -134,8 +134,8 @@ export const inviteACall = (self, map) => userid => {
GlobalUI.clearNotify()
}
-export const inviteToJoin = (self, map) => userid => {
- self.socket.emit(INVITE_TO_JOIN, {
+export const inviteToJoin = (socket, self, map) => userid => {
+ socket.emit(INVITE_TO_JOIN, {
mapid: map.Active.Map.id,
inviter: map.Active.Mapper.id,
invited: userid
@@ -143,22 +143,22 @@ export const inviteToJoin = (self, map) => userid => {
map.ChatView.invitationPending(userid)
}
-export const sendCoords = (self, map) => coords => {
- var map = map.Active.Map
+export const sendCoords = (socket, self, map) => coords => {
+ var m = map.Active.Map
var mapper = map.Active.Mapper
- if (map && map.authorizeToEdit(mapper)) {
+ if (m && m.authorizeToEdit(mapper)) {
var update = {
usercoords: coords,
userid: map.Active.Mapper.id,
- mapid: map.Active.Map.id
+ mapid: m.id
}
- self.socket.emit(SEND_COORDS, update)
+ socket.emit(SEND_COORDS, update)
}
}
-export const dragTopic = (self, map) => positions => {
+export const dragTopic = (socket, self, map) => positions => {
if (map.Active.Map) {
positions.mapid = map.Active.Map.id
- self.socket.emit(DRAG_TOPIC, positions)
+ socket.emit(DRAG_TOPIC, positions)
}
}
diff --git a/frontend/src/Metamaps/Map/Views/Room.js b/frontend/src/Metamaps/Map/Views/Room.js
index 03548c7f..5d2008d1 100644
--- a/frontend/src/Metamaps/Map/Views/Room.js
+++ b/frontend/src/Metamaps/Map/Views/Room.js
@@ -7,7 +7,6 @@ import VideoView from './VideoView'
const Room = function(opts = {}) {
this.isActiveRoom = false
- this.socket = opts.socket
this.webrtc = opts.webrtc
this.room = opts.room
this.config = opts.config
diff --git a/frontend/src/Metamaps/Map/Views/index.js b/frontend/src/Metamaps/Map/Views/index.js
index a76b6527..5bf34cf8 100644
--- a/frontend/src/Metamaps/Map/Views/index.js
+++ b/frontend/src/Metamaps/Map/Views/index.js
@@ -2,13 +2,8 @@
import VideoView from './VideoView'
import Room from './Room'
-import { JUNTO_UPDATED } from '../Realtime/events'
const Views = {
- init: (serverData) => {
- $(document).on(JUNTO_UPDATED, () => ExploreMaps.render())
- //map.ChatView.init([serverData['sounds/MM_sounds.mp3'], serverData['sounds/MM_sounds.ogg']])
- },
VideoView,
Room
}
diff --git a/frontend/src/Metamaps/Map/index.js b/frontend/src/Metamaps/Map/index.js
index 41552dd7..87045073 100644
--- a/frontend/src/Metamaps/Map/index.js
+++ b/frontend/src/Metamaps/Map/index.js
@@ -91,8 +91,8 @@ const mapControl = {
newMap.Active.Mapper = ReactApp.currentUser
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.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
@@ -127,15 +127,15 @@ const mapControl = {
end: function(map) {
$('.main').removeClass('compressed')
$('.rightclickmenu').remove()
- map.AutoLayout.resetSpiral()
- map.TopicCard.hideCard()
- map.map.SynapseCard.hideCard()
+ //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.Map.requests = []
+ //map.Map.hasLearnedTopicCreation = true
map.Realtime.endActiveMap()
- map.Map.requests = []
- map.Map.hasLearnedTopicCreation = true
}
}
export { mapControl }
@@ -442,6 +442,9 @@ const Map = (map) => {
Map.events = {
editedByActiveMapper: 'Metamaps:Map:events:editedByActiveMapper'
}
+Map.init = (serverData) => {
+ ChatView.init([serverData['sounds/MM_sounds.mp3'], serverData['sounds/MM_sounds.ogg']])
+}
export { CheatSheet }
export default Map
diff --git a/frontend/src/Metamaps/index.js b/frontend/src/Metamaps/index.js
index 8769071d..7cd8f768 100644
--- a/frontend/src/Metamaps/index.js
+++ b/frontend/src/Metamaps/index.js
@@ -58,7 +58,6 @@ Metamaps.Synapse = Synapse
Metamaps.SynapseCard = SynapseCard
Metamaps.Topic = Topic
Metamaps.Util = Util
-Metamaps.Views = Views
Metamaps.Visualize = Visualize
Metamaps.GlobalUI = GlobalUI
@@ -76,7 +75,6 @@ document.addEventListener('DOMContentLoaded', function() {
Metamaps[prop].hasOwnProperty('init') &&
typeof (Metamaps[prop].init) === 'function'
) {
- console.log(prop)
Metamaps[prop].init(Metamaps.ServerData)
}
}
diff --git a/frontend/src/components/MapView/MapChat/index.js b/frontend/src/components/MapView/MapChat/index.js
index 64b7b10e..a78d6eb8 100644
--- a/frontend/src/components/MapView/MapChat/index.js
+++ b/frontend/src/components/MapView/MapChat/index.js
@@ -59,7 +59,7 @@ class MapChat extends Component {
scroll = () => {
// hack: figure out how to do this right
- this.messagesDiv.scrollTop = this.messagesDiv.scrollHeight + 100
+ if (this.messagesDiv) this.messagesDiv.scrollTop = this.messagesDiv.scrollHeight + 100
}
toggleDrawer = () => {
diff --git a/realtime/global.js b/realtime/global.js
index f7d372e6..aaba0753 100644
--- a/realtime/global.js
+++ b/realtime/global.js
@@ -7,7 +7,7 @@ const {
LEAVE_CALL,
JOIN_MAP,
LEAVE_MAP
-} = require('../frontend/src/Metamaps/Realtime/events')
+} = require('../frontend/src/Metamaps/Map/Realtime/events')
module.exports = function(io, store) {
store.subscribe(() => {
diff --git a/realtime/junto.js b/realtime/junto.js
index a361c1e3..c05649aa 100644
--- a/realtime/junto.js
+++ b/realtime/junto.js
@@ -17,7 +17,7 @@ const {
INVITE_A_CALL,
JOIN_CALL,
LEAVE_CALL
-} = require('../frontend/src/Metamaps/Realtime/events')
+} = require('../frontend/src/Metamaps/Map/Realtime/events')
const { mapRoom, userMapRoom } = require('./rooms')
diff --git a/realtime/map.js b/realtime/map.js
index 9d8a1582..c0ded25c 100644
--- a/realtime/map.js
+++ b/realtime/map.js
@@ -10,7 +10,7 @@ const {
SEND_COORDS,
SEND_MAPPER_INFO,
DRAG_TOPIC
-} = require('../frontend/src/Metamaps/Realtime/events')
+} = require('../frontend/src/Metamaps/Map/Realtime/events')
const { mapRoom, userMapRoom } = require('./rooms')
diff --git a/realtime/reducer.js b/realtime/reducer.js
index 9b50d3a0..423f8dd5 100644
--- a/realtime/reducer.js
+++ b/realtime/reducer.js
@@ -4,7 +4,7 @@ const {
LEAVE_MAP,
JOIN_CALL,
LEAVE_CALL
-} = require('../frontend/src/Metamaps/Realtime/events')
+} = require('../frontend/src/Metamaps/Map/Realtime/events')
const NOT_IN_CONVERSATION = 0
const IN_CONVERSATION = 1