metamaps--metamaps/frontend/src/Metamaps/Topic.js

404 lines
13 KiB
JavaScript
Raw Normal View History

/* global $ */
2016-04-14 06:12:12 +00:00
import $jit from '../patched/JIT'
2016-09-22 09:36:47 +00:00
import Active from './Active'
2016-09-22 10:31:56 +00:00
import AutoLayout from './AutoLayout'
import Create from './Create'
import DataModel from './DataModel'
2016-09-22 10:31:56 +00:00
import Filter from './Filter'
import GlobalUI from './GlobalUI'
2016-09-22 09:36:47 +00:00
import JIT from './JIT'
2016-09-22 10:31:56 +00:00
import Map from './Map'
import Router from './Router'
2016-09-22 09:36:47 +00:00
import Selected from './Selected'
import Settings from './Settings'
2016-09-22 10:31:56 +00:00
import SynapseCard from './SynapseCard'
import TopicCard from './TopicCard'
2016-09-22 09:36:47 +00:00
import Util from './Util'
2016-09-22 10:31:56 +00:00
import Visualize from './Visualize'
2016-09-22 09:36:47 +00:00
const noOp = () => {}
const Topic = {
2016-04-14 06:16:16 +00:00
// this function is to retrieve a topic JSON object from the database
// @param id = the id of the topic to retrieve
2016-11-07 20:25:08 +00:00
get: function(id, callback = noOp) {
2016-04-14 06:16:16 +00:00
// if the desired topic is not yet in the local topic repository, fetch it
2016-11-07 20:25:08 +00:00
if (DataModel.Topics.get(id) === undefined) {
$.ajax({
url: '/topics/' + id + '.json',
2016-11-07 20:25:08 +00:00
success: function(data) {
DataModel.Topics.add(data)
callback(DataModel.Topics.get(id))
}
})
} else callback(DataModel.Topics.get(id))
2016-04-14 06:16:16 +00:00
},
2016-11-07 20:25:08 +00:00
launch: function(id) {
var start = function(data) {
Active.Topic = new DataModel.Topic(data.topic)
DataModel.Creators = new DataModel.MapperCollection(data.creators)
DataModel.Topics = new DataModel.TopicCollection([data.topic].concat(data.relatives))
DataModel.Synapses = new DataModel.SynapseCollection(data.synapses)
DataModel.attachCollectionEvents()
2016-04-14 06:16:16 +00:00
2016-10-02 21:37:14 +00:00
document.title = Active.Topic.get('name') + ' | Metamaps'
2016-04-14 06:16:16 +00:00
// set filter mapper H3 text
$('#filter_by_mapper h3').html('CREATORS')
// build and render the visualization
2016-09-22 10:31:56 +00:00
Visualize.type = 'RGraph'
2016-09-22 09:36:47 +00:00
JIT.prepareVizData()
2016-04-14 06:16:16 +00:00
// update filters
2016-09-22 10:31:56 +00:00
Filter.reset()
2016-04-14 06:16:16 +00:00
// reset selected arrays
2016-09-22 09:36:47 +00:00
Selected.reset()
2016-04-14 06:16:16 +00:00
// these three update the actual filter box with the right list items
2016-09-22 10:31:56 +00:00
Filter.checkMetacodes()
Filter.checkSynapses()
Filter.checkMappers()
2016-11-07 20:25:08 +00:00
2016-08-12 04:04:18 +00:00
// for mobile
2016-09-22 09:36:47 +00:00
$('#header_content').html(Active.Topic.get('name'))
2016-04-14 06:16:16 +00:00
}
2016-04-14 06:12:12 +00:00
2016-04-14 06:16:16 +00:00
$.ajax({
url: '/topics/' + id + '/network.json',
success: start
})
},
2016-11-07 20:25:08 +00:00
end: function() {
2016-09-22 09:36:47 +00:00
if (Active.Topic) {
2016-04-14 06:16:16 +00:00
$('.rightclickmenu').remove()
2016-09-22 10:31:56 +00:00
TopicCard.hideCard()
SynapseCard.hideCard()
Filter.close()
2016-04-14 06:16:16 +00:00
}
},
2016-11-07 20:25:08 +00:00
centerOn: function(nodeid, callback) {
// don't clash with fetchRelatives
2016-09-22 10:31:56 +00:00
if (!Visualize.mGraph.busy) {
Visualize.mGraph.onClick(nodeid, {
2016-04-14 06:16:16 +00:00
hideLabels: false,
duration: 1000,
2016-11-07 20:25:08 +00:00
onComplete: function() {
if (callback) callback()
}
2016-04-14 06:16:16 +00:00
})
2016-09-22 10:31:56 +00:00
Router.navigate('/topics/' + nodeid)
Active.Topic = DataModel.Topics.get(nodeid)
2016-04-14 06:16:16 +00:00
}
},
2016-11-07 20:25:08 +00:00
fetchRelatives: function(nodes, metacodeId) {
var self = this
var node = $.isArray(nodes) ? nodes[0] : nodes
2016-11-07 20:25:08 +00:00
var topics = DataModel.Topics.map(function(t) { return t.id })
var topicsString = topics.join()
2016-04-14 06:16:16 +00:00
2016-11-07 20:25:08 +00:00
var creators = DataModel.Creators.map(function(t) { return t.id })
var creatorsString = creators.join()
2016-04-14 06:16:16 +00:00
var topic = node.getData('topic')
2016-11-07 20:25:08 +00:00
var successCallback
successCallback = function(data) {
2016-09-22 10:31:56 +00:00
if (Visualize.mGraph.busy) {
// don't clash with centerOn
window.setTimeout(function() { successCallback(data) }, 100)
return
}
if (data.creators.length > 0) DataModel.Creators.add(data.creators)
if (data.topics.length > 0) DataModel.Topics.add(data.topics)
if (data.synapses.length > 0) DataModel.Synapses.add(data.synapses)
2016-04-14 06:16:16 +00:00
var topicColl = new DataModel.TopicCollection(data.topics)
2016-04-14 06:16:16 +00:00
topicColl.add(topic)
var synapseColl = new DataModel.SynapseCollection(data.synapses)
2016-04-14 06:16:16 +00:00
2016-09-22 09:36:47 +00:00
var graph = JIT.convertModelsToJIT(topicColl, synapseColl)[0]
2016-09-22 10:31:56 +00:00
Visualize.mGraph.op.sum(graph, {
2016-04-14 06:16:16 +00:00
type: 'fade',
duration: 500,
hideLabels: false
})
var i, l, t, s
2016-11-07 20:25:08 +00:00
Visualize.mGraph.graph.eachNode(function(n) {
t = DataModel.Topics.get(n.id)
2016-04-14 06:16:16 +00:00
t.set({ node: n }, { silent: true })
t.updateNode()
2016-11-07 20:25:08 +00:00
n.eachAdjacency(function(edge) {
2016-04-14 06:16:16 +00:00
if (!edge.getData('init')) {
edge.setData('init', true)
l = edge.getData('synapseIDs').length
for (i = 0; i < l; i++) {
s = DataModel.Synapses.get(edge.getData('synapseIDs')[i])
2016-04-14 06:16:16 +00:00
s.set({ edge: edge }, { silent: true })
s.updateEdge()
2016-04-14 06:12:12 +00:00
}
2016-04-14 06:16:16 +00:00
}
})
})
if ($.isArray(nodes) && nodes.length > 1) {
2016-11-07 20:25:08 +00:00
self.fetchRelatives(nodes.slice(1), metacodeId)
}
2016-04-14 06:16:16 +00:00
}
2016-04-14 06:12:12 +00:00
2016-11-07 20:25:08 +00:00
let paramsString = metacodeId ? 'metacode=' + metacodeId + '&' : ''
paramsString += 'network=' + topicsString + '&creators=' + creatorsString
2016-04-14 06:16:16 +00:00
$.ajax({
type: 'GET',
2016-04-14 06:16:16 +00:00
url: '/topics/' + topic.id + '/relatives.json?' + paramsString,
success: successCallback,
2016-11-07 20:25:08 +00:00
error: function() {}
2016-04-14 06:16:16 +00:00
})
},
// opts is additional options in a hash
// TODO: move createNewInDB and permitCreateSynapseAfter into opts
2016-11-07 20:25:08 +00:00
renderTopic: function(mapping, topic, createNewInDB, permitCreateSynapseAfter, opts = {}) {
2016-04-14 06:16:16 +00:00
var nodeOnViz, tempPos
var newnode = topic.createNode()
2016-11-07 20:25:08 +00:00
var midpoint = {}
var pixelPos
2016-04-14 06:16:16 +00:00
2016-09-22 10:31:56 +00:00
if (!$.isEmptyObject(Visualize.mGraph.graph.nodes)) {
Visualize.mGraph.graph.addNode(newnode)
nodeOnViz = Visualize.mGraph.graph.getNode(newnode.id)
2016-04-14 06:16:16 +00:00
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')
2016-09-22 10:31:56 +00:00
if (Visualize.type === 'RGraph') {
2016-04-14 06:16:16 +00:00
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')
2016-09-22 10:31:56 +00:00
} else if (Visualize.type === 'ForceDirected') {
2016-04-14 06:16:16 +00:00
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')
}
2016-09-22 10:31:56 +00:00
if (Create.newTopic.addSynapse && permitCreateSynapseAfter) {
Create.newSynapse.topic1id = JIT.tempNode.getData('topic').id
2016-04-14 06:16:16 +00:00
// position the form
2016-09-22 09:08:53 +00:00
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)
2016-04-14 06:16:16 +00:00
$('#new_synapse').css('left', pixelPos.x + 'px')
$('#new_synapse').css('top', pixelPos.y + 'px')
// show the form
2016-09-22 10:31:56 +00:00
Create.newSynapse.open()
Visualize.mGraph.fx.animate({
2016-04-14 06:16:16 +00:00
modes: ['node-property:dim'],
duration: 500,
2016-11-07 20:25:08 +00:00
onComplete: function() {
2016-09-22 09:08:53 +00:00
JIT.tempNode = null
JIT.tempNode2 = null
JIT.tempInit = false
2016-04-14 06:16:16 +00:00
}
})
} else {
2016-09-22 10:31:56 +00:00
Visualize.mGraph.fx.plotNode(nodeOnViz, Visualize.mGraph.canvas)
Visualize.mGraph.fx.animate({
2016-04-14 06:16:16 +00:00
modes: ['node-property:dim'],
duration: 500,
2016-11-07 20:25:08 +00:00
onComplete: function() {}
2016-04-14 06:16:16 +00:00
})
}
} else {
2016-09-22 10:31:56 +00:00
Visualize.mGraph.loadJSON(newnode)
nodeOnViz = Visualize.mGraph.graph.getNode(newnode.id)
2016-04-14 06:16:16 +00:00
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')
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')
2016-09-22 10:31:56 +00:00
Visualize.mGraph.fx.plotNode(nodeOnViz, Visualize.mGraph.canvas)
Visualize.mGraph.fx.animate({
2016-04-14 06:16:16 +00:00
modes: ['node-property:dim'],
duration: 500,
2016-11-07 20:25:08 +00:00
onComplete: function() {}
2016-04-14 06:16:16 +00:00
})
}
2016-11-07 20:25:08 +00:00
var mappingSuccessCallback = function(mappingModel, response, topicModel) {
// call a success callback if provided
if (opts.success) {
opts.success(topicModel)
}
2016-04-14 06:16:16 +00:00
}
2016-11-07 20:25:08 +00:00
var topicSuccessCallback = function(topicModel, response) {
2016-09-22 09:36:47 +00:00
if (Active.Map) {
2016-04-14 06:16:16 +00:00
mapping.save({ mappable_id: topicModel.id }, {
2016-11-07 20:25:08 +00:00
success: function(model, response) {
mappingSuccessCallback(model, response, topicModel)
},
2016-11-07 20:25:08 +00:00
error: function(model, response) {
2016-04-14 06:16:16 +00:00
console.log('error saving mapping to database')
}
})
}
2016-09-22 10:31:56 +00:00
if (Create.newTopic.addSynapse) {
Create.newSynapse.topic2id = topicModel.id
2016-04-14 06:16:16 +00:00
}
}
2016-04-14 06:12:12 +00:00
2016-09-22 09:36:47 +00:00
if (!Settings.sandbox && createNewInDB) {
2016-04-14 06:16:16 +00:00
if (topic.isNew()) {
topic.save(null, {
success: topicSuccessCallback,
2016-11-07 20:25:08 +00:00
error: function(model, response) {
2016-04-14 06:16:16 +00:00
console.log('error saving topic to database')
}
})
2016-09-22 09:36:47 +00:00
} else if (!topic.isNew() && Active.Map) {
2016-04-14 06:16:16 +00:00
mapping.save(null, {
success: mappingSuccessCallback
})
}
}
},
2016-11-07 20:25:08 +00:00
createTopicLocally: function() {
2016-09-22 10:31:56 +00:00
var self = Topic
2016-04-14 06:12:12 +00:00
2016-09-22 10:31:56 +00:00
if (Create.newTopic.name === '') {
GlobalUI.notifyUser('Please enter a topic title...')
2016-04-14 06:16:16 +00:00
return
}
2016-04-14 06:12:12 +00:00
2016-04-14 06:16:16 +00:00
// hide the 'double-click to add a topic' message
2016-09-22 10:31:56 +00:00
GlobalUI.hideDiv('#instructions')
2016-04-14 06:12:12 +00:00
2016-09-22 10:31:56 +00:00
$(document).trigger(Map.events.editedByActiveMapper)
2016-04-14 06:12:12 +00:00
var metacode = DataModel.Metacodes.get(Create.newTopic.metacode)
2016-04-14 06:12:12 +00:00
var topic = new DataModel.Topic({
2016-09-22 10:31:56 +00:00
name: Create.newTopic.name,
metacode_id: metacode.id,
2016-09-22 09:36:47 +00:00
defer_to_map_id: Active.Map.id
2016-04-14 06:16:16 +00:00
})
DataModel.Topics.add(topic)
2016-04-14 06:12:12 +00:00
2016-09-22 10:31:56 +00:00
if (Create.newTopic.pinned) {
var nextCoords = AutoLayout.getNextCoord({ mappings: DataModel.Mappings })
}
var mapping = new DataModel.Mapping({
2016-09-22 10:31:56 +00:00
xloc: nextCoords ? nextCoords.x : Create.newTopic.x,
yloc: nextCoords ? nextCoords.y : Create.newTopic.y,
2016-04-14 06:16:16 +00:00
mappable_id: topic.cid,
2016-11-07 20:25:08 +00:00
mappable_type: 'Topic'
2016-04-14 06:16:16 +00:00
})
DataModel.Mappings.add(mapping)
2016-04-14 06:12:12 +00:00
2016-04-14 06:16:16 +00:00
// 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()
2016-04-14 06:12:12 +00:00
2016-04-14 06:16:16 +00:00
self.renderTopic(mapping, topic, true, true) // this function also includes the creation of the topic in the database
},
2016-11-07 20:25:08 +00:00
getTopicFromAutocomplete: function(id) {
2016-09-22 10:31:56 +00:00
var self = Topic
2016-04-14 06:12:12 +00:00
// hide the 'double-click to add a topic' message
GlobalUI.hideDiv('#instructions')
2016-09-22 10:31:56 +00:00
$(document).trigger(Map.events.editedByActiveMapper)
2016-04-14 06:12:12 +00:00
if (!Create.newTopic.pinned) Create.newTopic.hide()
Create.newTopic.reset()
2016-04-14 06:12:12 +00:00
self.get(id, (topic) => {
if (Create.newTopic.pinned) {
var nextCoords = AutoLayout.getNextCoord({ mappings: DataModel.Mappings })
}
var mapping = new DataModel.Mapping({
xloc: nextCoords ? nextCoords.x : Create.newTopic.x,
yloc: nextCoords ? nextCoords.y : Create.newTopic.y,
mappable_type: 'Topic',
2016-11-07 20:25:08 +00:00
mappable_id: topic.id
})
DataModel.Mappings.add(mapping)
2016-04-14 06:12:12 +00:00
self.renderTopic(mapping, topic, true, true)
// this blocked the enterKeyHandler from creating a new topic as well
if (Create.newTopic.pinned) Create.newTopic.beingCreated = true
2016-04-14 06:16:16 +00:00
})
},
2016-11-07 20:25:08 +00:00
getMapFromAutocomplete: function(data) {
var self = Topic
$(document).trigger(Map.events.editedByActiveMapper)
var metacode = DataModel.Metacodes.findWhere({ name: 'Metamap' })
2016-10-03 00:32:37 +00:00
var topic = new DataModel.Topic({
name: data.name,
metacode_id: metacode.id,
2016-10-03 00:32:37 +00:00
defer_to_map_id: Active.Map.id,
link: window.location.origin + '/maps/' + data.id
})
2016-10-03 00:32:37 +00:00
DataModel.Topics.add(topic)
var mapping = new DataModel.Mapping({
xloc: Create.newTopic.x,
yloc: Create.newTopic.y,
mappable_id: topic.cid,
2016-11-07 20:25:08 +00:00
mappable_type: 'Topic'
})
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()
self.renderTopic(mapping, topic, true, true) // this function also includes the creation of the topic in the database
// this blocked the enterKeyHandler from creating a new topic as well
if (Create.newTopic.pinned) Create.newTopic.beingCreated = true
},
2016-11-07 20:25:08 +00:00
getTopicFromSearch: function(event, id) {
2016-09-22 10:31:56 +00:00
var self = Topic
2016-04-14 06:12:12 +00:00
2016-09-22 10:31:56 +00:00
$(document).trigger(Map.events.editedByActiveMapper)
2016-04-14 06:12:12 +00:00
self.get(id, (topic) => {
var nextCoords = AutoLayout.getNextCoord({ mappings: DataModel.Mappings })
var mapping = new DataModel.Mapping({
xloc: nextCoords.x,
yloc: nextCoords.y,
mappable_type: 'Topic',
2016-11-07 20:25:08 +00:00
mappable_id: topic.id
})
DataModel.Mappings.add(mapping)
self.renderTopic(mapping, topic, true, true)
GlobalUI.notifyUser('Topic was added to your map!')
2016-04-14 06:16:16 +00:00
})
event.stopPropagation()
event.preventDefault()
return false
}
}
export default Topic