Compare commits
38 commits
develop
...
feature/co
Author | SHA1 | Date | |
---|---|---|---|
|
f49a674566 | ||
|
7951f08e45 | ||
|
9c65d2df09 | ||
|
a84815aaed | ||
|
9b0ead24a3 | ||
|
3a4fa90c49 | ||
|
0a52de714e | ||
|
597efb36e1 | ||
|
72fd2717b6 | ||
|
500a74bd5f | ||
|
2423608fd3 | ||
|
0929380e91 | ||
|
6e347dc33a | ||
|
96f66a2f8c | ||
|
74dd20f02e | ||
|
04036882ab | ||
|
fd54eb718a | ||
|
ba230e1eed | ||
|
274b86532a | ||
|
6ed9796e05 | ||
|
08d2cbb00d | ||
|
fc2604376a | ||
|
e7a52dc14e | ||
|
149b7ecbd6 | ||
|
b2b5090b28 | ||
|
38c01c4e8f | ||
|
b13ac98c9f | ||
|
4a17d00123 | ||
|
9c13f5a281 | ||
|
616a489ae4 | ||
|
816815d1b5 | ||
|
3ff102b228 | ||
|
bf9b25da9f | ||
|
5ba7ba9355 | ||
|
ce7c88c78c | ||
|
74630c2631 | ||
|
966dd79187 | ||
|
5d04d16590 |
23 changed files with 812 additions and 580 deletions
|
@ -22,7 +22,7 @@ class MapPolicy < ApplicationPolicy
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversation?
|
def conversation?
|
||||||
show? && %w(connorturland@gmail.com devin@callysto.com chessscholar@gmail.com solaureum@gmail.com ishanshapiro@gmail.com).include?(user.email)
|
show? && %w(admin@admin.com user@user.com connorturland@gmail.com devin@callysto.com chessscholar@gmail.com solaureum@gmail.com ishanshapiro@gmail.com).include?(user.email)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create?
|
def create?
|
||||||
|
|
58
frontend/src/ConvoAlgo/exampleObject.js
Normal file
58
frontend/src/ConvoAlgo/exampleObject.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
|
||||||
|
// if we've placed a node into an island, we need to NOT place it in any other islands
|
||||||
|
// Every node should only appear in one island
|
||||||
|
|
||||||
|
// the top level array represents islands
|
||||||
|
// every island has some sort of 'focal' node
|
||||||
|
/*
|
||||||
|
var example = [
|
||||||
|
// the island that contains the focal node
|
||||||
|
{
|
||||||
|
id: 21,
|
||||||
|
parents: [
|
||||||
|
{
|
||||||
|
id: 25,
|
||||||
|
parents: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 25,
|
||||||
|
parents: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
children: [{
|
||||||
|
id: 26,
|
||||||
|
children: []
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
// all other islands should not contain children on the top level node
|
||||||
|
{
|
||||||
|
id: 21,
|
||||||
|
// parents may contain children
|
||||||
|
parents: [
|
||||||
|
{
|
||||||
|
id: 100,
|
||||||
|
parents: [
|
||||||
|
{
|
||||||
|
id: 101,
|
||||||
|
parents: [],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 103,
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 102,
|
||||||
|
parents: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 21,
|
||||||
|
parents: []
|
||||||
|
},
|
||||||
|
]
|
||||||
|
*/
|
223
frontend/src/ConvoAlgo/index.js
Normal file
223
frontend/src/ConvoAlgo/index.js
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
import { findIndex, orderBy } from 'lodash'
|
||||||
|
|
||||||
|
/*
|
||||||
|
step 1
|
||||||
|
generate an object/array that represents the intended layout
|
||||||
|
|
||||||
|
|
||||||
|
step 2
|
||||||
|
generate x,y coordinates for every topic in the layout object
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// synapses = [{ topic1_id: 4, topic2_id: 5, direction: 'from-to', desc: 'has reply' }]
|
||||||
|
|
||||||
|
const isEven = n => n % 2 === 0
|
||||||
|
const isOdd = n => Math.abs(n % 2) === 1
|
||||||
|
|
||||||
|
export const X_GRID_SPACE = 250
|
||||||
|
export const Y_GRID_SPACE = 200
|
||||||
|
export const ISLAND_SPACING = 300
|
||||||
|
|
||||||
|
export const generateLayoutObject = (topics, synapses, focalTopicId) => {
|
||||||
|
let layout = [] // will be the final output
|
||||||
|
const usedTopics = {} // will store the topics that have been placed into islands
|
||||||
|
let newRoot
|
||||||
|
let currentTopic
|
||||||
|
|
||||||
|
const addParentsAndChildren = (topic, getParents, getChildren, degreeFromFocus) => {
|
||||||
|
if (!topic.id) return topic
|
||||||
|
|
||||||
|
usedTopics[topic.id] = true
|
||||||
|
topic.degreeFromFocus = degreeFromFocus
|
||||||
|
const nextDegree = degreeFromFocus + 1
|
||||||
|
|
||||||
|
if (getChildren) {
|
||||||
|
topic.children = []
|
||||||
|
synapses.filter(synapse => {
|
||||||
|
return synapse.topic1_id === topic.id
|
||||||
|
&& !usedTopics[synapse.topic2_id]
|
||||||
|
&& synapse.category === 'from-to'
|
||||||
|
})
|
||||||
|
.map(synapse => synapse.topic2_id)
|
||||||
|
.forEach(childId => topic.children.push(addParentsAndChildren({id: childId}, false, true, nextDegree)))
|
||||||
|
|
||||||
|
topic.children = orderBy(topic.children, 'maxDescendants', 'desc')
|
||||||
|
topic.maxDescendants = topic.children.length ? topic.children[0].maxDescendants + 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getParents) {
|
||||||
|
topic.parents = []
|
||||||
|
synapses.filter(synapse => {
|
||||||
|
return synapse.topic2_id === topic.id
|
||||||
|
&& !usedTopics[synapse.topic1_id]
|
||||||
|
&& synapse.category === 'from-to'
|
||||||
|
})
|
||||||
|
.map(synapse => synapse.topic1_id)
|
||||||
|
.forEach(parentId => topic.parents.push(addParentsAndChildren({id: parentId}, true, false, nextDegree)))
|
||||||
|
|
||||||
|
topic.parents = orderBy(topic.parents, 'maxAncestors', 'desc')
|
||||||
|
topic.maxAncestors = topic.parents.length ? topic.parents[0].maxAncestors + 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getParents && getChildren) {
|
||||||
|
topic.longestThread = topic.maxDescendants + topic.maxAncestors + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return topic
|
||||||
|
}
|
||||||
|
|
||||||
|
// start with the focal node, and build its island
|
||||||
|
currentTopic = topics.find(t => t.id === focalTopicId)
|
||||||
|
if (!currentTopic) {
|
||||||
|
console.log('you didnt pass a valid focalTopicId')
|
||||||
|
return layout
|
||||||
|
}
|
||||||
|
newRoot = {
|
||||||
|
id: currentTopic.id
|
||||||
|
}
|
||||||
|
layout.push(addParentsAndChildren(newRoot, true, true, 0))
|
||||||
|
|
||||||
|
// right now there's no reasoning going on about the selection of focal topics
|
||||||
|
// its just whichever ones happen to be found in the array first
|
||||||
|
topics.forEach(topic => {
|
||||||
|
if (topic && topic.id && !usedTopics[topic.id]) {
|
||||||
|
newRoot = {
|
||||||
|
id: topic.id
|
||||||
|
}
|
||||||
|
layout.push(addParentsAndChildren(newRoot, true, true, 0))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return layout
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const generateObjectCoordinates = (layoutObject, focalTopicId, focalCoords) => {
|
||||||
|
const coords = {}
|
||||||
|
|
||||||
|
const traverseIsland = (island, func, parent, child) => {
|
||||||
|
func(island, parent, child)
|
||||||
|
if (island.parents) {
|
||||||
|
island.parents.forEach(p => traverseIsland(p, func, null, island))
|
||||||
|
}
|
||||||
|
if (island.children) {
|
||||||
|
island.children.forEach(c => traverseIsland(c, func, island, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const myFunction = n => n*5
|
||||||
|
|
||||||
|
// myFunction(2) === 10
|
||||||
|
|
||||||
|
const positionTopic = tempPosStore => (topic, parent, child) => {
|
||||||
|
let pos = {}
|
||||||
|
|
||||||
|
const getYValueForX = (x, attempt = 0) => {
|
||||||
|
tempPosStore[x] = tempPosStore[x] || {}
|
||||||
|
let yValue
|
||||||
|
let relationSign
|
||||||
|
let indexOfTopic
|
||||||
|
let relation = parent || child
|
||||||
|
let arrayOfTopics = parent ? parent.children : (child ? child.parents : [])
|
||||||
|
|
||||||
|
// first figure out what you'd like it to be
|
||||||
|
// then figure out if that spot's taken
|
||||||
|
// and if it is then call this function again with another attempt
|
||||||
|
|
||||||
|
// after the focal topic only, ODD indexes will move negatively on the Y axis
|
||||||
|
// and EVEN indexes will move positively on the Y axis
|
||||||
|
|
||||||
|
// for everything beyond the direct parents and children of the focal topic
|
||||||
|
// maintain the positivity or negativity on the Y axis of its parent or child
|
||||||
|
|
||||||
|
if (!relation) yValue = 0
|
||||||
|
else if (attempt === 0) yValue = coords[relation.id].y
|
||||||
|
else if (attempt > 0) {
|
||||||
|
// if the relations sign is 0, alternate between putting this topic into the upper and lower quadrants
|
||||||
|
if (coords[relation.id].y === 0) {
|
||||||
|
indexOfTopic = findIndex(arrayOfTopics, t => t.id === topic.id)
|
||||||
|
relationSign = isOdd(indexOfTopic) ? 1 : -1
|
||||||
|
} else {
|
||||||
|
// if the quadrant of the related topic is already decided, make sure to keep it
|
||||||
|
relationSign = coords[relation.id].y > 0 ? 1 : -1
|
||||||
|
}
|
||||||
|
yValue = coords[relation.id].y + (Y_GRID_SPACE * attempt * relationSign)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempPosStore[x][yValue]) yValue = getYValueForX(x, attempt + 1)
|
||||||
|
tempPosStore[x][yValue] = true
|
||||||
|
return yValue
|
||||||
|
}
|
||||||
|
|
||||||
|
pos.x = topic.degreeFromFocus * X_GRID_SPACE * (parent ? 1 : -1),
|
||||||
|
pos.y = getYValueForX(pos.x)
|
||||||
|
coords[topic.id] = pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// lay all of them out as if there were no other ones
|
||||||
|
layoutObject.forEach((island, index) => {
|
||||||
|
const tempPosStore = {}
|
||||||
|
if (index === 0) {
|
||||||
|
tempPosStore[X_GRID_SPACE] = {
|
||||||
|
0: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traverseIsland(island, positionTopic(tempPosStore))
|
||||||
|
})
|
||||||
|
|
||||||
|
// calculate the bounds of each island
|
||||||
|
const islandBoundArray= []
|
||||||
|
const adjustBounds = islandBounds => (topic, parent, child) => {
|
||||||
|
const relation = parent || child
|
||||||
|
if (!relation) return
|
||||||
|
islandBounds.minX = Math.min(islandBounds.minX, coords[topic.id].x)
|
||||||
|
islandBounds.maxX = Math.max(islandBounds.maxX, coords[topic.id].x)
|
||||||
|
islandBounds.minY = Math.min(islandBounds.minY, coords[topic.id].y)
|
||||||
|
islandBounds.maxY = Math.max(islandBounds.maxY, coords[topic.id].y)
|
||||||
|
}
|
||||||
|
layoutObject.forEach(island => {
|
||||||
|
const islandBounds = {
|
||||||
|
minX: coords[island.id].x,
|
||||||
|
maxX: coords[island.id].x,
|
||||||
|
minY: coords[island.id].y,
|
||||||
|
maxY: coords[island.id].y
|
||||||
|
}
|
||||||
|
islandBoundArray.push(islandBounds)
|
||||||
|
traverseIsland(island, adjustBounds(islandBounds))
|
||||||
|
})
|
||||||
|
|
||||||
|
// reposition the islands according to the bounds
|
||||||
|
const translateIsland = (island, x, y) => {
|
||||||
|
const adjustTopicPos = topic => {
|
||||||
|
coords[topic.id].x = coords[topic.id].x + x
|
||||||
|
coords[topic.id].y = coords[topic.id].y + y
|
||||||
|
}
|
||||||
|
traverseIsland(island, adjustTopicPos)
|
||||||
|
}
|
||||||
|
let maxYForIslands = 0 // the highest Y value that has thus been placed
|
||||||
|
let minYForIslands = 0 // the lowest Y value that has thus been placed
|
||||||
|
layoutObject.forEach((island, index) => {
|
||||||
|
let translateY
|
||||||
|
const islandHeight = islandBoundArray[index].maxY - islandBoundArray[index].minY
|
||||||
|
if (index === 0) {
|
||||||
|
translateIsland(island, focalCoords.x, focalCoords.y) // position the selected island to where the user has it already
|
||||||
|
maxYForIslands = focalCoords.y + islandBoundArray[0].maxY
|
||||||
|
minYForIslands = focalCoords.y + islandBoundArray[0].minY
|
||||||
|
}
|
||||||
|
else if (isOdd(index)) {
|
||||||
|
translateIsland(island, focalCoords.x - islandBoundArray[index].maxX, maxYForIslands + ISLAND_SPACING + Math.abs(islandBoundArray[index].minY))
|
||||||
|
maxYForIslands = maxYForIslands + ISLAND_SPACING + islandHeight
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
translateIsland(island, focalCoords.x - islandBoundArray[index].maxX, minYForIslands - ISLAND_SPACING - islandBoundArray[index].maxY)
|
||||||
|
minYForIslands = minYForIslands - ISLAND_SPACING - islandHeight
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return coords
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLayoutForData = (topics, synapses, focalTopicId, focalCoords) => {
|
||||||
|
return generateObjectCoordinates(generateLayoutObject(topics, synapses, focalTopicId), focalTopicId, focalCoords)
|
||||||
|
}
|
|
@ -4,7 +4,9 @@ import { indexOf } from 'lodash'
|
||||||
|
|
||||||
import Active from './Active'
|
import Active from './Active'
|
||||||
import Control from './Control'
|
import Control from './Control'
|
||||||
|
import Create from './Create'
|
||||||
import DataModel from './DataModel'
|
import DataModel from './DataModel'
|
||||||
|
import Engine from './Engine'
|
||||||
import Map from './Map'
|
import Map from './Map'
|
||||||
import Mapper from './Mapper'
|
import Mapper from './Mapper'
|
||||||
import Synapse from './Synapse'
|
import Synapse from './Synapse'
|
||||||
|
@ -28,7 +30,7 @@ const Cable = {
|
||||||
},
|
},
|
||||||
unsubscribeFromMap: () => {
|
unsubscribeFromMap: () => {
|
||||||
let self = Cable
|
let self = Cable
|
||||||
self.sub.unsubscribe()
|
self.sub && self.sub.unsubscribe()
|
||||||
delete self.sub
|
delete self.sub
|
||||||
},
|
},
|
||||||
synapseAdded: event => {
|
synapseAdded: event => {
|
||||||
|
@ -44,15 +46,15 @@ const Cable = {
|
||||||
if (t1.authorizeToShow(m) && t2.authorizeToShow(m) && s.authorizeToShow(m) && !DataModel.Synapses.get(event.synapse.id)) {
|
if (t1.authorizeToShow(m) && t2.authorizeToShow(m) && s.authorizeToShow(m) && !DataModel.Synapses.get(event.synapse.id)) {
|
||||||
// refactor the heck outta this, its adding wicked wait time
|
// refactor the heck outta this, its adding wicked wait time
|
||||||
var topic1, topic2, node1, node2, synapse, mapping, cancel, mapper
|
var topic1, topic2, node1, node2, synapse, mapping, cancel, mapper
|
||||||
|
|
||||||
const waitThenRenderSynapse = () => {
|
const waitThenRenderSynapse = () => {
|
||||||
if (synapse && mapping && mapper) {
|
if (synapse && mapping && mapper && synapse.getTopic1() && synapse.getTopic2()) {
|
||||||
topic1 = synapse.getTopic1()
|
topic1 = synapse.getTopic1()
|
||||||
node1 = topic1.get('node')
|
node1 = topic1.get('node')
|
||||||
topic2 = synapse.getTopic2()
|
topic2 = synapse.getTopic2()
|
||||||
node2 = topic2.get('node')
|
node2 = topic2.get('node')
|
||||||
|
Synapse.renderSynapse(mapping, synapse, node1, node2, true)
|
||||||
Synapse.renderSynapse(mapping, synapse, node1, node2, false)
|
Engine.runLayout()
|
||||||
} else if (!cancel) {
|
} else if (!cancel) {
|
||||||
setTimeout(waitThenRenderSynapse, 10)
|
setTimeout(waitThenRenderSynapse, 10)
|
||||||
}
|
}
|
||||||
|
@ -119,6 +121,7 @@ const Cable = {
|
||||||
}
|
}
|
||||||
DataModel.Synapses.remove(synapse)
|
DataModel.Synapses.remove(synapse)
|
||||||
DataModel.Mappings.remove(mapping)
|
DataModel.Mappings.remove(mapping)
|
||||||
|
Engine.runLayout()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
topicAdded: event => {
|
topicAdded: event => {
|
||||||
|
@ -134,7 +137,8 @@ const Cable = {
|
||||||
|
|
||||||
const waitThenRenderTopic = () => {
|
const waitThenRenderTopic = () => {
|
||||||
if (topic && mapping && mapper) {
|
if (topic && mapping && mapper) {
|
||||||
Topic.renderTopic(mapping, topic, false, false)
|
Topic.renderTopic(mapping, topic, true)
|
||||||
|
Engine.runLayout()
|
||||||
} else if (!cancel) {
|
} else if (!cancel) {
|
||||||
setTimeout(waitThenRenderTopic, 10)
|
setTimeout(waitThenRenderTopic, 10)
|
||||||
}
|
}
|
||||||
|
@ -185,7 +189,7 @@ const Cable = {
|
||||||
},
|
},
|
||||||
topicMoved: event => {
|
topicMoved: event => {
|
||||||
var topic, node, mapping
|
var topic, node, mapping
|
||||||
if (Active.Map) {
|
/*if (Active.Map) {
|
||||||
topic = DataModel.Topics.get(event.id)
|
topic = DataModel.Topics.get(event.id)
|
||||||
mapping = DataModel.Mappings.get(event.mapping_id)
|
mapping = DataModel.Mappings.get(event.mapping_id)
|
||||||
mapping.set('xloc', event.x)
|
mapping.set('xloc', event.x)
|
||||||
|
@ -193,7 +197,7 @@ const Cable = {
|
||||||
if (topic) node = topic.get('node')
|
if (topic) node = topic.get('node')
|
||||||
if (node) node.pos.setc(event.x, event.y)
|
if (node) node.pos.setc(event.x, event.y)
|
||||||
Visualize.mGraph.plot()
|
Visualize.mGraph.plot()
|
||||||
}
|
}*/
|
||||||
},
|
},
|
||||||
topicRemoved: event => {
|
topicRemoved: event => {
|
||||||
var topic = DataModel.Topics.get(event.id)
|
var topic = DataModel.Topics.get(event.id)
|
||||||
|
@ -203,6 +207,7 @@ const Cable = {
|
||||||
Control.hideNode(node.id)
|
Control.hideNode(node.id)
|
||||||
DataModel.Topics.remove(topic)
|
DataModel.Topics.remove(topic)
|
||||||
DataModel.Mappings.remove(mapping)
|
DataModel.Mappings.remove(mapping)
|
||||||
|
Engine.runLayout()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
messageCreated: event => {
|
messageCreated: event => {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import outdent from 'outdent'
|
||||||
|
|
||||||
import Active from './Active'
|
import Active from './Active'
|
||||||
import DataModel from './DataModel'
|
import DataModel from './DataModel'
|
||||||
|
import Engine from './Engine'
|
||||||
import Filter from './Filter'
|
import Filter from './Filter'
|
||||||
import GlobalUI from './GlobalUI'
|
import GlobalUI from './GlobalUI'
|
||||||
import Mouse from './Mouse'
|
import Mouse from './Mouse'
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
/* global $, Hogan, Bloodhound */
|
/* global Metamaps, $, Hogan, Bloodhound */
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
|
||||||
import DataModel from './DataModel'
|
import DataModel from './DataModel'
|
||||||
|
import Engine from './Engine'
|
||||||
|
import MetacodeSelect from '../components/MetacodeSelect'
|
||||||
import Mouse from './Mouse'
|
import Mouse from './Mouse'
|
||||||
import Selected from './Selected'
|
import Selected from './Selected'
|
||||||
import Synapse from './Synapse'
|
import Synapse from './Synapse'
|
||||||
import Topic from './Topic'
|
import Topic from './Topic'
|
||||||
|
import Util from './Util'
|
||||||
import Visualize from './Visualize'
|
import Visualize from './Visualize'
|
||||||
import GlobalUI from './GlobalUI'
|
import GlobalUI from './GlobalUI'
|
||||||
|
|
||||||
|
@ -16,9 +22,11 @@ const Create = {
|
||||||
newSelectedMetacodeNames: [],
|
newSelectedMetacodeNames: [],
|
||||||
selectedMetacodes: [],
|
selectedMetacodes: [],
|
||||||
newSelectedMetacodes: [],
|
newSelectedMetacodes: [],
|
||||||
init: function() {
|
recentMetacodes: [],
|
||||||
|
mostUsedMetacodes: [],
|
||||||
|
init: function (serverData) {
|
||||||
var self = Create
|
var self = Create
|
||||||
self.newTopic.init()
|
self.newTopic.init(serverData)
|
||||||
self.newSynapse.init()
|
self.newSynapse.init()
|
||||||
|
|
||||||
// // SWITCHING METACODE SETS
|
// // SWITCHING METACODE SETS
|
||||||
|
@ -58,10 +66,11 @@ const Create = {
|
||||||
if (!custom) {
|
if (!custom) {
|
||||||
codesToSwitchToIds = $('#metacodeSwitchTabs' + set).attr('data-metacodes').split(',')
|
codesToSwitchToIds = $('#metacodeSwitchTabs' + set).attr('data-metacodes').split(',')
|
||||||
$('.customMetacodeList li').addClass('toggledOff')
|
$('.customMetacodeList li').addClass('toggledOff')
|
||||||
Create.selectedMetacodes = []
|
console.log(codesToSwitchToIds)
|
||||||
Create.selectedMetacodeNames = []
|
Create.selectedMetacodes = codesToSwitchToIds
|
||||||
Create.newSelectedMetacodes = []
|
Create.selectedMetacodeNames = DataModel.Metacodes.filter(m => codesToSwitchToIds.indexOf(m.id) > -1).map(m => m.get('name'))
|
||||||
Create.newSelectedMetacodeNames = []
|
Create.newSelectedMetacodes = codesToSwitchToIds
|
||||||
|
Create.newSelectedMetacodeNames = DataModel.Metacodes.filter(m => codesToSwitchToIds.indexOf(m.id) > -1).map(m => m.get('name'))
|
||||||
} else if (custom) {
|
} else if (custom) {
|
||||||
// uses .slice to avoid setting the two arrays to the same actual array
|
// uses .slice to avoid setting the two arrays to the same actual array
|
||||||
Create.selectedMetacodes = Create.newSelectedMetacodes.slice(0)
|
Create.selectedMetacodes = Create.newSelectedMetacodes.slice(0)
|
||||||
|
@ -70,12 +79,13 @@ const Create = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort by name
|
// sort by name
|
||||||
for (var i = 0; i < codesToSwitchToIds.length; i++) {
|
codesToSwitchToIds.forEach(id => {
|
||||||
metacodeModels.add(DataModel.Metacodes.get(codesToSwitchToIds[i]))
|
const metacode = DataModel.Metacodes.get(id)
|
||||||
}
|
metacodeModels.add(metacode)
|
||||||
|
$('.customMetacodeList #' + id).removeClass('toggledOff')
|
||||||
|
})
|
||||||
metacodeModels.sort()
|
metacodeModels.sort()
|
||||||
|
|
||||||
$('#metacodeImg, #metacodeImgTitle').empty()
|
|
||||||
$('#metacodeImg').removeData('cloudcarousel')
|
$('#metacodeImg').removeData('cloudcarousel')
|
||||||
var newMetacodes = ''
|
var newMetacodes = ''
|
||||||
metacodeModels.each(function(metacode) {
|
metacodeModels.each(function(metacode) {
|
||||||
|
@ -83,16 +93,16 @@ const Create = {
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#metacodeImg').empty().append(newMetacodes).CloudCarousel({
|
$('#metacodeImg').empty().append(newMetacodes).CloudCarousel({
|
||||||
titleBox: $('#metacodeImgTitle'),
|
|
||||||
yRadius: 40,
|
yRadius: 40,
|
||||||
xRadius: 190,
|
xRadius: 190,
|
||||||
xPos: 170,
|
xPos: 170,
|
||||||
yPos: 40,
|
yPos: 40,
|
||||||
speed: 0.3,
|
speed: 0.3,
|
||||||
mouseWheel: true,
|
|
||||||
bringToFront: true
|
bringToFront: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Create.newTopic.setMetacode(metacodeModels.models[0].id)
|
||||||
|
|
||||||
GlobalUI.closeLightbox()
|
GlobalUI.closeLightbox()
|
||||||
$('#topic_name').focus()
|
$('#topic_name').focus()
|
||||||
|
|
||||||
|
@ -119,13 +129,7 @@ const Create = {
|
||||||
var self = Create
|
var self = Create
|
||||||
self.isSwitchingSet = false
|
self.isSwitchingSet = false
|
||||||
|
|
||||||
if (self.selectedMetacodeSet !== 'metacodeset-custom') {
|
if (self.selectedMetacodeSet === 'metacodeset-custom') {
|
||||||
$('.customMetacodeList li').addClass('toggledOff')
|
|
||||||
self.selectedMetacodes = []
|
|
||||||
self.selectedMetacodeNames = []
|
|
||||||
self.newSelectedMetacodes = []
|
|
||||||
self.newSelectedMetacodeNames = []
|
|
||||||
} else { // custom set is selected
|
|
||||||
// reset it to the current actual selection
|
// reset it to the current actual selection
|
||||||
$('.customMetacodeList li').addClass('toggledOff')
|
$('.customMetacodeList li').addClass('toggledOff')
|
||||||
for (var i = 0; i < self.selectedMetacodes.length; i++) {
|
for (var i = 0; i < self.selectedMetacodes.length; i++) {
|
||||||
|
@ -139,27 +143,33 @@ const Create = {
|
||||||
$('#topic_name').focus()
|
$('#topic_name').focus()
|
||||||
},
|
},
|
||||||
newTopic: {
|
newTopic: {
|
||||||
init: function() {
|
init: function (serverData) {
|
||||||
$('#topic_name').keyup(function(e) {
|
const DOWN_ARROW = 40
|
||||||
const ESC = 27
|
const ESC = 27
|
||||||
|
|
||||||
|
if (!serverData.ActiveMapper) return
|
||||||
|
|
||||||
|
$('#topic_name').keyup(function (e) {
|
||||||
|
|
||||||
|
Create.newTopic.name = $(this).val()
|
||||||
|
if (e.which == DOWN_ARROW && !Create.newTopic.name.length) {
|
||||||
|
Create.newTopic.openSelector()
|
||||||
|
}
|
||||||
|
|
||||||
if (e.keyCode === ESC) {
|
if (e.keyCode === ESC) {
|
||||||
Create.newTopic.hide()
|
Create.newTopic.hide()
|
||||||
} // if
|
} // if
|
||||||
|
|
||||||
Create.newTopic.name = $(this).val()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
$('.pinCarousel').click(function() {
|
$('.selectedMetacode').click(function() {
|
||||||
if (Create.newTopic.pinned) {
|
if (Create.newTopic.metacodeSelectorOpen) {
|
||||||
$('.pinCarousel').removeClass('isPinned')
|
Create.newTopic.hideSelector()
|
||||||
Create.newTopic.pinned = false
|
$('#topic_name').focus()
|
||||||
} else {
|
} else Create.newTopic.openSelector()
|
||||||
$('.pinCarousel').addClass('isPinned')
|
|
||||||
Create.newTopic.pinned = true
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Create.newTopic.initSelector()
|
||||||
|
|
||||||
var topicBloodhound = new Bloodhound({
|
var topicBloodhound = new Bloodhound({
|
||||||
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
|
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
|
||||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||||
|
@ -200,52 +210,86 @@ const Create = {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
$('#topic_name').click(function() { Create.newTopic.hideSelector() })
|
||||||
|
|
||||||
// initialize metacode spinner and then hide it
|
// initialize metacode spinner and then hide it
|
||||||
$('#metacodeImg').CloudCarousel({
|
$('#metacodeImg').CloudCarousel({
|
||||||
titleBox: $('#metacodeImgTitle'),
|
|
||||||
yRadius: 40,
|
yRadius: 40,
|
||||||
xRadius: 190,
|
xRadius: 190,
|
||||||
xPos: 170,
|
xPos: 170,
|
||||||
yPos: 40,
|
yPos: 40,
|
||||||
speed: 0.3,
|
speed: 0.3,
|
||||||
mouseWheel: true,
|
|
||||||
bringToFront: true
|
bringToFront: true
|
||||||
})
|
})
|
||||||
$('.new_topic').hide()
|
$('#new_topic').hide()
|
||||||
$('#new_topic').attr('oncontextmenu', 'return false') // prevents the mouse up event from opening the default context menu on this element
|
.css({ left: '50%', top: '50%' })
|
||||||
|
.attr('oncontextmenu', 'return false') // prevents the mouse up event from opening the default context menu on this element
|
||||||
},
|
},
|
||||||
name: null,
|
name: null,
|
||||||
newId: 1,
|
newId: 1,
|
||||||
beingCreated: false,
|
beingCreated: false,
|
||||||
|
metacodeSelectorOpen: false,
|
||||||
metacode: null,
|
metacode: null,
|
||||||
x: null,
|
x: null,
|
||||||
y: null,
|
y: null,
|
||||||
addSynapse: false,
|
addSynapse: false,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
open: function() {
|
initSelector: function () {
|
||||||
$('#new_topic').fadeIn('fast', function() {
|
ReactDOM.render(
|
||||||
$('#topic_name').focus()
|
React.createElement(MetacodeSelect, {
|
||||||
})
|
onClick: function (id) {
|
||||||
Create.newTopic.beingCreated = true
|
Create.newTopic.setMetacode(id)
|
||||||
Create.newTopic.name = ''
|
Create.newTopic.hideSelector()
|
||||||
GlobalUI.hideDiv('#instructions')
|
$('#topic_name').focus()
|
||||||
|
},
|
||||||
|
close: function () {
|
||||||
|
Create.newTopic.hideSelector()
|
||||||
|
$('#topic_name').focus()
|
||||||
|
},
|
||||||
|
metacodes: DataModel.Metacodes.filter(m => Create.selectedMetacodes.indexOf(m.id.toString()) > -1)
|
||||||
|
}),
|
||||||
|
document.getElementById('metacodeSelector')
|
||||||
|
)
|
||||||
},
|
},
|
||||||
hide: function(force) {
|
openSelector: function () {
|
||||||
if (force || !Create.newTopic.pinned) {
|
Create.newTopic.initSelector()
|
||||||
$('#new_topic').fadeOut('fast')
|
$('#metacodeSelector').show()
|
||||||
}
|
Create.newTopic.metacodeSelectorOpen = true
|
||||||
if (force) {
|
$('.metacodeFilterInput').focus()
|
||||||
$('.pinCarousel').removeClass('isPinned')
|
$('.selectedMetacode').addClass('isBeingSelected')
|
||||||
Create.newTopic.pinned = false
|
},
|
||||||
}
|
hideSelector: function () {
|
||||||
if (DataModel.Topics.length === 0) {
|
ReactDOM.unmountComponentAtNode(document.getElementById('metacodeSelector'))
|
||||||
GlobalUI.showDiv('#instructions')
|
$('#metacodeSelector').hide()
|
||||||
}
|
Create.newTopic.metacodeSelectorOpen = false
|
||||||
Create.newTopic.beingCreated = false
|
$('.selectedMetacode').removeClass('isBeingSelected')
|
||||||
|
},
|
||||||
|
setMetacode: function (id) {
|
||||||
|
Create.newTopic.metacode = id
|
||||||
|
var metacode = DataModel.Metacodes.get(id)
|
||||||
|
$('.selectedMetacode img').attr('src', metacode.get('icon'))
|
||||||
|
$('.selectedMetacode span').html(metacode.get('name'))
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/user/update_metacode_focus',
|
||||||
|
data: { value: id },
|
||||||
|
success: function (data) {},
|
||||||
|
error: function () {
|
||||||
|
console.log('failed to save metacode focus')
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
reset: function() {
|
reset: function() {
|
||||||
$('#topic_name').typeahead('val', '')
|
$('#topic_name').typeahead('val', '')
|
||||||
|
Create.newTopic.hideSelector()
|
||||||
|
},
|
||||||
|
position: function() {
|
||||||
|
const pixels = Util.coordsToPixels(Visualize.mGraph, Mouse.newNodeCoords)
|
||||||
|
$('#new_topic').css({
|
||||||
|
left: pixels.x,
|
||||||
|
top: pixels.y
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
newSynapse: {
|
newSynapse: {
|
||||||
|
@ -317,7 +361,9 @@ const Create = {
|
||||||
|
|
||||||
$('#synapse_desc').focusout(function() {
|
$('#synapse_desc').focusout(function() {
|
||||||
if (Create.newSynapse.beingCreated) {
|
if (Create.newSynapse.beingCreated) {
|
||||||
Synapse.createSynapseLocally()
|
Synapse.createSynapseLocally(Create.newSynapse.topic1id, Create.newSynapse.topic2id)
|
||||||
|
Engine.runLayout()
|
||||||
|
Create.newSynapse.hide()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -325,7 +371,9 @@ const Create = {
|
||||||
const TAB = 9
|
const TAB = 9
|
||||||
if (Create.newSynapse.beingCreated && e.keyCode === TAB) {
|
if (Create.newSynapse.beingCreated && e.keyCode === TAB) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
Synapse.createSynapseLocally()
|
Synapse.createSynapseLocally(Create.newSynapse.topic1id, Create.newSynapse.topic2id)
|
||||||
|
Engine.runLayout()
|
||||||
|
Create.newSynapse.hide()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -334,10 +382,13 @@ const Create = {
|
||||||
Synapse.getSynapseFromAutocomplete(datum.id)
|
Synapse.getSynapseFromAutocomplete(datum.id)
|
||||||
} else {
|
} else {
|
||||||
Create.newSynapse.description = datum.value
|
Create.newSynapse.description = datum.value
|
||||||
Synapse.createSynapseLocally()
|
Synapse.createSynapseLocally(Create.newSynapse.topic1id, Create.newSynapse.topic2id)
|
||||||
|
Engine.runLayout()
|
||||||
|
Create.newSynapse.hide()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
focusNode: null,
|
||||||
beingCreated: false,
|
beingCreated: false,
|
||||||
description: null,
|
description: null,
|
||||||
topic1id: null,
|
topic1id: null,
|
||||||
|
@ -356,8 +407,26 @@ const Create = {
|
||||||
Create.newTopic.addSynapse = false
|
Create.newTopic.addSynapse = false
|
||||||
Create.newSynapse.topic1id = 0
|
Create.newSynapse.topic1id = 0
|
||||||
Create.newSynapse.topic2id = 0
|
Create.newSynapse.topic2id = 0
|
||||||
|
Create.newSynapse.node1 = null
|
||||||
|
Create.newSynapse.node2 = null
|
||||||
Mouse.synapseStartCoordinates = []
|
Mouse.synapseStartCoordinates = []
|
||||||
|
Mouse.synapseEndCoordinates = null
|
||||||
if (Visualize.mGraph) Visualize.mGraph.plot()
|
if (Visualize.mGraph) Visualize.mGraph.plot()
|
||||||
|
},
|
||||||
|
updateForm: function() {
|
||||||
|
let pixelPos, midpoint = {}
|
||||||
|
if (Create.newSynapse.beingCreated) {
|
||||||
|
Mouse.synapseEndCoordinates = {
|
||||||
|
x: Create.newSynapse.node2.pos.getc().x,
|
||||||
|
y: Create.newSynapse.node2.pos.getc().y
|
||||||
|
}
|
||||||
|
// position the form
|
||||||
|
midpoint.x = Create.newSynapse.node1.pos.getc().x + (Create.newSynapse.node2.pos.getc().x - Create.newSynapse.node1.pos.getc().x) / 2
|
||||||
|
midpoint.y = Create.newSynapse.node1.pos.getc().y + (Create.newSynapse.node2.pos.getc().y - Create.newSynapse.node1.pos.getc().y) / 2
|
||||||
|
pixelPos = Util.coordsToPixels(Visualize.mGraph, midpoint)
|
||||||
|
$('#new_synapse').css('left', pixelPos.x + 'px')
|
||||||
|
$('#new_synapse').css('top', pixelPos.y + 'px')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,16 @@ const Mapping = Backbone.Model.extend({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getMap: function() {
|
getMap: function(callback) {
|
||||||
return Map.get(this.get('map_id'))
|
Map.get(this.get('map_id'), callback)
|
||||||
},
|
},
|
||||||
getTopic: function() {
|
getMappable: function(callback) {
|
||||||
if (this.get('mappable_type') !== 'Topic') return false
|
if (this.get('mappable_type') === 'Topic') {
|
||||||
return Topic.get(this.get('mappable_id'))
|
Topic.get(this.get('mappable_id'), callback)
|
||||||
},
|
}
|
||||||
getSynapse: function() {
|
else if (this.get('mappable_type') === 'Synapse') {
|
||||||
if (this.get('mappable_type') !== 'Synapse') return false
|
Synapse.get(this.get('mappable_id'), callback)
|
||||||
return Synapse.get(this.get('mappable_id'))
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -84,8 +84,8 @@ const Synapse = Backbone.Model.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Active.Map) {
|
if (Active.Map && providedMapping) {
|
||||||
mapping = providedMapping || this.getMapping()
|
mapping = providedMapping
|
||||||
mappingID = mapping.isNew() ? mapping.cid : mapping.id
|
mappingID = mapping.isNew() ? mapping.cid : mapping.id
|
||||||
edge.data.$mappings = []
|
edge.data.$mappings = []
|
||||||
edge.data.$mappingIDs = [mappingID]
|
edge.data.$mappingIDs = [mappingID]
|
||||||
|
@ -96,10 +96,12 @@ const Synapse = Backbone.Model.extend({
|
||||||
updateEdge: function() {
|
updateEdge: function() {
|
||||||
var mapping
|
var mapping
|
||||||
var edge = this.get('edge')
|
var edge = this.get('edge')
|
||||||
|
edge.data.$synapses = edge.data.$synapses || []
|
||||||
edge.getData('synapses').push(this)
|
edge.getData('synapses').push(this)
|
||||||
|
|
||||||
if (Active.Map) {
|
if (Active.Map) {
|
||||||
mapping = this.getMapping()
|
mapping = this.getMapping()
|
||||||
|
edge.data.$mappings = edge.data.$mappings || []
|
||||||
edge.getData('mappings').push(mapping)
|
edge.getData('mappings').push(mapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Backbone from 'backbone'
|
||||||
try { Backbone.$ = window.$ } catch (err) {}
|
try { Backbone.$ = window.$ } catch (err) {}
|
||||||
|
|
||||||
import Active from '../Active'
|
import Active from '../Active'
|
||||||
|
import Engine from '../Engine'
|
||||||
import Filter from '../Filter'
|
import Filter from '../Filter'
|
||||||
import TopicCard from '../TopicCard'
|
import TopicCard from '../TopicCard'
|
||||||
import Visualize from '../Visualize'
|
import Visualize from '../Visualize'
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Active from '../Active'
|
import Active from '../Active'
|
||||||
|
import Engine from '../Engine'
|
||||||
import Filter from '../Filter'
|
import Filter from '../Filter'
|
||||||
import { InfoBox } from '../Map'
|
import { InfoBox } from '../Map'
|
||||||
|
|
||||||
|
|
68
frontend/src/Metamaps/Engine.js
Normal file
68
frontend/src/Metamaps/Engine.js
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
//import Matter, { Vector, Sleeping, World, Constraint, Composite, Runner, Common, Body, Bodies, Events } from 'matter-js'
|
||||||
|
import { last, sortBy, values } from 'lodash'
|
||||||
|
|
||||||
|
import $jit from '../patched/JIT'
|
||||||
|
import { getLayoutForData, X_GRID_SPACE } from '../ConvoAlgo'
|
||||||
|
|
||||||
|
import Active from './Active'
|
||||||
|
import Create from './Create'
|
||||||
|
import DataModel from './DataModel'
|
||||||
|
import Mouse from './Mouse'
|
||||||
|
import JIT from './JIT'
|
||||||
|
import Visualize from './Visualize'
|
||||||
|
|
||||||
|
const Engine = {
|
||||||
|
init: (serverData) => {
|
||||||
|
|
||||||
|
},
|
||||||
|
run: init => {
|
||||||
|
if (init) {
|
||||||
|
if (Active.Mapper && Object.keys(Visualize.mGraph.graph.nodes).length) {
|
||||||
|
Engine.setFocusNode(Engine.findFocusNode(Visualize.mGraph.graph.nodes), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
endActiveMap: () => {
|
||||||
|
|
||||||
|
},
|
||||||
|
runLayout: init => {
|
||||||
|
Visualize.mGraph.busy = true
|
||||||
|
const synapses = DataModel.Synapses.map(s => s.attributes)
|
||||||
|
const topics = DataModel.Topics.map(t => t.attributes)
|
||||||
|
const focalNodeId = Create.newSynapse.focusNode.getData('topic').id
|
||||||
|
const focalCoords = init ? { x: 0, y: 0 } : Create.newSynapse.focusNode.pos
|
||||||
|
const layout = getLayoutForData(topics, synapses, focalNodeId, focalCoords)
|
||||||
|
Visualize.mGraph.graph.eachNode(n => {
|
||||||
|
let calculatedCoords = layout[n.getData('topic').id]
|
||||||
|
const endPos = new $jit.Complex(calculatedCoords.x, calculatedCoords.y)
|
||||||
|
n.setPos(endPos, 'end')
|
||||||
|
})
|
||||||
|
Visualize.mGraph.animate({
|
||||||
|
modes: ['linear'],
|
||||||
|
transition: $jit.Trans.Quart.easeOut,
|
||||||
|
duration: 500,
|
||||||
|
onComplete: () => {
|
||||||
|
Visualize.mGraph.busy = false
|
||||||
|
Create.newSynapse.updateForm()
|
||||||
|
Create.newTopic.position()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
findFocusNode: nodes => {
|
||||||
|
return last(sortBy(values(nodes), n => new Date(n.getData('topic').get('created_at'))))
|
||||||
|
},
|
||||||
|
setFocusNode: (node, init, dontRun) => {
|
||||||
|
if (!Active.Mapper) return
|
||||||
|
Create.newSynapse.focusNode = node
|
||||||
|
Mouse.focusNodeCoords = node.pos
|
||||||
|
Mouse.newNodeCoords = {
|
||||||
|
x: node.pos.x + X_GRID_SPACE,
|
||||||
|
y: node.pos.y
|
||||||
|
}
|
||||||
|
Create.newSynapse.updateForm()
|
||||||
|
Create.newTopic.position()
|
||||||
|
if (!dontRun) Engine.runLayout(init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Engine
|
|
@ -9,6 +9,7 @@ import Active from './Active'
|
||||||
import Control from './Control'
|
import Control from './Control'
|
||||||
import Create from './Create'
|
import Create from './Create'
|
||||||
import DataModel from './DataModel'
|
import DataModel from './DataModel'
|
||||||
|
import Engine from './Engine'
|
||||||
import Filter from './Filter'
|
import Filter from './Filter'
|
||||||
import GlobalUI from './GlobalUI'
|
import GlobalUI from './GlobalUI'
|
||||||
import Map from './Map'
|
import Map from './Map'
|
||||||
|
@ -392,7 +393,6 @@ const JIT = {
|
||||||
Visualize.mGraph.busy = false
|
Visualize.mGraph.busy = false
|
||||||
Mouse.boxEndCoordinates = eventInfo.getPos()
|
Mouse.boxEndCoordinates = eventInfo.getPos()
|
||||||
JIT.selectWithBox(e)
|
JIT.selectWithBox(e)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -404,6 +404,7 @@ const JIT = {
|
||||||
JIT.selectEdgeOnClickHandler(node, e)
|
JIT.selectEdgeOnClickHandler(node, e)
|
||||||
} else if (node && !node.nodeFrom) {
|
} else if (node && !node.nodeFrom) {
|
||||||
JIT.selectNodeOnClickHandler(node, e)
|
JIT.selectNodeOnClickHandler(node, e)
|
||||||
|
Engine.setFocusNode(node)
|
||||||
} else {
|
} else {
|
||||||
JIT.canvasClickHandler(eventInfo.getPos(), e)
|
JIT.canvasClickHandler(eventInfo.getPos(), e)
|
||||||
} // if
|
} // if
|
||||||
|
@ -415,7 +416,6 @@ const JIT = {
|
||||||
|
|
||||||
if (Mouse.boxStartCoordinates) {
|
if (Mouse.boxStartCoordinates) {
|
||||||
Create.newSynapse.hide()
|
Create.newSynapse.hide()
|
||||||
Create.newTopic.hide()
|
|
||||||
Visualize.mGraph.busy = false
|
Visualize.mGraph.busy = false
|
||||||
Mouse.boxEndCoordinates = eventInfo.getPos()
|
Mouse.boxEndCoordinates = eventInfo.getPos()
|
||||||
JIT.selectWithBox(e)
|
JIT.selectWithBox(e)
|
||||||
|
@ -432,7 +432,6 @@ const JIT = {
|
||||||
} else {
|
} else {
|
||||||
// right click open space
|
// right click open space
|
||||||
Create.newSynapse.hide()
|
Create.newSynapse.hide()
|
||||||
Create.newTopic.hide()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -721,14 +720,16 @@ const JIT = {
|
||||||
$('canvas').css('cursor', 'default')
|
$('canvas').css('cursor', 'default')
|
||||||
}
|
}
|
||||||
}, // onMouseMoveHandler
|
}, // onMouseMoveHandler
|
||||||
enterKeyHandler: function() {
|
enterKeyHandler: function(e) {
|
||||||
const creatingMap = GlobalUI.lightbox
|
const creatingMap = GlobalUI.lightbox
|
||||||
if (creatingMap === 'newmap' || creatingMap === 'forkmap') {
|
if (creatingMap === 'newmap' || creatingMap === 'forkmap') {
|
||||||
GlobalUI.CreateMap.submit()
|
GlobalUI.CreateMap.submit()
|
||||||
} else if (Create.newTopic.beingCreated) {
|
} else if (e.target.id === 'topic_name' && !Create.newTopic.metacodeSelectorOpen) {
|
||||||
Topic.createTopicLocally()
|
Topic.createTopicLocally()
|
||||||
} else if (Create.newSynapse.beingCreated) {
|
} else if (Create.newSynapse.beingCreated) {
|
||||||
Synapse.createSynapseLocally()
|
Synapse.createSynapseLocally(Create.newSynapse.topic1id, Create.newSynapse.topic2id)
|
||||||
|
Engine.runLayout()
|
||||||
|
Create.newSynapse.hide()
|
||||||
}
|
}
|
||||||
}, // enterKeyHandler
|
}, // enterKeyHandler
|
||||||
escKeyHandler: function() {
|
escKeyHandler: function() {
|
||||||
|
@ -741,131 +742,28 @@ const JIT = {
|
||||||
var authorized = Active.Map && Active.Map.authorizeToEdit(Active.Mapper)
|
var authorized = Active.Map && Active.Map.authorizeToEdit(Active.Mapper)
|
||||||
|
|
||||||
if (node && !node.nodeFrom) {
|
if (node && !node.nodeFrom) {
|
||||||
self.handleSelectionBeforeDragging(node, e)
|
|
||||||
|
|
||||||
const pos = eventInfo.getPos()
|
const pos = eventInfo.getPos()
|
||||||
const EDGE_THICKNESS = 30
|
if ((e.button === 0 || e.buttons === 0) && authorized) {
|
||||||
const SHIFT = 2 / Visualize.mGraph.canvas.scaleOffsetX
|
// start synapse creation ->second option is for firefox
|
||||||
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 = Visualize.mGraph.canvas.getSize().width
|
|
||||||
const height = Visualize.mGraph.canvas.getSize().height
|
|
||||||
const xPix = Util.coordsToPixels(Visualize.mGraph, pos).x
|
|
||||||
const yPix = Util.coordsToPixels(Visualize.mGraph, pos).y
|
|
||||||
|
|
||||||
if (self.dragFlag === 0) {
|
|
||||||
self.mouseDownPix = Util.coordsToPixels(Visualize.mGraph, eventInfo.getPos())
|
|
||||||
self.dragFlag = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Util.getDistance(Util.coordsToPixels(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(Visualize.mGraph, { x: EDGE_THICKNESS, y: yPix }).x - SHIFT, y: pos.y }
|
|
||||||
Visualize.mGraph.canvas.translate(SHIFT, 0)
|
|
||||||
self.updateTopicPositions(node, self.virtualPointer)
|
|
||||||
Visualize.mGraph.plot()
|
|
||||||
|
|
||||||
self.dragLeftEdge = setInterval(function() {
|
|
||||||
self.virtualPointer = { x: Util.pixelsToCoords(Visualize.mGraph, { x: EDGE_THICKNESS, y: yPix }).x - SHIFT, y: pos.y }
|
|
||||||
Visualize.mGraph.canvas.translate(SHIFT, 0)
|
|
||||||
self.updateTopicPositions(node, self.virtualPointer)
|
|
||||||
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(Visualize.mGraph, { x: width - EDGE_THICKNESS, y: yPix }).x + SHIFT, y: pos.y }
|
|
||||||
Visualize.mGraph.canvas.translate(-SHIFT, 0)
|
|
||||||
self.updateTopicPositions(node, self.virtualPointer)
|
|
||||||
Visualize.mGraph.plot()
|
|
||||||
|
|
||||||
self.dragRightEdge = setInterval(function() {
|
|
||||||
self.virtualPointer = { x: Util.pixelsToCoords(Visualize.mGraph, { x: width - EDGE_THICKNESS, y: yPix }).x + SHIFT, y: pos.y }
|
|
||||||
Visualize.mGraph.canvas.translate(-SHIFT, 0)
|
|
||||||
self.updateTopicPositions(node, self.virtualPointer)
|
|
||||||
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(Visualize.mGraph, { x: xPix, y: EDGE_THICKNESS }).y - SHIFT }
|
|
||||||
Visualize.mGraph.canvas.translate(0, SHIFT)
|
|
||||||
self.updateTopicPositions(node, self.virtualPointer)
|
|
||||||
Visualize.mGraph.plot()
|
|
||||||
|
|
||||||
self.dragTopEdge = setInterval(function() {
|
|
||||||
self.virtualPointer = { x: pos.x, y: Util.pixelsToCoords(Visualize.mGraph, { x: xPix, y: EDGE_THICKNESS }).y - SHIFT }
|
|
||||||
Visualize.mGraph.canvas.translate(0, SHIFT)
|
|
||||||
self.updateTopicPositions(node, self.virtualPointer)
|
|
||||||
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(Visualize.mGraph, { x: xPix, y: height - EDGE_THICKNESS }).y + SHIFT }
|
|
||||||
Visualize.mGraph.canvas.translate(0, -SHIFT)
|
|
||||||
self.updateTopicPositions(node, self.virtualPointer)
|
|
||||||
Visualize.mGraph.plot()
|
|
||||||
|
|
||||||
self.dragBottomEdge = setInterval(function() {
|
|
||||||
self.virtualPointer = { x: pos.x, y: Util.pixelsToCoords(Visualize.mGraph, { x: xPix, y: height - EDGE_THICKNESS }).y + SHIFT }
|
|
||||||
Visualize.mGraph.canvas.translate(0, -SHIFT)
|
|
||||||
self.updateTopicPositions(node, self.virtualPointer)
|
|
||||||
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.updateTopicPositions(node, pos)
|
|
||||||
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 (JIT.tempInit === false) {
|
if (JIT.tempInit === false) {
|
||||||
JIT.tempNode = node
|
JIT.tempNode = node
|
||||||
JIT.tempInit = true
|
JIT.tempInit = true
|
||||||
|
|
||||||
Create.newTopic.hide()
|
|
||||||
Create.newSynapse.hide()
|
Create.newSynapse.hide()
|
||||||
// set the draw synapse start positions
|
// set the draw synapse start positions
|
||||||
var l = Selected.Nodes.length
|
Mouse.synapseStartCoordinates = []
|
||||||
if (l > 0) {
|
if (Selected.Nodes.length) {
|
||||||
for (let i = l - 1; i >= 0; i -= 1) {
|
Selected.Nodes.forEach(n => {
|
||||||
const n = Selected.Nodes[i]
|
|
||||||
Mouse.synapseStartCoordinates.push({
|
Mouse.synapseStartCoordinates.push({
|
||||||
x: n.pos.getc().x,
|
x: n.pos.getc().x,
|
||||||
y: n.pos.getc().y
|
y: n.pos.getc().y
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
Mouse.synapseStartCoordinates = [{
|
Mouse.synapseStartCoordinates = [{
|
||||||
x: JIT.tempNode.pos.getc().x,
|
x: node.pos.getc().x,
|
||||||
y: JIT.tempNode.pos.getc().y
|
y: node.pos.getc().y
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
Mouse.synapseEndCoordinates = {
|
Mouse.synapseEndCoordinates = {
|
||||||
|
@ -877,43 +775,28 @@ const JIT = {
|
||||||
let temp = eventInfo.getNode()
|
let temp = eventInfo.getNode()
|
||||||
if (temp !== false && temp.id !== node.id && Selected.Nodes.indexOf(temp) === -1) { // this means a Node has been returned
|
if (temp !== false && temp.id !== node.id && Selected.Nodes.indexOf(temp) === -1) { // this means a Node has been returned
|
||||||
JIT.tempNode2 = temp
|
JIT.tempNode2 = temp
|
||||||
|
|
||||||
Mouse.synapseEndCoordinates = {
|
Mouse.synapseEndCoordinates = {
|
||||||
x: JIT.tempNode2.pos.getc().x,
|
x: JIT.tempNode2.pos.getc().x,
|
||||||
y: JIT.tempNode2.pos.getc().y
|
y: JIT.tempNode2.pos.getc().y
|
||||||
}
|
}
|
||||||
|
|
||||||
// before making the highlighted one bigger, make sure all the others are regular size
|
// before making the highlighted one bigger, make sure all the others are regular size
|
||||||
Visualize.mGraph.graph.eachNode(function(n) {
|
Visualize.mGraph.graph.eachNode(function(n) {
|
||||||
n.setData('dim', 25, 'current')
|
n.setData('dim', 25, 'current')
|
||||||
})
|
})
|
||||||
temp.setData('dim', 35, 'current')
|
temp.setData('dim', 35, 'current')
|
||||||
Visualize.mGraph.plot()
|
|
||||||
} else if (!temp) {
|
} else if (!temp) {
|
||||||
JIT.tempNode2 = null
|
JIT.tempNode2 = null
|
||||||
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')
|
|
||||||
Create.newTopic.x = eventInfo.getPos().x
|
|
||||||
Create.newTopic.y = eventInfo.getPos().y
|
|
||||||
Visualize.mGraph.plot()
|
|
||||||
|
|
||||||
Mouse.synapseEndCoordinates = {
|
Mouse.synapseEndCoordinates = {
|
||||||
x: pos.x,
|
x: pos.x,
|
||||||
y: pos.y
|
y: pos.y
|
||||||
}
|
}
|
||||||
|
Visualize.mGraph.graph.eachNode(function(n) {
|
||||||
|
n.setData('dim', 25, 'current')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else if ((e.button === 2 || (e.button === 0 && e.altKey) || e.buttons === 2) && 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.')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Visualize.mGraph.plot()
|
||||||
}, // onDragMoveTopicHandler
|
}, // onDragMoveTopicHandler
|
||||||
onDragCancelHandler: function(node, eventInfo, e) {
|
onDragCancelHandler: function(node, eventInfo, e) {
|
||||||
JIT.tempNode = null
|
JIT.tempNode = null
|
||||||
|
@ -931,30 +814,15 @@ const JIT = {
|
||||||
let pixelPos
|
let pixelPos
|
||||||
let mapping
|
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 (JIT.tempInit && JIT.tempNode2 === null) {
|
if (JIT.tempInit && JIT.tempNode2 === null) {
|
||||||
// this means you want to add a new topic, and then a synapse
|
Mouse.synapseEndCoordinates = null
|
||||||
Create.newTopic.addSynapse = true
|
|
||||||
Create.newTopic.open()
|
|
||||||
} else if (JIT.tempInit && JIT.tempNode2 !== null) {
|
} else if (JIT.tempInit && JIT.tempNode2 !== null) {
|
||||||
// this means you want to create a synapse between two existing topics
|
// this means you want to create a synapse between two existing topics
|
||||||
Create.newTopic.addSynapse = false
|
|
||||||
Create.newSynapse.topic1id = JIT.tempNode.getData('topic').id
|
Create.newSynapse.topic1id = JIT.tempNode.getData('topic').id
|
||||||
Create.newSynapse.topic2id = JIT.tempNode2.getData('topic').id
|
Create.newSynapse.topic2id = JIT.tempNode2.getData('topic').id
|
||||||
|
Create.newSynapse.node1 = JIT.tempNode
|
||||||
|
Create.newSynapse.node2 = JIT.tempNode2
|
||||||
JIT.tempNode2.setData('dim', 25, 'current')
|
JIT.tempNode2.setData('dim', 25, 'current')
|
||||||
Visualize.mGraph.plot()
|
|
||||||
midpoint.x = JIT.tempNode.pos.getc().x + (JIT.tempNode2.pos.getc().x - JIT.tempNode.pos.getc().x) / 2
|
midpoint.x = JIT.tempNode.pos.getc().x + (JIT.tempNode2.pos.getc().x - JIT.tempNode.pos.getc().x) / 2
|
||||||
midpoint.y = JIT.tempNode.pos.getc().y + (JIT.tempNode2.pos.getc().y - JIT.tempNode.pos.getc().y) / 2
|
midpoint.y = JIT.tempNode.pos.getc().y + (JIT.tempNode2.pos.getc().y - JIT.tempNode.pos.getc().y) / 2
|
||||||
pixelPos = Util.coordsToPixels(Visualize.mGraph, midpoint)
|
pixelPos = Util.coordsToPixels(Visualize.mGraph, midpoint)
|
||||||
|
@ -964,36 +832,8 @@ const JIT = {
|
||||||
JIT.tempNode = null
|
JIT.tempNode = null
|
||||||
JIT.tempNode2 = null
|
JIT.tempNode2 = null
|
||||||
JIT.tempInit = false
|
JIT.tempInit = false
|
||||||
} else if (!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 = Active.Map
|
|
||||||
if (!map) return false
|
|
||||||
return map.authorizeToEdit(Active.Mapper)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkWhetherToSave()) {
|
|
||||||
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 = Selected.Nodes.length
|
|
||||||
for (var i = l - 1; i >= 0; i -= 1) {
|
|
||||||
const n = Selected.Nodes[i]
|
|
||||||
if (n !== node) {
|
|
||||||
mapping = n.getData('mapping')
|
|
||||||
mapping.save({
|
|
||||||
xloc: n.getPos().x,
|
|
||||||
yloc: n.getPos().y
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Visualize.mGraph.plot()
|
||||||
}, // onDragEndTopicHandler
|
}, // onDragEndTopicHandler
|
||||||
canvasClickHandler: function(canvasLoc, e) {
|
canvasClickHandler: function(canvasLoc, e) {
|
||||||
// grab the location and timestamp of the click
|
// grab the location and timestamp of the click
|
||||||
|
@ -1004,27 +844,12 @@ const JIT = {
|
||||||
const authorized = Active.Map && Active.Map.authorizeToEdit(Active.Mapper)
|
const authorized = Active.Map && Active.Map.authorizeToEdit(Active.Mapper)
|
||||||
|
|
||||||
if (now - storedTime < Mouse.DOUBLE_CLICK_TOLERANCE && !Mouse.didPan) {
|
if (now - storedTime < Mouse.DOUBLE_CLICK_TOLERANCE && !Mouse.didPan) {
|
||||||
if (Active.Map && !authorized) {
|
|
||||||
GlobalUI.notifyUser('Cannot edit Public map.')
|
|
||||||
return
|
|
||||||
} else if (Active.Topic) {
|
|
||||||
GlobalUI.notifyUser('Cannot create in Topic view.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// DOUBLE CLICK
|
// DOUBLE CLICK
|
||||||
// pop up node creation :)
|
|
||||||
Create.newTopic.addSynapse = false
|
|
||||||
Create.newTopic.x = canvasLoc.x
|
|
||||||
Create.newTopic.y = canvasLoc.y
|
|
||||||
$('#new_topic').css('left', e.clientX + 'px')
|
|
||||||
$('#new_topic').css('top', e.clientY + 'px')
|
|
||||||
Create.newTopic.open()
|
|
||||||
} else if (!Mouse.didPan) {
|
} else if (!Mouse.didPan) {
|
||||||
// SINGLE CLICK, no pan
|
// SINGLE CLICK, no pan
|
||||||
Filter.close()
|
Filter.close()
|
||||||
TopicCard.hideCard()
|
TopicCard.hideCard()
|
||||||
SynapseCard.hideCard()
|
SynapseCard.hideCard()
|
||||||
Create.newTopic.hide()
|
|
||||||
$('.rightclickmenu').remove()
|
$('.rightclickmenu').remove()
|
||||||
// reset the draw synapse positions to false
|
// reset the draw synapse positions to false
|
||||||
Mouse.synapseStartCoordinates = []
|
Mouse.synapseStartCoordinates = []
|
||||||
|
@ -1038,7 +863,6 @@ const JIT = {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// SINGLE CLICK, resulting from pan
|
// SINGLE CLICK, resulting from pan
|
||||||
Create.newTopic.hide()
|
|
||||||
}
|
}
|
||||||
}, // canvasClickHandler
|
}, // canvasClickHandler
|
||||||
updateTopicPositions: function(node, pos) {
|
updateTopicPositions: function(node, pos) {
|
||||||
|
@ -1272,26 +1096,6 @@ const JIT = {
|
||||||
Mouse.boxEndCoordinates = false
|
Mouse.boxEndCoordinates = false
|
||||||
Visualize.mGraph.plot()
|
Visualize.mGraph.plot()
|
||||||
}, // selectWithBox
|
}, // selectWithBox
|
||||||
drawSelectBox: function(eventInfo, e) {
|
|
||||||
const ctx = Visualize.mGraph.canvas.getCtx()
|
|
||||||
|
|
||||||
const startX = Mouse.boxStartCoordinates.x
|
|
||||||
const startY = Mouse.boxStartCoordinates.y
|
|
||||||
const currX = eventInfo.getPos().x
|
|
||||||
const currY = eventInfo.getPos().y
|
|
||||||
|
|
||||||
Visualize.mGraph.canvas.clear()
|
|
||||||
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) {
|
selectNodeOnClickHandler: function(node, e) {
|
||||||
if (Visualize.mGraph.busy) return
|
if (Visualize.mGraph.busy) return
|
||||||
|
|
||||||
|
@ -1320,43 +1124,19 @@ const JIT = {
|
||||||
// wait a certain length of time, then check again, then run this code
|
// wait a certain length of time, then check again, then run this code
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
if (!JIT.nodeWasDoubleClicked()) {
|
if (!JIT.nodeWasDoubleClicked()) {
|
||||||
var nodeAlreadySelected = node.selected
|
if (e.button === 1 && !e.ctrlKey) {
|
||||||
|
var len = Selected.Nodes.length
|
||||||
|
|
||||||
if (e.button !== 1) {
|
for (let i = 0; i < len; i += 1) {
|
||||||
if (!e.shiftKey) {
|
let n = Selected.Nodes[i]
|
||||||
Control.deselectAllNodes()
|
let result = Util.openLink(DataModel.Topics.get(n.id).attributes.link)
|
||||||
Control.deselectAllEdges()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nodeAlreadySelected) {
|
if (!result) { // if link failed to open
|
||||||
Control.deselectNode(node)
|
break
|
||||||
} else {
|
|
||||||
Control.selectNode(node, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// trigger animation to final styles
|
|
||||||
Visualize.mGraph.fx.animate({
|
|
||||||
modes: ['edge-property:lineWidth:color:alpha'],
|
|
||||||
duration: 500
|
|
||||||
})
|
|
||||||
Visualize.mGraph.plot()
|
|
||||||
} else {
|
|
||||||
if (!e.ctrlKey) {
|
|
||||||
var len = Selected.Nodes.length
|
|
||||||
|
|
||||||
for (let i = 0; i < len; i += 1) {
|
|
||||||
let n = Selected.Nodes[i]
|
|
||||||
let result = Util.openLink(DataModel.Topics.get(n.id).attributes.link)
|
|
||||||
|
|
||||||
if (!result) { // if link failed to open
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!node.selected) {
|
|
||||||
Util.openLink(DataModel.Topics.get(node.id).attributes.link)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!node.selected) Util.openLink(DataModel.Topics.get(node.id).attributes.link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, Mouse.DOUBLE_CLICK_TOLERANCE)
|
}, Mouse.DOUBLE_CLICK_TOLERANCE)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Active from './Active'
|
import Active from './Active'
|
||||||
import Control from './Control'
|
import Control from './Control'
|
||||||
|
import Create from './Create'
|
||||||
import DataModel from './DataModel'
|
import DataModel from './DataModel'
|
||||||
import JIT from './JIT'
|
import JIT from './JIT'
|
||||||
import Mobile from './Mobile'
|
import Mobile from './Mobile'
|
||||||
|
@ -24,7 +25,7 @@ const Listeners = {
|
||||||
case 13: // if enter key is pressed
|
case 13: // if enter key is pressed
|
||||||
// prevent topic creation if sending a message
|
// prevent topic creation if sending a message
|
||||||
if (e.target.className !== 'chat-input') {
|
if (e.target.className !== 'chat-input') {
|
||||||
JIT.enterKeyHandler()
|
JIT.enterKeyHandler(e)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 27: // if esc key is pressed
|
case 27: // if esc key is pressed
|
||||||
|
@ -130,6 +131,8 @@ const Listeners = {
|
||||||
$(window).resize(function() {
|
$(window).resize(function() {
|
||||||
if (Visualize && Visualize.mGraph) {
|
if (Visualize && Visualize.mGraph) {
|
||||||
Util.resizeCanvas(Visualize.mGraph.canvas)
|
Util.resizeCanvas(Visualize.mGraph.canvas)
|
||||||
|
Create.newSynapse.updateForm()
|
||||||
|
Create.newTopic.position()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Active.Map && Realtime.inConversation) Realtime.positionVideos()
|
if (Active.Map && Realtime.inConversation) Realtime.positionVideos()
|
||||||
|
|
|
@ -8,6 +8,7 @@ import AutoLayout from '../AutoLayout'
|
||||||
import Create from '../Create'
|
import Create from '../Create'
|
||||||
import DataModel from '../DataModel'
|
import DataModel from '../DataModel'
|
||||||
import DataModelMap from '../DataModel/Map'
|
import DataModelMap from '../DataModel/Map'
|
||||||
|
import Engine from '../Engine'
|
||||||
import Filter from '../Filter'
|
import Filter from '../Filter'
|
||||||
import GlobalUI from '../GlobalUI'
|
import GlobalUI from '../GlobalUI'
|
||||||
import JIT from '../JIT'
|
import JIT from '../JIT'
|
||||||
|
@ -146,11 +147,12 @@ const Map = {
|
||||||
$('.rightclickmenu').remove()
|
$('.rightclickmenu').remove()
|
||||||
TopicCard.hideCard()
|
TopicCard.hideCard()
|
||||||
SynapseCard.hideCard()
|
SynapseCard.hideCard()
|
||||||
Create.newTopic.hide(true) // true means force (and override pinned)
|
$('#new_topic').hide()
|
||||||
Create.newSynapse.hide()
|
Create.newSynapse.hide()
|
||||||
Filter.close()
|
Filter.close()
|
||||||
InfoBox.close()
|
InfoBox.close()
|
||||||
Realtime.endActiveMap()
|
Realtime.endActiveMap()
|
||||||
|
Engine.endActiveMap()
|
||||||
$('.viewOnly').removeClass('isViewOnly')
|
$('.viewOnly').removeClass('isViewOnly')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,11 +6,13 @@ const Mouse = {
|
||||||
edgeHoveringOver: false,
|
edgeHoveringOver: false,
|
||||||
boxStartCoordinates: false,
|
boxStartCoordinates: false,
|
||||||
boxEndCoordinates: false,
|
boxEndCoordinates: false,
|
||||||
|
focusNodeCoords: null,
|
||||||
|
newNodeCoords: { x: 100, y: 0 },
|
||||||
synapseStartCoordinates: [],
|
synapseStartCoordinates: [],
|
||||||
synapseEndCoordinates: null,
|
synapseEndCoordinates: null,
|
||||||
lastNodeClick: 0,
|
lastNodeClick: 0,
|
||||||
lastCanvasClick: 0,
|
lastCanvasClick: 0,
|
||||||
DOUBLE_CLICK_TOLERANCE: 300
|
DOUBLE_CLICK_TOLERANCE: 501
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Mouse
|
export default Mouse
|
||||||
|
|
|
@ -5,6 +5,7 @@ import SocketIoConnection from 'simplewebrtc/socketioconnection'
|
||||||
|
|
||||||
import Active from '../Active'
|
import Active from '../Active'
|
||||||
import Cable from '../Cable'
|
import Cable from '../Cable'
|
||||||
|
import Create from '../Create'
|
||||||
import DataModel from '../DataModel'
|
import DataModel from '../DataModel'
|
||||||
import JIT from '../JIT'
|
import JIT from '../JIT'
|
||||||
import Util from '../Util'
|
import Util from '../Util'
|
||||||
|
@ -233,8 +234,13 @@ let Realtime = {
|
||||||
setupLocalEvents: function() {
|
setupLocalEvents: function() {
|
||||||
var self = Realtime
|
var self = Realtime
|
||||||
// local event listeners that trigger events
|
// local event listeners that trigger events
|
||||||
$(document).on(JIT.events.zoom + '.map', self.positionPeerIcons)
|
const panOrZoom = () => {
|
||||||
$(document).on(JIT.events.pan + '.map', self.positionPeerIcons)
|
self.positionPeerIcons()
|
||||||
|
Create.newSynapse.updateForm()
|
||||||
|
Create.newTopic.position()
|
||||||
|
}
|
||||||
|
$(document).on(JIT.events.zoom + '.map', panOrZoom)
|
||||||
|
$(document).on(JIT.events.pan + '.map', panOrZoom)
|
||||||
$(document).on('mousemove.map', function(event) {
|
$(document).on('mousemove.map', function(event) {
|
||||||
var pixels = {
|
var pixels = {
|
||||||
x: event.pageX,
|
x: event.pageX,
|
||||||
|
|
|
@ -4,6 +4,8 @@ import Active from './Active'
|
||||||
import Control from './Control'
|
import Control from './Control'
|
||||||
import Create from './Create'
|
import Create from './Create'
|
||||||
import DataModel from './DataModel'
|
import DataModel from './DataModel'
|
||||||
|
import Engine from './Engine'
|
||||||
|
import JIT from './JIT'
|
||||||
import Map from './Map'
|
import Map from './Map'
|
||||||
import Selected from './Selected'
|
import Selected from './Selected'
|
||||||
import Settings from './Settings'
|
import Settings from './Settings'
|
||||||
|
@ -27,91 +29,48 @@ const Synapse = {
|
||||||
} else callback(DataModel.Synapses.get(id))
|
} else callback(DataModel.Synapses.get(id))
|
||||||
},
|
},
|
||||||
|
|
||||||
renderSynapse: function(mapping, synapse, node1, node2, createNewInDB) {
|
renderSynapse: function(mapping, synapse, node1, node2, fromRemote) {
|
||||||
var edgeOnViz
|
const newedge = synapse.createEdge(mapping)
|
||||||
|
|
||||||
var newedge = synapse.createEdge(mapping)
|
|
||||||
|
|
||||||
Visualize.mGraph.graph.addAdjacence(node1, node2, newedge.data)
|
Visualize.mGraph.graph.addAdjacence(node1, node2, newedge.data)
|
||||||
edgeOnViz = Visualize.mGraph.graph.getAdjacence(node1.id, node2.id)
|
const edgeOnViz = Visualize.mGraph.graph.getAdjacence(node1.id, node2.id)
|
||||||
synapse.set('edge', edgeOnViz)
|
synapse.set('edge', edgeOnViz)
|
||||||
synapse.updateEdge() // links the synapse and the mapping to the edge
|
synapse.updateEdge() // links the synapse and the mapping to the edge
|
||||||
|
if (!fromRemote && synapse.isNew()) {
|
||||||
Control.selectEdge(edgeOnViz)
|
synapse.save(null, {
|
||||||
|
success: synapseModel => Active.Map && mapping.save({ mappable_id: synapseModel.id })
|
||||||
var synapseSuccessCallback = function(synapseModel, response) {
|
})
|
||||||
if (Active.Map) {
|
} else if (!fromRemote && !synapse.isNew() && Active.Map) {
|
||||||
mapping.save({ mappable_id: synapseModel.id }, {
|
mapping.save()
|
||||||
error: function(model, response) {
|
|
||||||
console.log('error saving mapping to database')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Settings.sandbox && createNewInDB) {
|
|
||||||
if (synapse.isNew()) {
|
|
||||||
synapse.save(null, {
|
|
||||||
success: synapseSuccessCallback,
|
|
||||||
error: function(model, response) {
|
|
||||||
console.log('error saving synapse to database')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (!synapse.isNew() && Active.Map) {
|
|
||||||
mapping.save(null, {
|
|
||||||
error: function(model, response) {
|
|
||||||
console.log('error saving mapping to database')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createSynapseLocally: function() {
|
createSynapseLocally: function(topic1id, topic2id) {
|
||||||
var self = Synapse
|
var self = Synapse
|
||||||
let topic1
|
|
||||||
let topic2
|
|
||||||
let node1
|
|
||||||
let node2
|
|
||||||
let synapse
|
|
||||||
let mapping
|
|
||||||
|
|
||||||
$(document).trigger(Map.events.editedByActiveMapper)
|
$(document).trigger(Map.events.editedByActiveMapper)
|
||||||
|
|
||||||
// for each node in this array we will create a synapse going to the position2 node.
|
// for each node in this array we will create a synapse going to the position2 node.
|
||||||
var synapsesToCreate = []
|
const synapsesToCreate = []
|
||||||
|
const topic2 = DataModel.Topics.get(topic2id)
|
||||||
topic2 = DataModel.Topics.get(Create.newSynapse.topic2id)
|
const node2 = topic2.get('node')
|
||||||
node2 = topic2.get('node')
|
if (Selected.Nodes.length === 0) {
|
||||||
|
synapsesToCreate.push(DataModel.Topics.get(topic1id).get('node'))
|
||||||
var len = Selected.Nodes.length
|
} else {
|
||||||
if (len === 0) {
|
synapsesToCreate.concat(Selected.Nodes)
|
||||||
topic1 = DataModel.Topics.get(Create.newSynapse.topic1id)
|
|
||||||
synapsesToCreate[0] = topic1.get('node')
|
|
||||||
} else if (len > 0) {
|
|
||||||
synapsesToCreate = Selected.Nodes
|
|
||||||
}
|
}
|
||||||
|
synapsesToCreate.forEach(node1 => {
|
||||||
for (var i = 0; i < synapsesToCreate.length; i++) {
|
const topic1 = node1.getData('topic')
|
||||||
node1 = synapsesToCreate[i]
|
const synapse = new DataModel.Synapse({
|
||||||
topic1 = node1.getData('topic')
|
desc: Create.newSynapse.description || '',
|
||||||
synapse = new DataModel.Synapse({
|
topic1_id: topic1.id,
|
||||||
desc: Create.newSynapse.description,
|
topic2_id: topic2.id
|
||||||
topic1_id: topic1.isNew() ? topic1.cid : topic1.id,
|
|
||||||
topic2_id: topic2.isNew() ? topic2.cid : topic2.id
|
|
||||||
})
|
})
|
||||||
DataModel.Synapses.add(synapse)
|
DataModel.Synapses.add(synapse)
|
||||||
|
const mapping = new DataModel.Mapping({
|
||||||
mapping = new DataModel.Mapping({
|
|
||||||
mappable_type: 'Synapse',
|
mappable_type: 'Synapse',
|
||||||
mappable_id: synapse.cid
|
mappable_id: synapse.cid
|
||||||
})
|
})
|
||||||
DataModel.Mappings.add(mapping)
|
DataModel.Mappings.add(mapping)
|
||||||
|
|
||||||
// this function also includes the creation of the synapse in the database
|
// this function also includes the creation of the synapse in the database
|
||||||
self.renderSynapse(mapping, synapse, node1, node2, true)
|
self.renderSynapse(mapping, synapse, node1, node2)
|
||||||
} // for each in synapsesToCreate
|
}) // for each in synapsesToCreate
|
||||||
|
|
||||||
Create.newSynapse.hide()
|
|
||||||
},
|
},
|
||||||
getSynapseFromAutocomplete: function(id) {
|
getSynapseFromAutocomplete: function(id) {
|
||||||
var self = Synapse
|
var self = Synapse
|
||||||
|
@ -127,7 +86,8 @@ const Synapse = {
|
||||||
const topic2 = DataModel.Topics.get(Create.newSynapse.topic2id)
|
const topic2 = DataModel.Topics.get(Create.newSynapse.topic2id)
|
||||||
const node2 = topic2.get('node')
|
const node2 = topic2.get('node')
|
||||||
Create.newSynapse.hide()
|
Create.newSynapse.hide()
|
||||||
self.renderSynapse(mapping, synapse, node1, node2, true)
|
self.renderSynapse(mapping, synapse, node1, node2)
|
||||||
|
Engine.runLayout()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,16 @@ import Active from './Active'
|
||||||
import AutoLayout from './AutoLayout'
|
import AutoLayout from './AutoLayout'
|
||||||
import Create from './Create'
|
import Create from './Create'
|
||||||
import DataModel from './DataModel'
|
import DataModel from './DataModel'
|
||||||
|
import Engine from './Engine'
|
||||||
import Filter from './Filter'
|
import Filter from './Filter'
|
||||||
import GlobalUI from './GlobalUI'
|
import GlobalUI from './GlobalUI'
|
||||||
import JIT from './JIT'
|
import JIT from './JIT'
|
||||||
import Map from './Map'
|
import Map from './Map'
|
||||||
|
import Mouse from './Mouse'
|
||||||
import Router from './Router'
|
import Router from './Router'
|
||||||
import Selected from './Selected'
|
import Selected from './Selected'
|
||||||
import Settings from './Settings'
|
import Settings from './Settings'
|
||||||
|
import Synapse from './Synapse'
|
||||||
import SynapseCard from './SynapseCard'
|
import SynapseCard from './SynapseCard'
|
||||||
import TopicCard from './TopicCard'
|
import TopicCard from './TopicCard'
|
||||||
import Util from './Util'
|
import Util from './Util'
|
||||||
|
@ -165,192 +168,86 @@ const Topic = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// opts is additional options in a hash
|
renderTopic: function(mapping, topic, fromRemote) {
|
||||||
// TODO: move createNewInDB and permitCreateSynapseAfter into opts
|
let nodeOnViz
|
||||||
renderTopic: function(mapping, topic, createNewInDB, permitCreateSynapseAfter, opts = {}) {
|
const newnode = topic.createNode()
|
||||||
var nodeOnViz, tempPos
|
const createSynapse = !!Create.newSynapse.focusNode && !fromRemote
|
||||||
|
const connectToId = createSynapse ? Create.newSynapse.focusNode.getData('topic').id : null
|
||||||
var newnode = topic.createNode()
|
|
||||||
|
|
||||||
var midpoint = {}
|
|
||||||
var pixelPos
|
|
||||||
|
|
||||||
if (!$.isEmptyObject(Visualize.mGraph.graph.nodes)) {
|
if (!$.isEmptyObject(Visualize.mGraph.graph.nodes)) {
|
||||||
Visualize.mGraph.graph.addNode(newnode)
|
Visualize.mGraph.graph.addNode(newnode)
|
||||||
nodeOnViz = Visualize.mGraph.graph.getNode(newnode.id)
|
|
||||||
topic.set('node', nodeOnViz, {silent: true})
|
|
||||||
topic.updateNode() // links the topic and the mapping to the node
|
|
||||||
|
|
||||||
nodeOnViz.setData('dim', 1, 'start')
|
|
||||||
nodeOnViz.setData('dim', 25, 'end')
|
|
||||||
if (Visualize.type === 'RGraph') {
|
|
||||||
tempPos = new $jit.Complex(mapping.get('xloc'), mapping.get('yloc'))
|
|
||||||
tempPos = tempPos.toPolar()
|
|
||||||
nodeOnViz.setPos(tempPos, 'current')
|
|
||||||
nodeOnViz.setPos(tempPos, 'start')
|
|
||||||
nodeOnViz.setPos(tempPos, 'end')
|
|
||||||
} else if (Visualize.type === 'ForceDirected') {
|
|
||||||
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'current')
|
|
||||||
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'start')
|
|
||||||
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'end')
|
|
||||||
}
|
|
||||||
if (Create.newTopic.addSynapse && permitCreateSynapseAfter) {
|
|
||||||
Create.newSynapse.topic1id = JIT.tempNode.getData('topic').id
|
|
||||||
|
|
||||||
// position the form
|
|
||||||
midpoint.x = JIT.tempNode.pos.getc().x + (nodeOnViz.pos.getc().x - JIT.tempNode.pos.getc().x) / 2
|
|
||||||
midpoint.y = JIT.tempNode.pos.getc().y + (nodeOnViz.pos.getc().y - JIT.tempNode.pos.getc().y) / 2
|
|
||||||
pixelPos = Util.coordsToPixels(Visualize.mGraph, midpoint)
|
|
||||||
$('#new_synapse').css('left', pixelPos.x + 'px')
|
|
||||||
$('#new_synapse').css('top', pixelPos.y + 'px')
|
|
||||||
// show the form
|
|
||||||
Create.newSynapse.open()
|
|
||||||
Visualize.mGraph.fx.animate({
|
|
||||||
modes: ['node-property:dim'],
|
|
||||||
duration: 500,
|
|
||||||
onComplete: function() {
|
|
||||||
JIT.tempNode = null
|
|
||||||
JIT.tempNode2 = null
|
|
||||||
JIT.tempInit = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Visualize.mGraph.fx.plotNode(nodeOnViz, Visualize.mGraph.canvas)
|
|
||||||
Visualize.mGraph.fx.animate({
|
|
||||||
modes: ['node-property:dim'],
|
|
||||||
duration: 500,
|
|
||||||
onComplete: function() {}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Visualize.mGraph.loadJSON(newnode)
|
Visualize.mGraph.loadJSON(newnode)
|
||||||
nodeOnViz = Visualize.mGraph.graph.getNode(newnode.id)
|
}
|
||||||
topic.set('node', nodeOnViz, {silent: true})
|
nodeOnViz = Visualize.mGraph.graph.getNode(newnode.id)
|
||||||
topic.updateNode() // links the topic and the mapping to the node
|
topic.set('node', nodeOnViz, {silent: true})
|
||||||
|
topic.updateNode() // links the topic and the mapping to the node
|
||||||
nodeOnViz.setData('dim', 1, 'start')
|
nodeOnViz.setData('dim', 1, 'start')
|
||||||
nodeOnViz.setData('dim', 25, 'end')
|
nodeOnViz.setData('dim', 25, 'end')
|
||||||
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'current')
|
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'current')
|
||||||
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'start')
|
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'start')
|
||||||
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'end')
|
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'end')
|
||||||
Visualize.mGraph.fx.plotNode(nodeOnViz, Visualize.mGraph.canvas)
|
Visualize.mGraph.fx.plotNode(nodeOnViz, Visualize.mGraph.canvas)
|
||||||
Visualize.mGraph.fx.animate({
|
Visualize.mGraph.fx.animate({
|
||||||
modes: ['node-property:dim'],
|
modes: ['node-property:dim'],
|
||||||
duration: 500,
|
duration: 200
|
||||||
onComplete: function() {}
|
})
|
||||||
|
if (!fromRemote && topic.isNew()) {
|
||||||
|
topic.save(null, {
|
||||||
|
success: topicModel => {
|
||||||
|
Active.Map && mapping.save({ mappable_id: topicModel.id })
|
||||||
|
createSynapse && Synapse.createSynapseLocally(connectToId, topicModel.id)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
} else if (!fromRemote && !topic.isNew()) {
|
||||||
|
Active.Map && mapping.save()
|
||||||
var mappingSuccessCallback = function(mappingModel, response, topicModel) {
|
createSynapse && Synapse.createSynapseLocally(connectToId, topic.id)
|
||||||
// call a success callback if provided
|
|
||||||
if (opts.success) {
|
|
||||||
opts.success(topicModel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var topicSuccessCallback = function(topicModel, response) {
|
|
||||||
if (Active.Map) {
|
|
||||||
mapping.save({ mappable_id: topicModel.id }, {
|
|
||||||
success: function(model, response) {
|
|
||||||
mappingSuccessCallback(model, response, topicModel)
|
|
||||||
},
|
|
||||||
error: function(model, response) {
|
|
||||||
console.log('error saving mapping to database')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Create.newTopic.addSynapse) {
|
|
||||||
Create.newSynapse.topic2id = topicModel.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Settings.sandbox && createNewInDB) {
|
|
||||||
if (topic.isNew()) {
|
|
||||||
topic.save(null, {
|
|
||||||
success: topicSuccessCallback,
|
|
||||||
error: function(model, response) {
|
|
||||||
console.log('error saving topic to database')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (!topic.isNew() && Active.Map) {
|
|
||||||
mapping.save(null, {
|
|
||||||
success: mappingSuccessCallback
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createTopicLocally: function() {
|
createTopicLocally: function() {
|
||||||
var self = Topic
|
var self = Topic
|
||||||
|
|
||||||
if (Create.newTopic.name === '') {
|
if (Create.newTopic.name === '') {
|
||||||
GlobalUI.notifyUser('Please enter a topic title...')
|
GlobalUI.notifyUser('Please enter a topic title...')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// hide the 'double-click to add a topic' message
|
|
||||||
GlobalUI.hideDiv('#instructions')
|
|
||||||
|
|
||||||
$(document).trigger(Map.events.editedByActiveMapper)
|
$(document).trigger(Map.events.editedByActiveMapper)
|
||||||
|
|
||||||
var metacode = DataModel.Metacodes.get(Create.newTopic.metacode)
|
var metacode = DataModel.Metacodes.get(Create.newTopic.metacode)
|
||||||
|
|
||||||
var topic = new DataModel.Topic({
|
var topic = new DataModel.Topic({
|
||||||
name: Create.newTopic.name,
|
name: Create.newTopic.name,
|
||||||
metacode_id: metacode.id,
|
metacode_id: metacode.id,
|
||||||
defer_to_map_id: Active.Map.id
|
defer_to_map_id: Active.Map.id
|
||||||
})
|
})
|
||||||
DataModel.Topics.add(topic)
|
DataModel.Topics.add(topic)
|
||||||
|
|
||||||
if (Create.newTopic.pinned) {
|
|
||||||
var nextCoords = AutoLayout.getNextCoord({ mappings: DataModel.Mappings })
|
|
||||||
}
|
|
||||||
var mapping = new DataModel.Mapping({
|
var mapping = new DataModel.Mapping({
|
||||||
xloc: nextCoords ? nextCoords.x : Create.newTopic.x,
|
xloc: Mouse.newNodeCoords.x,
|
||||||
yloc: nextCoords ? nextCoords.y : Create.newTopic.y,
|
yloc: Mouse.newNodeCoords.y,
|
||||||
mappable_id: topic.cid,
|
mappable_id: topic.cid,
|
||||||
mappable_type: 'Topic'
|
mappable_type: 'Topic'
|
||||||
})
|
})
|
||||||
DataModel.Mappings.add(mapping)
|
DataModel.Mappings.add(mapping)
|
||||||
|
// these can't happen until the new topic values are retrieved
|
||||||
// these can't happen until the value is retrieved, which happens in the line above
|
|
||||||
if (!Create.newTopic.pinned) Create.newTopic.hide()
|
|
||||||
Create.newTopic.reset()
|
Create.newTopic.reset()
|
||||||
|
self.renderTopic(mapping, topic)
|
||||||
self.renderTopic(mapping, topic, true, true) // this function also includes the creation of the topic in the database
|
Engine.setFocusNode(topic.get('node'), false, true)
|
||||||
},
|
},
|
||||||
getTopicFromAutocomplete: function(id) {
|
getTopicFromAutocomplete: function(id) {
|
||||||
var self = Topic
|
var self = Topic
|
||||||
|
|
||||||
// hide the 'double-click to add a topic' message
|
|
||||||
GlobalUI.hideDiv('#instructions')
|
|
||||||
|
|
||||||
$(document).trigger(Map.events.editedByActiveMapper)
|
$(document).trigger(Map.events.editedByActiveMapper)
|
||||||
|
|
||||||
if (!Create.newTopic.pinned) Create.newTopic.hide()
|
|
||||||
Create.newTopic.reset()
|
Create.newTopic.reset()
|
||||||
|
|
||||||
self.get(id, (topic) => {
|
self.get(id, (topic) => {
|
||||||
if (Create.newTopic.pinned) {
|
|
||||||
var nextCoords = AutoLayout.getNextCoord({ mappings: DataModel.Mappings })
|
|
||||||
}
|
|
||||||
var mapping = new DataModel.Mapping({
|
var mapping = new DataModel.Mapping({
|
||||||
xloc: nextCoords ? nextCoords.x : Create.newTopic.x,
|
xloc: Mouse.newNodeCoords.x,
|
||||||
yloc: nextCoords ? nextCoords.y : Create.newTopic.y,
|
yloc: Mouse.newNodeCoords.y,
|
||||||
mappable_type: 'Topic',
|
mappable_type: 'Topic',
|
||||||
mappable_id: topic.id
|
mappable_id: topic.id
|
||||||
})
|
})
|
||||||
DataModel.Mappings.add(mapping)
|
DataModel.Mappings.add(mapping)
|
||||||
|
self.renderTopic(mapping, topic)
|
||||||
self.renderTopic(mapping, topic, true, true)
|
Engine.setFocusNode(topic.get('node'), false, true)
|
||||||
// this blocked the enterKeyHandler from creating a new topic as well
|
|
||||||
if (Create.newTopic.pinned) Create.newTopic.beingCreated = true
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getMapFromAutocomplete: function(data) {
|
getMapFromAutocomplete: function(data) {
|
||||||
var self = Topic
|
var self = Topic
|
||||||
|
|
||||||
$(document).trigger(Map.events.editedByActiveMapper)
|
$(document).trigger(Map.events.editedByActiveMapper)
|
||||||
|
|
||||||
var metacode = DataModel.Metacodes.findWhere({ name: 'Metamap' })
|
var metacode = DataModel.Metacodes.findWhere({ name: 'Metamap' })
|
||||||
var topic = new DataModel.Topic({
|
var topic = new DataModel.Topic({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
@ -359,28 +256,20 @@ const Topic = {
|
||||||
link: window.location.origin + '/maps/' + data.id
|
link: window.location.origin + '/maps/' + data.id
|
||||||
})
|
})
|
||||||
DataModel.Topics.add(topic)
|
DataModel.Topics.add(topic)
|
||||||
|
|
||||||
var mapping = new DataModel.Mapping({
|
var mapping = new DataModel.Mapping({
|
||||||
xloc: Create.newTopic.x,
|
xloc: Mouse.newNodeCoords.x,
|
||||||
yloc: Create.newTopic.y,
|
yloc: Mouse.newNodeCoords.y,
|
||||||
mappable_id: topic.cid,
|
mappable_id: topic.cid,
|
||||||
mappable_type: 'Topic'
|
mappable_type: 'Topic'
|
||||||
})
|
})
|
||||||
DataModel.Mappings.add(mapping)
|
DataModel.Mappings.add(mapping)
|
||||||
|
|
||||||
// these can't happen until the value is retrieved, which happens in the line above
|
|
||||||
if (!Create.newTopic.pinned) Create.newTopic.hide()
|
|
||||||
Create.newTopic.reset()
|
Create.newTopic.reset()
|
||||||
|
self.renderTopic(mapping, topic)
|
||||||
self.renderTopic(mapping, topic, true, true) // this function also includes the creation of the topic in the database
|
Engine.setFocusNode(topic.get('node'), false, true)
|
||||||
// this blocked the enterKeyHandler from creating a new topic as well
|
|
||||||
if (Create.newTopic.pinned) Create.newTopic.beingCreated = true
|
|
||||||
},
|
},
|
||||||
getTopicFromSearch: function(event, id) {
|
getTopicFromSearch: function(event, id) {
|
||||||
var self = Topic
|
var self = Topic
|
||||||
|
|
||||||
$(document).trigger(Map.events.editedByActiveMapper)
|
$(document).trigger(Map.events.editedByActiveMapper)
|
||||||
|
|
||||||
self.get(id, (topic) => {
|
self.get(id, (topic) => {
|
||||||
var nextCoords = AutoLayout.getNextCoord({ mappings: DataModel.Mappings })
|
var nextCoords = AutoLayout.getNextCoord({ mappings: DataModel.Mappings })
|
||||||
var mapping = new DataModel.Mapping({
|
var mapping = new DataModel.Mapping({
|
||||||
|
@ -390,10 +279,10 @@ const Topic = {
|
||||||
mappable_id: topic.id
|
mappable_id: topic.id
|
||||||
})
|
})
|
||||||
DataModel.Mappings.add(mapping)
|
DataModel.Mappings.add(mapping)
|
||||||
self.renderTopic(mapping, topic, true, true)
|
self.renderTopic(mapping, topic)
|
||||||
GlobalUI.notifyUser('Topic was added to your map!')
|
Engine.runLayout()
|
||||||
|
GlobalUI.notifyUser('Topic was added to your map')
|
||||||
})
|
})
|
||||||
|
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -6,6 +6,7 @@ import $jit from '../patched/JIT'
|
||||||
|
|
||||||
import Active from './Active'
|
import Active from './Active'
|
||||||
import DataModel from './DataModel'
|
import DataModel from './DataModel'
|
||||||
|
import Engine from './Engine'
|
||||||
import JIT from './JIT'
|
import JIT from './JIT'
|
||||||
import Loading from './Loading'
|
import Loading from './Loading'
|
||||||
import Router from './Router'
|
import Router from './Router'
|
||||||
|
@ -94,10 +95,14 @@ const Visualize = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const startPos = new $jit.Complex(0, 0)
|
//const startPos = new $jit.Complex(0, 0)
|
||||||
const endPos = new $jit.Complex(mapping.get('xloc'), mapping.get('yloc'))
|
const endPos = new $jit.Complex(0, 0)
|
||||||
n.setPos(startPos, 'start')
|
//n.setPos(startPos, 'start')
|
||||||
|
//n.setPos(endPos, 'end')
|
||||||
|
n.setPos(endPos, 'current')
|
||||||
n.setPos(endPos, 'end')
|
n.setPos(endPos, 'end')
|
||||||
|
n.setData('dim', 1, 'start')
|
||||||
|
n.setData('dim', 25, 'end')
|
||||||
})
|
})
|
||||||
} else if (self.type === 'ForceDirected3D') {
|
} else if (self.type === 'ForceDirected3D') {
|
||||||
self.mGraph.compute()
|
self.mGraph.compute()
|
||||||
|
@ -151,6 +156,8 @@ const Visualize = {
|
||||||
|
|
||||||
function runAnimation() {
|
function runAnimation() {
|
||||||
Loading.hide()
|
Loading.hide()
|
||||||
|
$('#new_topic').show()
|
||||||
|
$('#topic_name').focus()
|
||||||
// load JSON data, if it's not empty
|
// load JSON data, if it's not empty
|
||||||
if (!self.loadLater) {
|
if (!self.loadLater) {
|
||||||
// load JSON data.
|
// load JSON data.
|
||||||
|
@ -168,7 +175,8 @@ const Visualize = {
|
||||||
if (self.type === 'RGraph') {
|
if (self.type === 'RGraph') {
|
||||||
self.mGraph.fx.animate(JIT.RGraph.animate)
|
self.mGraph.fx.animate(JIT.RGraph.animate)
|
||||||
} else if (self.type === 'ForceDirected') {
|
} else if (self.type === 'ForceDirected') {
|
||||||
self.mGraph.animate(JIT.ForceDirected.animateSavedLayout)
|
self.mGraph.plot()
|
||||||
|
Engine.run(true)
|
||||||
} else if (self.type === 'ForceDirected3D') {
|
} else if (self.type === 'ForceDirected3D') {
|
||||||
self.mGraph.animate(JIT.ForceDirected.animateFDLayout)
|
self.mGraph.animate(JIT.ForceDirected.animateFDLayout)
|
||||||
}
|
}
|
||||||
|
@ -204,6 +212,8 @@ const Visualize = {
|
||||||
Router.timeoutId = setTimeout(function() {
|
Router.timeoutId = setTimeout(function() {
|
||||||
var m = Active.Map
|
var m = Active.Map
|
||||||
var t = Active.Topic
|
var t = Active.Topic
|
||||||
|
|
||||||
|
if (m && window.location.pathname === '/maps/' + m.id + '/conversation') return
|
||||||
|
|
||||||
if (m && window.location.pathname !== '/maps/' + m.id) {
|
if (m && window.location.pathname !== '/maps/' + m.id) {
|
||||||
Router.navigateAndTrack('/maps/' + m.id)
|
Router.navigateAndTrack('/maps/' + m.id)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Control from './Control'
|
||||||
import Create from './Create'
|
import Create from './Create'
|
||||||
import DataModel from './DataModel'
|
import DataModel from './DataModel'
|
||||||
import Debug from './Debug'
|
import Debug from './Debug'
|
||||||
|
import Engine from './Engine'
|
||||||
import Filter from './Filter'
|
import Filter from './Filter'
|
||||||
import GlobalUI, {
|
import GlobalUI, {
|
||||||
Search, CreateMap, ImportDialog, Account as GlobalUIAccount,
|
Search, CreateMap, ImportDialog, Account as GlobalUIAccount,
|
||||||
|
@ -44,6 +45,7 @@ Metamaps.Control = Control
|
||||||
Metamaps.Create = Create
|
Metamaps.Create = Create
|
||||||
Metamaps.DataModel = DataModel
|
Metamaps.DataModel = DataModel
|
||||||
Metamaps.Debug = Debug
|
Metamaps.Debug = Debug
|
||||||
|
Metamaps.Engine = Engine
|
||||||
Metamaps.Filter = Filter
|
Metamaps.Filter = Filter
|
||||||
Metamaps.GlobalUI = GlobalUI
|
Metamaps.GlobalUI = GlobalUI
|
||||||
Metamaps.GlobalUI.Search = Search
|
Metamaps.GlobalUI.Search = Search
|
||||||
|
|
125
frontend/src/components/MetacodeSelect.js
Normal file
125
frontend/src/components/MetacodeSelect.js
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/* global $ */
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react'
|
||||||
|
|
||||||
|
const ENTER_KEY = 13
|
||||||
|
const LEFT_ARROW = 37
|
||||||
|
const UP_ARROW = 38
|
||||||
|
const RIGHT_ARROW = 39
|
||||||
|
const DOWN_ARROW = 40
|
||||||
|
|
||||||
|
const Metacode = (props) => {
|
||||||
|
const { m, onClick, underCursor } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li onClick={() => onClick(m.id) } className={ underCursor ? 'keySelect' : '' }>
|
||||||
|
<img src={ m.get('icon') } />
|
||||||
|
<span>{ m.get('name') }</span>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class MetacodeSelect extends Component {
|
||||||
|
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
filterText: '',
|
||||||
|
selectingSection: true,
|
||||||
|
underCursor: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const self = this
|
||||||
|
setTimeout(function() {
|
||||||
|
$(document.body).on('keyup.metacodeSelect', self.handleKeyUp.bind(self))
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
$(document.body).off('.metacodeSelect')
|
||||||
|
}
|
||||||
|
|
||||||
|
changeFilterText (e) {
|
||||||
|
this.setState({ filterText: e.target.value, underCursor: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectMetacodes () {
|
||||||
|
const { metacodes, recent, mostUsed } = this.props
|
||||||
|
const { filterText, activeTab } = this.state
|
||||||
|
|
||||||
|
let selectMetacodes = metacodes
|
||||||
|
if (filterText.length > 1) { // search
|
||||||
|
selectMetacodes = filterText.length > 1 ? metacodes.filter(m => {
|
||||||
|
return m.get('name').toLowerCase().search(filterText.toLowerCase()) > -1
|
||||||
|
}) : []
|
||||||
|
}
|
||||||
|
return selectMetacodes
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyUp (e) {
|
||||||
|
const { close } = this.props
|
||||||
|
const { underCursor } = this.state
|
||||||
|
const selectMetacodes = this.getSelectMetacodes()
|
||||||
|
let nextIndex
|
||||||
|
|
||||||
|
switch (e.which) {
|
||||||
|
case ENTER_KEY:
|
||||||
|
if (selectMetacodes.length) this.resetAndClick(selectMetacodes[underCursor].id)
|
||||||
|
break
|
||||||
|
case UP_ARROW:
|
||||||
|
if (underCursor == 0) {
|
||||||
|
close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextIndex = underCursor == 0 ? selectMetacodes.length - 1 : underCursor - 1
|
||||||
|
this.setState({ underCursor: nextIndex })
|
||||||
|
break
|
||||||
|
case DOWN_ARROW:
|
||||||
|
nextIndex = underCursor == selectMetacodes.length - 1 ? 0 : underCursor + 1
|
||||||
|
this.setState({ underCursor: nextIndex })
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetAndClick (id) {
|
||||||
|
const { onClick } = this.props
|
||||||
|
this.setState({ filterText: '', underCursor: 0 })
|
||||||
|
onClick(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { onClick, close } = this.props
|
||||||
|
const { filterText, underCursor } = this.state
|
||||||
|
const selectMetacodes = this.getSelectMetacodes()
|
||||||
|
return <div className='metacodeSelect'>
|
||||||
|
<div className='tabList'>
|
||||||
|
<input type='text'
|
||||||
|
className='metacodeFilterInput'
|
||||||
|
placeholder='Search...'
|
||||||
|
ref='input'
|
||||||
|
value={ filterText }
|
||||||
|
onChange={ this.changeFilterText.bind(this) } />
|
||||||
|
<ul className='metacodeList'>
|
||||||
|
{ selectMetacodes.map((m, index) => {
|
||||||
|
return <Metacode underCursor={underCursor == index}
|
||||||
|
key={m.id}
|
||||||
|
m={m}
|
||||||
|
onClick={this.resetAndClick.bind(this)} />
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
<div className='clearfloat'></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MetacodeSelect.propTypes = {
|
||||||
|
onClick: PropTypes.func.isRequired,
|
||||||
|
close: PropTypes.func.isRequired,
|
||||||
|
metacodes: PropTypes.array.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MetacodeSelect
|
||||||
|
|
|
@ -2560,7 +2560,11 @@ Extras.Classes.Navigation = new Class({
|
||||||
}
|
}
|
||||||
if (Metamaps.Mouse.boxStartCoordinates && ((e.button == 0 && e.shiftKey) || (e.button == 0 && e.ctrlKey) || rightClick)) {
|
if (Metamaps.Mouse.boxStartCoordinates && ((e.button == 0 && e.shiftKey) || (e.button == 0 && e.ctrlKey) || rightClick)) {
|
||||||
Metamaps.Visualize.mGraph.busy = true;
|
Metamaps.Visualize.mGraph.busy = true;
|
||||||
Metamaps.JIT.drawSelectBox(eventInfo,e);
|
Metamaps.Mouse.boxEndCoordinates = {
|
||||||
|
x: eventInfo.getPos().x,
|
||||||
|
y: eventInfo.getPos().y
|
||||||
|
}
|
||||||
|
Metamaps.Visualize.mGraph.plot()
|
||||||
//console.log('mouse move');
|
//console.log('mouse move');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2606,9 +2610,7 @@ Extras.Classes.Navigation = new Class({
|
||||||
this.pressed = false;
|
this.pressed = false;
|
||||||
|
|
||||||
// START METAMAPS CODE
|
// START METAMAPS CODE
|
||||||
if (Metamaps.Mouse.didPan) Metamaps.JIT.SmoothPanning();
|
if (Metamaps.Mouse.didPan) Metamaps.JIT.SmoothPanning();
|
||||||
|
|
||||||
|
|
||||||
// END METAMAPS CODE
|
// END METAMAPS CODE
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -2651,7 +2653,10 @@ Extras.Classes.Navigation = new Class({
|
||||||
}
|
}
|
||||||
if (Metamaps.Mouse.boxStartCoordinates && ((e.button == 0 && e.shiftKey) || (e.button == 0 && e.ctrlKey) || rightClick)) {
|
if (Metamaps.Mouse.boxStartCoordinates && ((e.button == 0 && e.shiftKey) || (e.button == 0 && e.ctrlKey) || rightClick)) {
|
||||||
Metamaps.Visualize.mGraph.busy = true;
|
Metamaps.Visualize.mGraph.busy = true;
|
||||||
Metamaps.JIT.drawSelectBox(eventInfo,e);
|
Metamaps.Mouse.boxEndCoordinates = {
|
||||||
|
x: eventInfo.getPos().x,
|
||||||
|
y: eventInfo.getPos().y
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rightClick){
|
if (rightClick){
|
||||||
|
@ -7225,7 +7230,7 @@ Graph.Plot = {
|
||||||
var T = !!root.visited;
|
var T = !!root.visited;
|
||||||
|
|
||||||
//START METAMAPS CODE
|
//START METAMAPS CODE
|
||||||
if (Metamaps.Mouse.synapseStartCoordinates.length > 0) {
|
if (Metamaps.Mouse.synapseStartCoordinates.length > 0 && Metamaps.Mouse.synapseEndCoordinates) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
var start;
|
var start;
|
||||||
var end = Metamaps.Mouse.synapseEndCoordinates;
|
var end = Metamaps.Mouse.synapseEndCoordinates;
|
||||||
|
@ -7238,6 +7243,26 @@ Graph.Plot = {
|
||||||
}
|
}
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Metamaps.Mouse.focusNodeCoords) {
|
||||||
|
ctx.save();
|
||||||
|
Metamaps.JIT.renderMidArrow(Metamaps.Mouse.focusNodeCoords, Metamaps.Mouse.newNodeCoords, 13, false, canvas, 0.3, true);
|
||||||
|
Metamaps.JIT.renderMidArrow(Metamaps.Mouse.focusNodeCoords, Metamaps.Mouse.newNodeCoords, 13, false, canvas, 0.7, true);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Metamaps.Mouse.boxStartCoordinates && Metamaps.Mouse.boxEndCoordinates) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(Metamaps.Mouse.boxStartCoordinates.x, Metamaps.Mouse.boxStartCoordinates.y)
|
||||||
|
ctx.lineTo(Metamaps.Mouse.boxStartCoordinates.x, Metamaps.Mouse.boxEndCoordinates.y)
|
||||||
|
ctx.lineTo(Metamaps.Mouse.boxEndCoordinates.x, Metamaps.Mouse.boxEndCoordinates.y)
|
||||||
|
ctx.lineTo(Metamaps.Mouse.boxEndCoordinates.x, Metamaps.Mouse.boxStartCoordinates.y)
|
||||||
|
ctx.lineTo(Metamaps.Mouse.boxStartCoordinates.x, Metamaps.Mouse.boxStartCoordinates.y)
|
||||||
|
ctx.strokeStyle = 'black'
|
||||||
|
ctx.stroke()
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
//END METAMAPS CODE
|
//END METAMAPS CODE
|
||||||
|
|
||||||
aGraph.eachNode(function(node) {
|
aGraph.eachNode(function(node) {
|
||||||
|
|
|
@ -40,10 +40,10 @@ module.exports = {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
entry: {
|
entry: {
|
||||||
'metamaps.bundle': './frontend/src/index.js'
|
'metamaps.secret.bundle': './frontend/src/index.js'
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: './app/assets/javascripts/webpacked',
|
path: './app/assets/javascripts',
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
devtoolModuleFilenameTemplate: '[absolute-resource-path]'
|
devtoolModuleFilenameTemplate: '[absolute-resource-path]'
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue