metamaps--metamaps/app/assets/javascripts/src/Metamaps.js.erb
2016-04-15 09:13:01 +08:00

3558 lines
128 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// TODO document this user agent function
var labelType, useGradients, nativeTextSupport, animate;
(function () {
var ua = navigator.userAgent,
iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i),
typeOfCanvas = typeof HTMLCanvasElement,
nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'),
textSupport = nativeCanvasSupport && (typeof document.createElement('canvas').getContext('2d').fillText == 'function');
//I'm setting this based on the fact that ExCanvas provides text support for IE
//and that as of today iPhone/iPad current text support is lame
labelType = (!nativeCanvasSupport || (textSupport && !iStuff)) ? 'Native' : 'HTML';
nativeTextSupport = labelType == 'Native';
useGradients = nativeCanvasSupport;
animate = !(iStuff || !nativeCanvasSupport);
})();
// TODO eliminate these 5 top-level variables
Metamaps.panningInt = null;
Metamaps.tempNode = null;
Metamaps.tempInit = false;
Metamaps.tempNode2 = null;
Metamaps.VERSION = '<%= METAMAPS_VERSION %>'
Metamaps.Settings = {
embed: false, // indicates that the app is on a page that is optimized for embedding in iFrames on other web pages
sandbox: false, // puts the app into a mode (when true) where it only creates data locally, and isn't writing it to the database
colors: {
background: '#344A58',
synapses: {
normal: '#888888',
hover: '#888888',
selected: '#FFFFFF'
},
topics: {
selected: '#FFFFFF'
},
labels: {
background: '#18202E',
text: '#DDD'
}
},
};
Metamaps.Touch = {
touchPos: null, // this stores the x and y values of a current touch event
touchDragNode: null // this stores a reference to a JIT node that is being dragged
};
Metamaps.Mouse = {
didPan: false,
didBoxZoom: false,
changeInX: 0,
changeInY: 0,
edgeHoveringOver: false,
boxStartCoordinates: false,
boxEndCoordinates: false,
synapseStartCoordinates: [],
synapseEndCoordinates: null,
lastNodeClick: 0,
lastCanvasClick: 0,
DOUBLE_CLICK_TOLERANCE: 300
};
Metamaps.Selected = {
reset: function () {
var self = Metamaps.Selected;
self.Nodes = [];
self.Edges = [];
},
Nodes: [],
Edges: []
};
/*
*
* BACKBONE
*
*/
Metamaps.Backbone.init = function () {
var self = Metamaps.Backbone;
self.Metacode = Backbone.Model.extend({
initialize: function () {
var image = new Image();
image.crossOrigin = "Anonymous";
image.src = this.get('icon');
this.set('image',image);
},
prepareLiForFilter: function () {
var li = '';
li += '<li data-id="' + this.id.toString() + '">';
li += '<img src="' + this.get('icon') + '" data-id="' + this.id.toString() + '"';
li += ' alt="' + this.get('name') + '" />';
li += '<p>' + this.get('name').toLowerCase() + '</p></li>';
return li;
}
});
self.MetacodeCollection = Backbone.Collection.extend({
model: this.Metacode,
url: '/metacodes',
comparator: function (a, b) {
a = a.get('name').toLowerCase();
b = b.get('name').toLowerCase();
return a > b ? 1 : a < b ? -1 : 0;
}
});
self.Topic = Backbone.Model.extend({
urlRoot: '/topics',
blacklist: ['node', 'created_at', 'updated_at', 'user_name', 'user_image', 'map_count', 'synapse_count'],
toJSON: function (options) {
return _.omit(this.attributes, this.blacklist);
},
save: function (key, val, options) {
var attrs;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (key == null || typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
var newOptions = options || {};
var s = newOptions.success;
var permBefore = this.get('permission');
newOptions.success = function (model, response, opt) {
if (s) s(model, response, opt);
model.trigger('saved');
if (permBefore === 'private' && model.get('permission') !== 'private') {
model.trigger('noLongerPrivate');
}
else if (permBefore !== 'private' && model.get('permission') === 'private') {
model.trigger('nowPrivate');
}
};
return Backbone.Model.prototype.save.call(this, attrs, newOptions);
},
initialize: function () {
if (this.isNew()) {
this.set({
"user_id": Metamaps.Active.Mapper.id,
"desc": '',
"link": '',
"permission": Metamaps.Active.Map ? Metamaps.Active.Map.get('permission') : 'commons'
});
}
this.on('changeByOther', this.updateCardView);
this.on('change', this.updateNodeView);
this.on('saved', this.savedEvent);
this.on('nowPrivate', function(){
var removeTopicData = {
mappableid: this.id
};
$(document).trigger(Metamaps.JIT.events.removeTopic, [removeTopicData]);
});
this.on('noLongerPrivate', function(){
var newTopicData = {
mappingid: this.getMapping().id,
mappableid: this.id
};
$(document).trigger(Metamaps.JIT.events.newTopic, [newTopicData]);
});
this.on('change:metacode_id', Metamaps.Filter.checkMetacodes, this);
},
authorizeToEdit: function (mapper) {
if (mapper && (this.get('permission') === "commons" || this.get('user_id') === mapper.get('id'))) return true;
else return false;
},
authorizePermissionChange: function (mapper) {
if (mapper && this.get('user_id') === mapper.get('id')) return true;
else return false;
},
getDate: function () {
},
getMetacode: function () {
return Metamaps.Metacodes.get(this.get('metacode_id'));
},
getMapping: function () {
if (!Metamaps.Active.Map) return false;
return Metamaps.Mappings.findWhere({
map_id: Metamaps.Active.Map.id,
mappable_type: "Topic",
mappable_id: this.isNew() ? this.cid : this.id
});
},
createNode: function () {
var mapping;
var node = {
adjacencies: [],
id: this.isNew() ? this.cid : this.id,
name: this.get('name')
};
if (Metamaps.Active.Map) {
mapping = this.getMapping();
node.data = {
$mapping: null,
$mappingID: mapping.id
};
}
return node;
},
updateNode: function () {
var mapping;
var node = this.get('node');
node.setData('topic', this);
if (Metamaps.Active.Map) {
mapping = this.getMapping();
node.setData('mapping', mapping);
}
return node;
},
savedEvent: function() {
Metamaps.Realtime.sendTopicChange(this);
},
updateViews: function() {
var onPageWithTopicCard = Metamaps.Active.Map || Metamaps.Active.Topic;
var node = this.get('node');
// update topic card, if this topic is the one open there
if (onPageWithTopicCard && this == Metamaps.TopicCard.openTopicCard) {
Metamaps.TopicCard.showCard(node);
}
// update the node on the map
if (onPageWithTopicCard && node) {
node.name = this.get('name');
Metamaps.Visualize.mGraph.plot();
}
},
updateCardView: function() {
var onPageWithTopicCard = Metamaps.Active.Map || Metamaps.Active.Topic;
var node = this.get('node');
// update topic card, if this topic is the one open there
if (onPageWithTopicCard && this == Metamaps.TopicCard.openTopicCard) {
Metamaps.TopicCard.showCard(node);
}
},
updateNodeView: function() {
var onPageWithTopicCard = Metamaps.Active.Map || Metamaps.Active.Topic;
var node = this.get('node');
// update the node on the map
if (onPageWithTopicCard && node) {
node.name = this.get('name');
Metamaps.Visualize.mGraph.plot();
}
}
});
self.TopicCollection = Backbone.Collection.extend({
model: self.Topic,
url: '/topics'
});
self.Synapse = Backbone.Model.extend({
urlRoot: '/synapses',
blacklist: ['edge', 'created_at', 'updated_at'],
toJSON: function (options) {
return _.omit(this.attributes, this.blacklist);
},
save: function (key, val, options) {
var attrs;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (key == null || typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
var newOptions = options || {};
var s = newOptions.success;
var permBefore = this.get('permission');
newOptions.success = function (model, response, opt) {
if (s) s(model, response, opt);
model.trigger('saved');
if (permBefore === 'private' && model.get('permission') !== 'private') {
model.trigger('noLongerPrivate');
}
else if (permBefore !== 'private' && model.get('permission') === 'private') {
model.trigger('nowPrivate');
}
};
return Backbone.Model.prototype.save.call(this, attrs, newOptions);
},
initialize: function () {
if (this.isNew()) {
this.set({
"user_id": Metamaps.Active.Mapper.id,
"permission": Metamaps.Active.Map ? Metamaps.Active.Map.get('permission') : 'commons',
"category": "from-to"
});
}
this.on('changeByOther', this.updateCardView);
this.on('change', this.updateEdgeView);
this.on('saved', this.savedEvent);
this.on('noLongerPrivate', function(){
var newSynapseData = {
mappingid: this.getMapping().id,
mappableid: this.id
};
$(document).trigger(Metamaps.JIT.events.newSynapse, [newSynapseData]);
});
this.on('nowPrivate', function(){
$(document).trigger(Metamaps.JIT.events.removeSynapse, [{
mappableid: this.id
}]);
});
this.on('change:desc', Metamaps.Filter.checkSynapses, this);
},
prepareLiForFilter: function () {
var li = '';
li += '<li data-id="' + this.get('desc') + '">';
li += '<img src="<%= asset_path('synapse16.png') %>"';
li += ' alt="synapse icon" />';
li += '<p>' + this.get('desc') + '</p></li>';
return li;
},
authorizeToEdit: function (mapper) {
if (mapper && (this.get('permission') === "commons" || this.get('user_id') === mapper.get('id'))) return true;
else return false;
},
authorizePermissionChange: function (mapper) {
if (mapper && this.get('user_id') === mapper.get('id')) return true;
else return false;
},
getTopic1: function () {
return Metamaps.Topics.get(this.get('node1_id'));
},
getTopic2: function () {
return Metamaps.Topics.get(this.get('node2_id'));
},
getDirection: function () {
var t1 = this.getTopic1(),
t2 = this.getTopic2();
return t1 && t2 ? [
t1.get('node').id,
t2.get('node').id
] : false;
},
getMapping: function () {
if (!Metamaps.Active.Map) return false;
return Metamaps.Mappings.findWhere({
map_id: Metamaps.Active.Map.id,
mappable_type: "Synapse",
mappable_id: this.isNew() ? this.cid : this.id
});
},
createEdge: function (providedMapping) {
var mapping, mappingID;
var synapseID = this.isNew() ? this.cid : this.id;
var edge = {
nodeFrom: this.get('node1_id'),
nodeTo: this.get('node2_id'),
data: {
$synapses: [],
$synapseIDs: [synapseID],
}
};
if (Metamaps.Active.Map) {
mapping = providedMapping || this.getMapping();
mappingID = mapping.isNew() ? mapping.cid : mapping.id;
edge.data.$mappings = [];
edge.data.$mappingIDs = [mappingID];
}
return edge;
},
updateEdge: function () {
var mapping;
var edge = this.get('edge');
edge.getData('synapses').push(this);
if (Metamaps.Active.Map) {
mapping = this.getMapping();
edge.getData('mappings').push(mapping);
}
return edge;
},
savedEvent: function() {
Metamaps.Realtime.sendSynapseChange(this);
},
updateViews: function() {
this.updateCardView();
this.updateEdgeView();
},
updateCardView: function() {
var onPageWithSynapseCard = Metamaps.Active.Map || Metamaps.Active.Topic;
var edge = this.get('edge');
// update synapse card, if this synapse is the one open there
if (onPageWithSynapseCard && edge == Metamaps.SynapseCard.openSynapseCard) {
Metamaps.SynapseCard.showCard(edge);
}
},
updateEdgeView: function() {
var onPageWithSynapseCard = Metamaps.Active.Map || Metamaps.Active.Topic;
var edge = this.get('edge');
// update the edge on the map
if (onPageWithSynapseCard && edge) {
Metamaps.Visualize.mGraph.plot();
}
}
});
self.SynapseCollection = Backbone.Collection.extend({
model: self.Synapse,
url: '/synapses'
});
self.Mapping = Backbone.Model.extend({
urlRoot: '/mappings',
blacklist: ['created_at', 'updated_at'],
toJSON: function (options) {
return _.omit(this.attributes, this.blacklist);
},
initialize: function () {
if (this.isNew()) {
this.set({
"user_id": Metamaps.Active.Mapper.id,
"map_id": Metamaps.Active.Map ? Metamaps.Active.Map.id : null
});
}
},
getMap: function () {
return Metamaps.Map.get(this.get('map_id'));
},
getTopic: function () {
if (this.get('mappable_type') === 'Topic') return Metamaps.Topic.get(this.get('mappable_id'));
else return false;
},
getSynapse: function () {
if (this.get('mappable_type') === 'Synapse') return Metamaps.Synapse.get(this.get('mappable_id'));
else return false;
}
});
self.MappingCollection = Backbone.Collection.extend({
model: self.Mapping,
url: '/mappings'
});
Metamaps.Metacodes = Metamaps.Metacodes ? new self.MetacodeCollection(Metamaps.Metacodes) : new self.MetacodeCollection();
Metamaps.Topics = Metamaps.Topics ? new self.TopicCollection(Metamaps.Topics) : new self.TopicCollection();
Metamaps.Synapses = Metamaps.Synapses ? new self.SynapseCollection(Metamaps.Synapses) : new self.SynapseCollection();
Metamaps.Mappers = Metamaps.Mappers ? new self.MapperCollection(Metamaps.Mappers) : new self.MapperCollection();
// this is for topic view
Metamaps.Creators = Metamaps.Creators ? new self.MapperCollection(Metamaps.Creators) : new self.MapperCollection();
if (Metamaps.Active.Map) {
Metamaps.Mappings = Metamaps.Mappings ? new self.MappingCollection(Metamaps.Mappings) : new self.MappingCollection();
Metamaps.Active.Map = new self.Map(Metamaps.Active.Map);
}
if (Metamaps.Active.Topic) Metamaps.Active.Topic = new self.Topic(Metamaps.Active.Topic);
//attach collection event listeners
self.attachCollectionEvents = function () {
Metamaps.Topics.on("add remove", function(topic){
Metamaps.Map.InfoBox.updateNumbers();
Metamaps.Filter.checkMetacodes();
Metamaps.Filter.checkMappers();
});
Metamaps.Synapses.on("add remove", function(synapse){
Metamaps.Map.InfoBox.updateNumbers();
Metamaps.Filter.checkSynapses();
Metamaps.Filter.checkMappers();
});
if (Metamaps.Active.Map) {
Metamaps.Mappings.on("add remove", function(mapping){
Metamaps.Map.InfoBox.updateNumbers();
Metamaps.Filter.checkSynapses();
Metamaps.Filter.checkMetacodes();
Metamaps.Filter.checkMappers();
});
}
}
self.attachCollectionEvents();
}; // end Metamaps.Backbone.init
/*
*
* CREATE
*
*/
Metamaps.Create = {
isSwitchingSet: false, // indicates whether the metacode set switch lightbox is open
selectedMetacodeSet: null,
selectedMetacodeSetIndex: null,
selectedMetacodeNames: [],
newSelectedMetacodeNames: [],
selectedMetacodes: [],
newSelectedMetacodes: [],
init: function () {
var self = Metamaps.Create;
self.newTopic.init();
self.newSynapse.init();
//////
//////
//// SWITCHING METACODE SETS
$('#metacodeSwitchTabs').tabs({
selected: self.selectedMetacodeSetIndex
}).addClass("ui-tabs-vertical ui-helper-clearfix");
$("#metacodeSwitchTabs .ui-tabs-nav li").removeClass("ui-corner-top").addClass("ui-corner-left");
$('.customMetacodeList li').click(self.toggleMetacodeSelected); // within the custom metacode set tab
},
toggleMetacodeSelected: function () {
var self = Metamaps.Create;
if ($(this).attr('class') != 'toggledOff') {
$(this).addClass('toggledOff');
var value_to_remove = $(this).attr('id');
var name_to_remove = $(this).attr('data-name');
self.newSelectedMetacodes.splice(self.newSelectedMetacodes.indexOf(value_to_remove), 1);
self.newSelectedMetacodeNames.splice(self.newSelectedMetacodeNames.indexOf(name_to_remove), 1);
} else if ($(this).attr('class') == 'toggledOff') {
$(this).removeClass('toggledOff');
self.newSelectedMetacodes.push($(this).attr('id'));
self.newSelectedMetacodeNames.push($(this).attr('data-name'));
}
},
updateMetacodeSet: function (set, index, custom) {
if (custom && Metamaps.Create.newSelectedMetacodes.length == 0) {
alert('Please select at least one metacode to use!');
return false;
}
var codesToSwitchToIds;
var metacodeModels = new Metamaps.Backbone.MetacodeCollection();
Metamaps.Create.selectedMetacodeSetIndex = index;
Metamaps.Create.selectedMetacodeSet = "metacodeset-" + set;
if (!custom) {
codesToSwitchToIds = $('#metacodeSwitchTabs' + set).attr('data-metacodes').split(',');
$('.customMetacodeList li').addClass('toggledOff');
Metamaps.Create.selectedMetacodes = [];
Metamaps.Create.selectedMetacodeNames = [];
Metamaps.Create.newSelectedMetacodes = [];
Metamaps.Create.newSelectedMetacodeNames = [];
}
else if (custom) {
// uses .slice to avoid setting the two arrays to the same actual array
Metamaps.Create.selectedMetacodes = Metamaps.Create.newSelectedMetacodes.slice(0);
Metamaps.Create.selectedMetacodeNames = Metamaps.Create.newSelectedMetacodeNames.slice(0);
codesToSwitchToIds = Metamaps.Create.selectedMetacodes.slice(0);
}
// sort by name
for (var i = 0; i < codesToSwitchToIds.length; i++) {
metacodeModels.add( Metamaps.Metacodes.get(codesToSwitchToIds[i]) );
};
metacodeModels.sort();
$('#metacodeImg, #metacodeImgTitle').empty();
$('#metacodeImg').removeData('cloudcarousel');
var newMetacodes = "";
metacodeModels.each(function(metacode){
newMetacodes += '<img class="cloudcarousel" width="40" height="40" src="' + metacode.get('icon') + '" data-id="' + metacode.id + '" title="' + metacode.get('name') + '" alt="' + metacode.get('name') + '"/>';
});
$('#metacodeImg').empty().append(newMetacodes).CloudCarousel({
titleBox: $('#metacodeImgTitle'),
yRadius: 40,
xRadius: 190,
xPos: 170,
yPos: 40,
speed: 0.3,
mouseWheel: true,
bringToFront: true
});
Metamaps.GlobalUI.closeLightbox();
$('#topic_name').focus();
var mdata = {
"metacodes": {
"value": custom ? Metamaps.Create.selectedMetacodes.toString() : Metamaps.Create.selectedMetacodeSet
}
};
$.ajax({
type: "POST",
dataType: 'json',
url: "/user/updatemetacodes",
data: mdata,
success: function (data) {
console.log('selected metacodes saved');
},
error: function () {
console.log('failed to save selected metacodes');
}
});
},
cancelMetacodeSetSwitch: function () {
var self = Metamaps.Create;
self.isSwitchingSet = false;
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
$('.customMetacodeList li').addClass('toggledOff');
for (var i = 0; i < self.selectedMetacodes.length; i++) {
$('#' + self.selectedMetacodes[i]).removeClass('toggledOff');
};
// uses .slice to avoid setting the two arrays to the same actual array
self.newSelectedMetacodeNames = self.selectedMetacodeNames.slice(0);
self.newSelectedMetacodes = self.selectedMetacodes.slice(0);
}
$('#metacodeSwitchTabs').tabs("option", "active", self.selectedMetacodeSetIndex);
$('#topic_name').focus();
},
newTopic: {
init: function () {
$('#topic_name').keyup(function () {
Metamaps.Create.newTopic.name = $(this).val();
});
var topicBloodhound = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/topics/autocomplete_topic?term=%QUERY',
wildcard: '%QUERY',
},
});
// initialize the autocomplete results for the metacode spinner
$('#topic_name').typeahead(
{
highlight: true,
minLength: 2,
},
[{
name: 'topic_autocomplete',
limit: 8,
display: function (s) { return s.label; },
templates: {
suggestion: function(s) {
return Hogan.compile($('#topicAutocompleteTemplate').html()).render(s);
},
},
source: topicBloodhound,
}]
);
// tell the autocomplete to submit the form with the topic you clicked on if you pick from the autocomplete
$('#topic_name').bind('typeahead:select', function (event, datum, dataset) {
Metamaps.Topic.getTopicFromAutocomplete(datum.id);
});
// initialize metacode spinner and then hide it
$("#metacodeImg").CloudCarousel({
titleBox: $('#metacodeImgTitle'),
yRadius: 40,
xRadius: 190,
xPos: 170,
yPos: 40,
speed: 0.3,
mouseWheel: true,
bringToFront: true
});
$('.new_topic').hide();
},
name: null,
newId: 1,
beingCreated: false,
metacode: null,
x: null,
y: null,
addSynapse: false,
open: function () {
$('#new_topic').fadeIn('fast', function () {
$('#topic_name').focus();
});
Metamaps.Create.newTopic.beingCreated = true;
Metamaps.Create.newTopic.name = "";
},
hide: function () {
$('#new_topic').fadeOut('fast');
$("#topic_name").typeahead('val', '');
Metamaps.Create.newTopic.beingCreated = false;
}
},
newSynapse: {
init: function () {
var self = Metamaps.Create.newSynapse;
var synapseBloodhound = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/synapses?term=%QUERY',
wildcard: '%QUERY',
},
});
var existingSynapseBloodhound = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/synapses?topic1id=%TOPIC1&topic2id=%TOPIC2',
prepare: function(query, settings) {
var self = Metamaps.Create.newSynapse;
if (Metamaps.Selected.Nodes.length < 2) {
settings.url = settings.url.replace("%TOPIC1", self.topic1id).replace("%TOPIC2", self.topic2id);
return settings;
} else {
return null;
}
},
},
});
// initialize the autocomplete results for synapse creation
$('#synapse_desc').typeahead(
{
highlight: true,
minLength: 2,
},
[{
name: 'synapse_autocomplete',
display: function(s) { return s.label; },
templates: {
suggestion: function(s) {
return Hogan.compile("<div class='genericSynapseDesc'>{{label}}</div>").render(s);
},
},
source: synapseBloodhound,
},
{
name: 'existing_synapses',
limit: 50,
display: function(s) { return s.label; },
templates: {
suggestion: function(s) {
return Hogan.compile($('#synapseAutocompleteTemplate').html()).render(s);
},
header: "<h3>Existing synapses</h3>"
},
source: existingSynapseBloodhound,
}]
);
$('#synapse_desc').keyup(function (e) {
var ESC = 27, BACKSPACE = 8, DELETE = 46;
if (e.keyCode === BACKSPACE && $(this).val() === "" ||
e.keyCode === DELETE && $(this).val() === "" ||
e.keyCode === ESC) {
Metamaps.Create.newSynapse.hide();
}//if
Metamaps.Create.newSynapse.description = $(this).val();
});
$('#synapse_desc').focusout(function() {
if (Metamaps.Create.newSynapse.beingCreated) {
Metamaps.Synapse.createSynapseLocally();
}
});
$('#synapse_desc').bind('typeahead:select', function (event, datum, dataset) {
if (datum.id) { // if they clicked on an existing synapse get it
Metamaps.Synapse.getSynapseFromAutocomplete(datum.id);
}
else {
Metamaps.Create.newSynapse.description = datum.value;
Metamaps.Synapse.createSynapseLocally();
}
});
},
beingCreated: false,
description: null,
topic1id: null,
topic2id: null,
newSynapseId: null,
open: function () {
$('#new_synapse').fadeIn(100, function () {
$('#synapse_desc').focus();
});
Metamaps.Create.newSynapse.beingCreated = true;
},
hide: function () {
$('#new_synapse').fadeOut('fast');
$("#synapse_desc").typeahead('val', '');
Metamaps.Create.newSynapse.beingCreated = false;
Metamaps.Create.newTopic.addSynapse = false;
Metamaps.Create.newSynapse.topic1id = 0;
Metamaps.Create.newSynapse.topic2id = 0;
Metamaps.Mouse.synapseStartCoordinates = [];
Metamaps.Visualize.mGraph.plot();
},
}
}; // end Metamaps.Create
////////////////// TOPIC AND SYNAPSE CARDS //////////////////////////
/*
*
* TOPICCARD
*
*/
Metamaps.TopicCard = {
openTopicCard: null, //stores the topic that's currently open
authorizedToEdit: false, // stores boolean for edit permission for open topic card
init: function () {
var self = Metamaps.TopicCard;
// initialize best_in_place editing
$('.authenticated div.permission.canEdit .best_in_place').best_in_place();
Metamaps.TopicCard.generateShowcardHTML = Hogan.compile($('#topicCardTemplate').html());
// initialize topic card draggability and resizability
$('.showcard').draggable({
handle: ".metacodeImage"
});
embedly('on', 'card.rendered', self.embedlyCardRendered);
},
/**
* Will open the Topic Card for the node that it's passed
* @param {$jit.Graph.Node} node
*/
showCard: function (node) {
var self = Metamaps.TopicCard;
var topic = node.getData('topic');
self.openTopicCard = topic;
self.authorizedToEdit = topic.authorizeToEdit(Metamaps.Active.Mapper);
//populate the card that's about to show with the right topics data
self.populateShowCard(topic);
$('.showcard').fadeIn('fast');
},
hideCard: function () {
var self = Metamaps.TopicCard;
$('.showcard').fadeOut('fast');
self.openTopicCard = null;
self.authorizedToEdit = false;
},
embedlyCardRendered: function (iframe) {
var self = Metamaps.TopicCard;
$('#embedlyLinkLoader').hide();
// means that the embedly call returned 404 not found
if ($('#embedlyLink')[0]) {
$('#embedlyLink').css('display', 'block').fadeIn('fast');
$('.embeds').addClass('nonEmbedlyLink');
}
$('.CardOnGraph').addClass('hasAttachment');
if (self.authorizedToEdit) {
$('.embeds').append('<div id="linkremove"></div>');
$('#linkremove').click(self.removeLink);
}
},
removeLink: function () {
var self = Metamaps.TopicCard;
self.openTopicCard.save({
link: null
});
$('.embeds').empty().removeClass('nonEmbedlyLink');
$('#addLinkInput input').val("");
$('.attachments').removeClass('hidden');
$('.CardOnGraph').removeClass('hasAttachment');
},
bindShowCardListeners: function (topic) {
var self = Metamaps.TopicCard;
var showCard = document.getElementById('showcard');
var authorized = self.authorizedToEdit;
// get mapper image
var setMapperImage = function (mapper) {
$('.contributorIcon').attr('src', mapper.get('image'));
};
Metamaps.Mapper.get(topic.get('user_id'), setMapperImage);
// starting embed.ly
var resetFunc = function () {
$('#addLinkInput input').val("");
$('#addLinkInput input').focus();
};
var inputEmbedFunc = function (event) {
var element = this;
setTimeout(function () {
var text = $(element).val();
if (event.type=="paste" || (event.type=="keyup" && event.which==13)){
// TODO evaluate converting this to '//' no matter what (infer protocol)
if (text.slice(0, 7) !== 'http://' &&
text.slice(0, 8) !== 'https://' &&
text.slice(0, 2) !== '//') {
text='//'+text;
}
topic.save({
link: text
});
var embedlyEl = $('<a/>', {
id: 'embedlyLink',
'data-card-description': '0',
href: text
}).html(text);
$('.attachments').addClass('hidden');
$('.embeds').append(embedlyEl);
$('.embeds').append('<div id="embedlyLinkLoader"></div>');
var loader = new CanvasLoader('embedlyLinkLoader');
loader.setColor('#4fb5c0'); // default is '#000000'
loader.setDiameter(28); // default is 40
loader.setDensity(41); // default is 40
loader.setRange(0.9); // default is 1.3
loader.show(); // Hidden by default
var e = embedly('card', document.getElementById('embedlyLink'));
if (!e) {
self.handleInvalidLink();
}
}
}, 100);
};
$('#addLinkReset').click(resetFunc);
$('#addLinkInput input').bind("paste keyup",inputEmbedFunc);
// initialize the link card, if there is a link
if (topic.get('link') && topic.get('link') !== '') {
var loader = new CanvasLoader('embedlyLinkLoader');
loader.setColor('#4fb5c0'); // default is '#000000'
loader.setDiameter(28); // default is 40
loader.setDensity(41); // default is 40
loader.setRange(0.9); // default is 1.3
loader.show(); // Hidden by default
var e = embedly('card', document.getElementById('embedlyLink'));
if (!e) {
self.handleInvalidLink();
}
}
var selectingMetacode = false;
// attach the listener that shows the metacode title when you hover over the image
$('.showcard .metacodeImage').mouseenter(function () {
$('.showcard .icon').css('z-index', '4');
$('.showcard .metacodeTitle').show();
});
$('.showcard .linkItem.icon').mouseleave(function () {
if (!selectingMetacode) {
$('.showcard .metacodeTitle').hide();
$('.showcard .icon').css('z-index', '1');
}
});
var metacodeLiClick = function () {
selectingMetacode = false;
var metacodeId = parseInt($(this).attr('data-id'));
var metacode = Metamaps.Metacodes.get(metacodeId);
$('.CardOnGraph').find('.metacodeTitle').html(metacode.get('name'))
.append('<div class="expandMetacodeSelect"></div>')
.attr('class', 'metacodeTitle mbg' + metacode.id);
$('.CardOnGraph').find('.metacodeImage').css('background-image', 'url(' + metacode.get('icon') + ')');
topic.save({
metacode_id: metacode.id
});
Metamaps.Visualize.mGraph.plot();
$('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge');
$('.metacodeTitle').hide();
$('.showcard .icon').css('z-index', '1');
};
var openMetacodeSelect = function (event) {
var windowWidth;
var showcardLeft;
var TOPICCARD_WIDTH = 300;
var METACODESELECT_WIDTH = 404;
var distanceFromEdge;
var MAX_METACODELIST_HEIGHT = 270;
var windowHeight;
var showcardTop;
var topicTitleHeight;
var distanceFromBottom;
if (!selectingMetacode) {
selectingMetacode = true;
// this is to make sure the metacode
// select is accessible onscreen, when opened
// while topic card is close to the right
// edge of the screen
windowWidth = $(window).width();
showcardLeft = parseInt($('.showcard').css('left'));
distanceFromEdge = windowWidth - (showcardLeft + TOPICCARD_WIDTH);
if (distanceFromEdge < METACODESELECT_WIDTH) {
$('.metacodeSelect').addClass('onRightEdge');
}
// this is to make sure the metacode
// select is accessible onscreen, when opened
// while topic card is close to the bottom
// edge of the screen
windowHeight = $(window).height();
showcardTop = parseInt($('.showcard').css('top'));
topicTitleHeight = $('.showcard .title').height() + parseInt($('.showcard .title').css('padding-top')) + parseInt($('.showcard .title').css('padding-bottom'));
heightOfSetList = $('.showcard .metacodeSelect').height();
distanceFromBottom = windowHeight - (showcardTop + topicTitleHeight);
if (distanceFromBottom < MAX_METACODELIST_HEIGHT) {
$('.metacodeSelect').addClass('onBottomEdge');
}
$('.metacodeSelect').show();
event.stopPropagation();
}
};
var hideMetacodeSelect = function () {
selectingMetacode = false;
$('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge');
$('.metacodeTitle').hide();
$('.showcard .icon').css('z-index', '1');
};
if (authorized) {
$('.showcard .metacodeTitle').click(openMetacodeSelect);
$('.showcard').click(hideMetacodeSelect);
$('.metacodeSelect > ul > li').click(function (event){
event.stopPropagation();
});
$('.metacodeSelect li li').click(metacodeLiClick);
var bipName = $(showCard).find('.best_in_place_name');
bipName.bind("best_in_place:activate", function () {
var $el = bipName.find('textarea');
var el = $el[0];
$el.attr('maxlength', '140');
$('.showcard .title').append('<div class="nameCounter forTopic"></div>');
var callback = function (data) {
$('.nameCounter.forTopic').html(data.all + '/140');
};
Countable.live(el, callback);
});
bipName.bind("best_in_place:deactivate", function () {
$('.nameCounter.forTopic').remove();
});
//bind best_in_place ajax callbacks
bipName.bind("ajax:success", function () {
var name = Metamaps.Util.decodeEntities($(this).html());
topic.set("name", name);
topic.trigger('saved');
});
$(showCard).find('.best_in_place_desc').bind("ajax:success", function () {
this.innerHTML = this.innerHTML.replace(/\r/g, '');
var desc = $(this).html() === $(this).data('nil') ? "" : $(this).html();
topic.set("desc", desc);
topic.trigger('saved');
});
}
var permissionLiClick = function (event) {
selectingPermission = false;
var permission = $(this).attr('class');
topic.save({
permission: permission
});
$('.showcard .mapPerm').removeClass('co pu pr minimize').addClass(permission.substring(0, 2));
$('.showcard .permissionSelect').remove();
event.stopPropagation();
};
var openPermissionSelect = function (event) {
if (!selectingPermission) {
selectingPermission = true;
$(this).addClass('minimize'); // this line flips the drop down arrow to a pull up arrow
if ($(this).hasClass('co')) {
$(this).append('<ul class="permissionSelect"><li class="public"></li><li class="private"></li></ul>');
} else if ($(this).hasClass('pu')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="private"></li></ul>');
} else if ($(this).hasClass('pr')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="public"></li></ul>');
}
$('.showcard .permissionSelect li').click(permissionLiClick);
event.stopPropagation();
}
};
var hidePermissionSelect = function () {
selectingPermission = false;
$('.showcard .yourTopic .mapPerm').removeClass('minimize'); // this line flips the pull up arrow to a drop down arrow
$('.showcard .permissionSelect').remove();
};
// ability to change permission
var selectingPermission = false;
if (topic.authorizePermissionChange(Metamaps.Active.Mapper)) {
$('.showcard .yourTopic .mapPerm').click(openPermissionSelect);
$('.showcard').click(hidePermissionSelect);
}
$('.links .mapCount').unbind().click(function(event){
$('.mapCount .tip').toggle();
$('.showcard .hoverTip').toggleClass('hide');
event.stopPropagation();
});
$('.mapCount .tip').unbind().click(function(event){
event.stopPropagation();
});
$('.showcard').unbind('.hideTip').bind('click.hideTip', function(){
$('.mapCount .tip').hide();
$('.showcard .hoverTip').removeClass('hide');
});
$('.mapCount .tip li a').click(Metamaps.Router.intercept);
var originalText = $('.showMore').html();
$('.mapCount .tip .showMore').unbind().toggle(
function(event){
$('.extraText').toggleClass("hideExtra");
$('.showMore').html('Show less...');
},
function(event){
$('.extraText').toggleClass("hideExtra");
$('.showMore').html(originalText);
});
$('.mapCount .tip showMore').unbind().click(function(event){
event.stopPropagation();
});
},
handleInvalidLink: function() {
var self = Metamaps.TopicCard;
self.removeLink();
Metamaps.GlobalUI.notifyUser("Invalid link");
},
populateShowCard: function (topic) {
var self = Metamaps.TopicCard;
var showCard = document.getElementById('showcard');
$(showCard).find('.permission').remove();
var topicForTemplate = self.buildObject(topic);
var html = self.generateShowcardHTML.render(topicForTemplate);
if (topic.authorizeToEdit(Metamaps.Active.Mapper)) {
var perm = document.createElement('div');
var string = 'permission canEdit';
if (topic.authorizePermissionChange(Metamaps.Active.Mapper)) string += ' yourTopic';
perm.className = string;
perm.innerHTML = html;
showCard.appendChild(perm);
} else {
var perm = document.createElement('div');
perm.className = 'permission cannotEdit';
perm.innerHTML = html;
showCard.appendChild(perm);
}
Metamaps.TopicCard.bindShowCardListeners(topic);
},
generateShowcardHTML: null, // will be initialized into a Hogan template within init function
//generateShowcardHTML
buildObject: function (topic) {
var self=Metamaps.TopicCard;
var nodeValues = {};
var authorized = topic.authorizeToEdit(Metamaps.Active.Mapper);
if (!authorized) {
} else {
}
var desc_nil = "Click to add description...";
nodeValues.attachmentsHidden = '';
if (topic.get('link') && topic.get('link')!== '') {
nodeValues.embeds = '<a href="' + topic.get('link') + '" id="embedlyLink" target="_blank" data-card-description="0">';
nodeValues.embeds += topic.get('link');
nodeValues.embeds += '</a><div id="embedlyLinkLoader"></div>';
nodeValues.attachmentsHidden = 'hidden';
nodeValues.hasAttachment = "hasAttachment";
}
else {
nodeValues.embeds = '';
nodeValues.hasAttachment = '';
}
if (authorized) {
nodeValues.attachments = '<div class="addLink"><div id="addLinkIcon"></div>';
nodeValues.attachments += '<div id="addLinkInput"><input placeholder="Enter or paste a link"></input>';
nodeValues.attachments += '<div id="addLinkReset"></div></div></div>';
} else {
nodeValues.attachmentsHidden = 'hidden';
nodeValues.attachments = '';
}
var inmapsAr = topic.get("inmaps");
var inmapsLinks = topic.get("inmapsLinks");
nodeValues.inmaps ='';
if (inmapsAr.length < 6) {
for (i = 0; i < inmapsAr.length; i++) {
var url = "/maps/" + inmapsLinks[i];
nodeValues.inmaps += '<li><a href="' + url + '">' + inmapsAr[i]+ '</a></li>';
}
}
else {
for (i = 0; i < 5; i++){
var url = "/maps/" + inmapsLinks[i];
nodeValues.inmaps += '<li><a href="' + url + '">' + inmapsAr[i] + '</a></li>';
}
extra = inmapsAr.length - 5;
nodeValues.inmaps += '<li><span class="showMore">See ' + extra + ' more...</span></li>'
for (i = 5; i < inmapsAr.length; i++){
var url = "/maps/" + inmapsLinks[i];
nodeValues.inmaps += '<li class="hideExtra extraText"><a href="' + url + '">' + inmapsAr[i]+ '</a></li>';
}
}
nodeValues.permission = topic.get("permission");
nodeValues.mk_permission = topic.get("permission").substring(0, 2);
nodeValues.map_count = topic.get("map_count").toString();
nodeValues.synapse_count = topic.get("synapse_count").toString();
nodeValues.id = topic.isNew() ? topic.cid : topic.id;
nodeValues.metacode = topic.getMetacode().get("name");
nodeValues.metacode_class = 'mbg' + topic.get('metacode_id');
nodeValues.imgsrc = topic.getMetacode().get("icon");
nodeValues.name = topic.get("name");
nodeValues.userid = topic.get("user_id");
nodeValues.username = topic.get("user_name");
nodeValues.date = topic.getDate();
// the code for this is stored in /views/main/_metacodeOptions.html.erb
nodeValues.metacode_select = $('#metacodeOptions').html();
nodeValues.desc_nil = desc_nil;
nodeValues.desc = (topic.get("desc") == "" && authorized) ? desc_nil : topic.get("desc");
return nodeValues;
}
}; // end Metamaps.TopicCard
/*
*
* SYNAPSECARD
*
*/
Metamaps.SynapseCard = {
openSynapseCard: null,
showCard: function (edge, e) {
var self = Metamaps.SynapseCard;
//reset so we don't interfere with other edges, but first, save its x and y
var myX = $('#edit_synapse').css('left');
var myY = $('#edit_synapse').css('top');
$('#edit_synapse').remove();
//so label is missing while editing
Metamaps.Control.deselectEdge(edge);
var index = edge.getData("displayIndex") ? edge.getData("displayIndex") : 0;
var synapse = edge.getData('synapses')[index]; // for now, just get the first synapse
//create the wrapper around the form elements, including permissions
//classes to make best_in_place happy
var edit_div = document.createElement('div');
edit_div.innerHTML = '<div id="editSynUpperBar"></div><div id="editSynLowerBar"></div>';
edit_div.setAttribute('id', 'edit_synapse');
if (synapse.authorizeToEdit(Metamaps.Active.Mapper)) {
edit_div.className = 'permission canEdit';
edit_div.className += synapse.authorizePermissionChange(Metamaps.Active.Mapper) ? ' yourEdge' : '';
} else {
edit_div.className = 'permission cannotEdit';
}
$('#wrapper').append(edit_div);
self.populateShowCard(edge, synapse);
//drop it in the right spot, activate it
$('#edit_synapse').css('position', 'absolute');
if (e) {
$('#edit_synapse').css('left', e.clientX);
$('#edit_synapse').css('top', e.clientY);
} else {
$('#edit_synapse').css('left', myX);
$('#edit_synapse').css('top', myY);
}
//$('#edit_synapse_name').click(); //required in case name is empty
//$('#edit_synapse_name input').focus();
$('#edit_synapse').show();
self.openSynapseCard = edge;
},
hideCard: function () {
$('#edit_synapse').remove();
Metamaps.SynapseCard.openSynapseCard = null;
},
populateShowCard: function (edge, synapse) {
var self = Metamaps.SynapseCard;
self.add_synapse_count(edge);
self.add_desc_form(synapse);
self.add_drop_down(edge, synapse);
self.add_user_info(synapse);
self.add_perms_form(synapse);
self.add_direction_form(synapse);
},
add_synapse_count: function (edge) {
var count = edge.getData("synapses").length;
$('#editSynUpperBar').append('<div id="synapseCardCount">' + count + '</div>')
},
add_desc_form: function (synapse) {
var data_nil = 'Click to add description.';
// TODO make it so that this would work even in sandbox mode,
// currently with Best_in_place it won't
//desc editing form
$('#editSynUpperBar').append('<div id="edit_synapse_desc"></div>');
$('#edit_synapse_desc').attr('class', 'best_in_place best_in_place_desc');
$('#edit_synapse_desc').attr('data-object', 'synapse');
$('#edit_synapse_desc').attr('data-attribute', 'desc');
$('#edit_synapse_desc').attr('data-type', 'textarea');
$('#edit_synapse_desc').attr('data-nil', data_nil);
$('#edit_synapse_desc').attr('data-url', '/synapses/' + synapse.id);
$('#edit_synapse_desc').html(synapse.get("desc"));
//if edge data is blank or just whitespace, populate it with data_nil
if ($('#edit_synapse_desc').html().trim() == '') {
if (synapse.authorizeToEdit(Metamaps.Active.Mapper)) {
$('#edit_synapse_desc').html(data_nil);
}
else {
$('#edit_synapse_desc').html("(no description)");
}
}
$('#edit_synapse_desc').bind("ajax:success", function () {
var desc = $(this).html();
if (desc == data_nil) {
synapse.set("desc", '');
} else {
synapse.set("desc", desc);
}
synapse.trigger('saved');
Metamaps.Control.selectEdge(synapse.get('edge'));
Metamaps.Visualize.mGraph.plot();
});
},
add_drop_down: function (edge, synapse) {
var list, i, synapses, l, desc;
synapses = edge.getData("synapses");
l = synapses.length;
if (l > 1) {
// append the element that you click to show dropdown select
$('#editSynUpperBar').append('<div id="dropdownSynapses"></div>');
$('#dropdownSynapses').click(function(e){
e.preventDefault();
e.stopPropagation(); // stop it from immediately closing it again
$('#switchSynapseList').toggle();
});
// hide the dropdown again if you click anywhere else on the synapse card
$('#edit_synapse').click(function(){
$('#switchSynapseList').hide();
});
// generate the list of other synapses
list = '<ul id="switchSynapseList">';
for (i = 0; i < l; i++) {
if (synapses[i] !== synapse) { // don't add the current one to the list
desc = synapses[i].get('desc');
desc = desc === "" || desc === null ? "(no description)" : desc;
list += '<li data-synapse-index="' + i + '">' + desc + '</li>';
}
}
list += '</ul>'
// add the list of the other synapses
$('#editSynLowerBar').append(list);
// attach click listeners to list items that
// will cause it to switch the displayed synapse
// when you click it
$('#switchSynapseList li').click(function(e){
e.stopPropagation();
var index = parseInt($(this).attr('data-synapse-index'));
edge.setData('displayIndex', index);
Metamaps.Visualize.mGraph.plot();
Metamaps.SynapseCard.showCard(edge, false);
});
}
},
add_user_info: function (synapse) {
var u = '<div id="edgeUser" class="hoverForTip">';
u += '<a href="/explore/mapper/' + synapse.get("user_id") + '"> <img src="" width="24" height="24" /></a>'
u += '<div class="tip">' + synapse.get("user_name") + '</div></div>';
$('#editSynLowerBar').append(u);
// get mapper image
var setMapperImage = function (mapper) {
$('#edgeUser img').attr('src', mapper.get('image'));
};
Metamaps.Mapper.get(synapse.get('user_id'), setMapperImage);
},
add_perms_form: function (synapse) {
//permissions - if owner, also allow permission editing
$('#editSynLowerBar').append('<div class="mapPerm ' + synapse.get("permission").substring(0, 2) + '"></div>');
// ability to change permission
var selectingPermission = false;
var permissionLiClick = function (event) {
selectingPermission = false;
var permission = $(this).attr('class');
synapse.save({
permission: permission
});
$('#edit_synapse .mapPerm').removeClass('co pu pr minimize').addClass(permission.substring(0, 2));
$('#edit_synapse .permissionSelect').remove();
event.stopPropagation();
};
var openPermissionSelect = function (event) {
if (!selectingPermission) {
selectingPermission = true;
$(this).addClass('minimize'); // this line flips the drop down arrow to a pull up arrow
if ($(this).hasClass('co')) {
$(this).append('<ul class="permissionSelect"><li class="public"></li><li class="private"></li></ul>');
} else if ($(this).hasClass('pu')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="private"></li></ul>');
} else if ($(this).hasClass('pr')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="public"></li></ul>');
}
$('#edit_synapse .permissionSelect li').click(permissionLiClick);
event.stopPropagation();
}
};
var hidePermissionSelect = function () {
selectingPermission = false;
$('#edit_synapse.yourEdge .mapPerm').removeClass('minimize'); // this line flips the pull up arrow to a drop down arrow
$('#edit_synapse .permissionSelect').remove();
};
if (synapse.authorizePermissionChange(Metamaps.Active.Mapper)) {
$('#edit_synapse.yourEdge .mapPerm').click(openPermissionSelect);
$('#edit_synapse').click(hidePermissionSelect);
}
}, //add_perms_form
add_direction_form: function (synapse) {
//directionality checkboxes
$('#editSynLowerBar').append('<div id="edit_synapse_left"></div>');
$('#editSynLowerBar').append('<div id="edit_synapse_right"></div>');
var edge = synapse.get('edge');
//determine which node is to the left and the right
//if directly in a line, top is left
if (edge.nodeFrom.pos.x < edge.nodeTo.pos.x ||
edge.nodeFrom.pos.x == edge.nodeTo.pos.x &&
edge.nodeFrom.pos.y < edge.nodeTo.pos.y) {
var left = edge.nodeTo.getData("topic");
var right = edge.nodeFrom.getData("topic");
} else {
var left = edge.nodeFrom.getData("topic");
var right = edge.nodeTo.getData("topic");
}
/*
* One node is actually on the left onscreen. Call it left, & the other right.
* If category is from-to, and that node is first, check the 'right' checkbox.
* Else check the 'left' checkbox since the arrow is incoming.
*/
var directionCat = synapse.get('category'); //both, none, from-to
if (directionCat == 'from-to') {
var from_to = [synapse.get("node1_id"), synapse.get("node2_id")];
if (from_to[0] == left.id) {
//check left checkbox
$('#edit_synapse_left').addClass('checked');
} else {
//check right checkbox
$('#edit_synapse_right').addClass('checked');
}
} else if (directionCat == 'both') {
//check both checkboxes
$('#edit_synapse_left').addClass('checked');
$('#edit_synapse_right').addClass('checked');
}
if (synapse.authorizeToEdit(Metamaps.Active.Mapper)) {
$('#edit_synapse_left, #edit_synapse_right').click(function () {
$(this).toggleClass('checked');
var leftChecked = $('#edit_synapse_left').is('.checked');
var rightChecked = $('#edit_synapse_right').is('.checked');
var dir = synapse.getDirection();
var dirCat = 'none';
if (leftChecked && rightChecked) {
dirCat = 'both';
} else if (!leftChecked && rightChecked) {
dirCat = 'from-to';
dir = [right.id, left.id];
} else if (leftChecked && !rightChecked) {
dirCat = 'from-to';
dir = [left.id, right.id];
}
synapse.save({
category: dirCat,
node1_id: dir[0],
node2_id: dir[1]
});
Metamaps.Visualize.mGraph.plot();
});
} // if
} //add_direction_form
}; // end Metamaps.SynapseCard
////////////////////// END TOPIC AND SYNAPSE CARDS //////////////////////////////////
/*
*
* VISUALIZE
*
*/
Metamaps.Visualize = {
mGraph: null, // a reference to the graph object.
cameraPosition: null, // stores the camera position when using a 3D visualization
type: "ForceDirected", // the type of graph we're building, could be "RGraph", "ForceDirected", or "ForceDirected3D"
loadLater: false, // indicates whether there is JSON that should be loaded right in the offset, or whether to wait till the first topic is created
init: function () {
var self = Metamaps.Visualize;
// disable awkward dragging of the canvas element that would sometimes happen
$('#infovis-canvas').on('dragstart', function (event) {
event.preventDefault();
});
// prevent touch events on the canvas from default behaviour
$("#infovis-canvas").bind('touchstart', function (event) {
event.preventDefault();
self.mGraph.events.touched = true;
});
// prevent touch events on the canvas from default behaviour
$("#infovis-canvas").bind('touchmove', function (event) {
//Metamaps.JIT.touchPanZoomHandler(event);
});
// prevent touch events on the canvas from default behaviour
$("#infovis-canvas").bind('touchend touchcancel', function (event) {
lastDist = 0;
if (!self.mGraph.events.touchMoved && !Metamaps.Touch.touchDragNode) Metamaps.TopicCard.hideCurrentCard();
self.mGraph.events.touched = self.mGraph.events.touchMoved = false;
Metamaps.Touch.touchDragNode = false;
});
},
computePositions: function () {
var self = Metamaps.Visualize,
mapping;
if (self.type == "RGraph") {
var i, l, startPos, endPos, topic, synapse;
self.mGraph.graph.eachNode(function (n) {
topic = Metamaps.Topics.get(n.id);
topic.set({ node: n }, { silent: true });
topic.updateNode();
n.eachAdjacency(function (edge) {
if(!edge.getData('init')) {
edge.setData('init', true);
l = edge.getData('synapseIDs').length;
for (i = 0; i < l; i++) {
synapse = Metamaps.Synapses.get(edge.getData('synapseIDs')[i]);
synapse.set({ edge: edge }, { silent: true });
synapse.updateEdge();
}
}
});
var pos = n.getPos();
pos.setc(-200, -200);
});
self.mGraph.compute('end');
} else if (self.type == "ForceDirected") {
var i, l, startPos, endPos, topic, synapse;
self.mGraph.graph.eachNode(function (n) {
topic = Metamaps.Topics.get(n.id);
topic.set({ node: n }, { silent: true });
topic.updateNode();
mapping = topic.getMapping();
n.eachAdjacency(function (edge) {
if(!edge.getData('init')) {
edge.setData('init', true);
l = edge.getData('synapseIDs').length;
for (i = 0; i < l; i++) {
synapse = Metamaps.Synapses.get(edge.getData('synapseIDs')[i]);
synapse.set({ edge: edge }, { silent: true });
synapse.updateEdge();
}
}
});
startPos = new $jit.Complex(0, 0);
endPos = new $jit.Complex(mapping.get('xloc'), mapping.get('yloc'));
n.setPos(startPos, 'start');
n.setPos(endPos, 'end');
});
} else if (self.type == "ForceDirected3D") {
self.mGraph.compute();
}
},
/**
* render does the heavy lifting of creating the engine that renders the graph with the properties we desire
*
*/
render: function () {
var self = Metamaps.Visualize, RGraphSettings, FDSettings;
if (self.type == "RGraph" && (!self.mGraph || self.mGraph instanceof $jit.ForceDirected)) {
RGraphSettings = $.extend(true, {}, Metamaps.JIT.ForceDirected.graphSettings);
$jit.RGraph.Plot.NodeTypes.implement(Metamaps.JIT.ForceDirected.nodeSettings);
$jit.RGraph.Plot.EdgeTypes.implement(Metamaps.JIT.ForceDirected.edgeSettings);
RGraphSettings.width = $(document).width();
RGraphSettings.height = $(document).height();
RGraphSettings.background = Metamaps.JIT.RGraph.background;
RGraphSettings.levelDistance = Metamaps.JIT.RGraph.levelDistance;
self.mGraph = new $jit.RGraph(RGraphSettings);
} else if (self.type == "ForceDirected" && (!self.mGraph || self.mGraph instanceof $jit.RGraph)) {
FDSettings = $.extend(true, {}, Metamaps.JIT.ForceDirected.graphSettings);
$jit.ForceDirected.Plot.NodeTypes.implement(Metamaps.JIT.ForceDirected.nodeSettings);
$jit.ForceDirected.Plot.EdgeTypes.implement(Metamaps.JIT.ForceDirected.edgeSettings);
FDSettings.width = $('body').width();
FDSettings.height = $('body').height();
self.mGraph = new $jit.ForceDirected(FDSettings);
} else if (self.type == "ForceDirected3D" && !self.mGraph) {
// init ForceDirected3D
self.mGraph = new $jit.ForceDirected3D(Metamaps.JIT.ForceDirected3D.graphSettings);
self.cameraPosition = self.mGraph.canvas.canvases[0].camera.position;
}
else {
self.mGraph.graph.empty();
}
function runAnimation() {
Metamaps.Loading.hide();
// load JSON data, if it's not empty
if (!self.loadLater) {
//load JSON data.
var rootIndex = 0;
if (Metamaps.Active.Topic) {
var node = _.find(Metamaps.JIT.vizData, function(node){
return node.id === Metamaps.Active.Topic.id;
});
rootIndex = _.indexOf(Metamaps.JIT.vizData, node);
}
self.mGraph.loadJSON(Metamaps.JIT.vizData, rootIndex);
//compute positions and plot.
self.computePositions();
self.mGraph.busy = true;
if (self.type == "RGraph") {
self.mGraph.fx.animate(Metamaps.JIT.RGraph.animate);
} else if (self.type == "ForceDirected") {
self.mGraph.animate(Metamaps.JIT.ForceDirected.animateSavedLayout);
} else if (self.type == "ForceDirected3D") {
self.mGraph.animate(Metamaps.JIT.ForceDirected.animateFDLayout);
}
}
}
// hold until all the needed metacode images are loaded
// hold for a maximum of 80 passes, or 4 seconds of waiting time
var tries = 0;
function hold() {
var unique = _.uniq(Metamaps.Topics.models, function (metacode) { return metacode.get('metacode_id'); }),
requiredMetacodes = _.map(unique, function (metacode) { return metacode.get('metacode_id'); }),
loadedCount = 0;
_.each(requiredMetacodes, function (metacode_id) {
var metacode = Metamaps.Metacodes.get(metacode_id),
img = metacode ? metacode.get('image') : false;
if (img && (img.complete || (typeof img.naturalWidth !== "undefined" && img.naturalWidth !== 0))) {
loadedCount += 1;
}
});
if (loadedCount === requiredMetacodes.length || tries > 80) runAnimation();
else setTimeout(function(){ tries++; hold() }, 50);
}
hold();
// update the url now that the map is ready
clearTimeout(Metamaps.Router.timeoutId);
Metamaps.Router.timeoutId = setTimeout(function(){
var m = Metamaps.Active.Map;
var t = Metamaps.Active.Topic;
if (m && window.location.pathname !== "/maps/" + m.id) {
Metamaps.Router.navigate("/maps/" + m.id);
}
else if (t && window.location.pathname !== "/topics/" + t.id) {
Metamaps.Router.navigate("/topics/" + t.id);
}
}, 800);
}
}; // end Metamaps.Visualize
/*
*
* UTIL
*
*/
Metamaps.Util = {
// helper function to determine how many lines are needed
// Line Splitter Function
// copyright Stephen Chapman, 19th April 2006
// you may copy this code but please keep the copyright notice as well
splitLine: function (st, n) {
var b = '';
var s = st ? st : '';
while (s.length > n) {
var c = s.substring(0, n);
var d = c.lastIndexOf(' ');
var e = c.lastIndexOf('\n');
if (e != -1) d = e;
if (d == -1) d = n;
b += c.substring(0, d) + '\n';
s = s.substring(d + 1);
}
return b + s;
},
nowDateFormatted: function () {
var date = new Date(Date.now());
var month = (date.getMonth() + 1) < 10 ? '0' + (date.getMonth() + 1) : (date.getMonth() + 1);
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
var year = date.getFullYear();
return month + '/' + day + '/' + year;
},
decodeEntities: function (desc) {
var str, temp = document.createElement('p');
temp.innerHTML = desc; //browser handles the topics
str = temp.textContent || temp.innerText;
temp = null; //delete the element;
return str;
}, //decodeEntities
getDistance: function (p1, p2) {
return Math.sqrt(Math.pow((p2.x - p1.x), 2) + Math.pow((p2.y - p1.y), 2));
},
coordsToPixels: function (coords) {
if (Metamaps.Visualize.mGraph) {
var canvas = Metamaps.Visualize.mGraph.canvas,
s = canvas.getSize(),
p = canvas.getPos(),
ox = canvas.translateOffsetX,
oy = canvas.translateOffsetY,
sx = canvas.scaleOffsetX,
sy = canvas.scaleOffsetY;
var pixels = {
x: (coords.x / (1/sx)) + p.x + s.width/2 + ox,
y: (coords.y / (1/sy)) + p.y + s.height/2 + oy
};
return pixels;
}
else {
return {
x: 0,
y: 0
};
}
},
pixelsToCoords: function (pixels) {
var coords;
if (Metamaps.Visualize.mGraph) {
var canvas = Metamaps.Visualize.mGraph.canvas,
s = canvas.getSize(),
p = canvas.getPos(),
ox = canvas.translateOffsetX,
oy = canvas.translateOffsetY,
sx = canvas.scaleOffsetX,
sy = canvas.scaleOffsetY;
coords = {
x: (pixels.x - p.x - s.width/2 - ox) * (1/sx),
y: (pixels.y - p.y - s.height/2 - oy) * (1/sy),
};
}
else {
coords = {
x: 0,
y: 0
};
}
return coords;
},
getPastelColor: function () {
var r = (Math.round(Math.random()* 127) + 127).toString(16);
var g = (Math.round(Math.random()* 127) + 127).toString(16);
var b = (Math.round(Math.random()* 127) + 127).toString(16);
return Metamaps.Util.colorLuminance('#' + r + g + b, -0.4);
},
// darkens a hex value by 'lum' percentage
colorLuminance: function (hex, lum) {
// validate hex string
hex = String(hex).replace(/[^0-9a-f]/gi, '');
if (hex.length < 6) {
hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
}
lum = lum || 0;
// convert to decimal and change luminosity
var rgb = "#", c, i;
for (i = 0; i < 3; i++) {
c = parseInt(hex.substr(i*2,2), 16);
c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
rgb += ("00"+c).substr(c.length);
}
return rgb;
},
generateOptionsList: function (data) {
var newlist = "";
for (var i = 0; i < data.length; i++) {
newlist = newlist + '<option value="' + data[i]['id'] + '">' + data[i]['1'][1] + '</option>';
}
return newlist;
},
checkURLisImage: function (url) {
// when the page reloads the following regular expression will be screwed up
// please replace it with this one before you save: /*backslashhere*.(jpeg|jpg|gif|png)$/
return (url.match(/\.(jpeg|jpg|gif|png)$/) != null);
},
checkURLisYoutubeVideo: function (url) {
return (url.match(/^https?:\/\/(?:www\.)?youtube.com\/watch\?(?=[^?]*v=\w+)(?:[^\s?]+)?$/) != null);
}
}; // end Metamaps.Util
/*
*
* REALTIME
*
*/
Metamaps.Realtime = {
videoId: 'video-wrapper',
socket: null,
webrtc: null,
readyToCall: false,
mappersOnMap: {},
disconnected: false,
chatOpen: false,
status: true, // stores whether realtime is True/On or False/Off,
broadcastingStatus: false,
inConversation: false,
localVideo: null,
init: function () {
var self = Metamaps.Realtime;
self.addJuntoListeners();
self.socket = new SocketIoConnection({ url: '<%= ENV['REALTIME_SERVER'] %>' });
self.socket.on('connect', function () {
console.log('connected');
if (!self.disconnected) {
self.startActiveMap();
} else self.disconnected = false;
});
self.socket.on('disconnect', function () {
self.disconnected = true;
});
if (Metamaps.Active.Mapper) {
self.webrtc = new SimpleWebRTC({
connection: self.socket,
localVideoEl: self.videoId,
remoteVideosEl: '',
detectSpeakingEvents: true,
autoAdjustMic: false, //true,
autoRequestMedia: false,
localVideo: {
autoplay: true,
mirror: true,
muted: true
},
media: {
video: true,
audio: true
},
nick: Metamaps.Active.Mapper.id
});
var
$video = $('<video></video>').attr('id', self.videoId);
self.localVideo = {
$video: $video,
view: new Metamaps.Views.videoView($video[0], $('body'), 'me', true, {
DOUBLE_CLICK_TOLERANCE: 200,
avatar: Metamaps.Active.Mapper ? Metamaps.Active.Mapper.get('image') : ''
})
};
self.room = new Metamaps.Views.room({
webrtc: self.webrtc,
socket: self.socket,
username: Metamaps.Active.Mapper ? Metamaps.Active.Mapper.get('name') : '',
image: Metamaps.Active.Mapper ? Metamaps.Active.Mapper.get('image') : '',
room: 'global',
$video: self.localVideo.$video,
myVideoView: self.localVideo.view,
config: { DOUBLE_CLICK_TOLERANCE: 200 }
});
self.room.videoAdded(self.handleVideoAdded);
self.room.chat.$container.hide();
$('body').prepend(self.room.chat.$container);
} // if Metamaps.Active.Mapper
},
addJuntoListeners: function () {
var self = Metamaps.Realtime;
$(document).on(Metamaps.Views.chatView.events.openTray, function () {
$('.main').addClass('compressed');
self.chatOpen = true;
self.positionPeerIcons();
});
$(document).on(Metamaps.Views.chatView.events.closeTray, function () {
$('.main').removeClass('compressed');
self.chatOpen = false;
self.positionPeerIcons();
});
$(document).on(Metamaps.Views.chatView.events.videosOn, function () {
$('#wrapper').removeClass('hideVideos');
});
$(document).on(Metamaps.Views.chatView.events.videosOff, function () {
$('#wrapper').addClass('hideVideos');
});
$(document).on(Metamaps.Views.chatView.events.cursorsOn, function () {
$('#wrapper').removeClass('hideCursors');
});
$(document).on(Metamaps.Views.chatView.events.cursorsOff, function () {
$('#wrapper').addClass('hideCursors');
});
},
handleVideoAdded: function (v, id) {
var self = Metamaps.Realtime;
self.positionVideos();
v.setParent($('#wrapper'));
v.$container.find('.video-cutoff').css({
border: '4px solid ' + self.mappersOnMap[id].color
});
$('#wrapper').append(v.$container);
},
positionVideos: function () {
var self = Metamaps.Realtime;
var videoIds = Object.keys(self.room.videos);
var numOfVideos = videoIds.length;
var numOfVideosToPosition = _.filter(videoIds, function (id) {
return !self.room.videos[id].manuallyPositioned;
}).length;
var screenHeight = $(document).height();
var screenWidth = $(document).width();
var topExtraPadding = 20;
var topPadding = 30;
var leftPadding = 30;
var videoHeight = 150;
var videoWidth = 180;
var column = 0;
var row = 0;
var yFormula = function () {
var y = topExtraPadding + (topPadding + videoHeight)*row + topPadding;
if (y + videoHeight > screenHeight) {
row = 0;
column += 1;
y = yFormula();
}
row++;
return y;
};
var xFormula = function () {
var x = (leftPadding + videoWidth)*column + leftPadding;
return x;
};
// do self first
var myVideo = Metamaps.Realtime.localVideo.view;
if (!myVideo.manuallyPositioned) {
myVideo.$container.css({
top: yFormula() + 'px',
left: xFormula() + 'px'
});
}
videoIds.forEach(function (id) {
var video = self.room.videos[id];
if (!video.manuallyPositioned) {
video.$container.css({
top: yFormula() + 'px',
left: xFormula() + 'px'
});
}
});
},
startActiveMap: function () {
var self = Metamaps.Realtime;
if (Metamaps.Active.Map && Metamaps.Active.Mapper) {
var commonsMap = Metamaps.Active.Map.get('permission') === 'commons';
var publicMap = Metamaps.Active.Map.get('permission') === 'public';
if (commonsMap) {
self.turnOn();
self.setupSocket();
}
else if (publicMap) {
self.attachMapListener();
}
self.room.addMessages(new Metamaps.Backbone.MessageCollection(Metamaps.Messages), true);
}
},
endActiveMap: function () {
var self = Metamaps.Realtime;
$(document).off('mousemove');
self.socket.removeAllListeners();
if (self.inConversation) self.leaveCall();
self.socket.emit('endMapperNotify');
$(".collabCompass").remove();
self.status = false;
self.room.leave();
self.room.chat.$container.hide();
self.room.chat.close();
},
reenableRealtime: function() {
var confirmString = "The layout of your map has fallen out of sync with the saved copy. ";
confirmString += "To save your changes without overwriting the map, hit 'Cancel' and ";
confirmString += "then use 'Save to new map'. ";
confirmString += "Do you want to discard your changes and enable realtime?";
var c = confirm(confirmString);
if (c) {
Metamaps.Router.maps(Metamaps.Active.Map.id);
}
},
turnOn: function (notify) {
var self = Metamaps.Realtime;
if (notify) self.sendRealtimeOn();
//$(".rtMapperSelf").removeClass('littleRtOff').addClass('littleRtOn');
//$('.rtOn').addClass('active');
//$('.rtOff').removeClass('active');
self.status = true;
//$(".sidebarCollaborateIcon").addClass("blue");
$(".collabCompass").show();
self.room.chat.$container.show();
self.room.room = 'map-' + Metamaps.Active.Map.id;
self.checkForACallToJoin();
self.activeMapper = {
id: Metamaps.Active.Mapper.id,
name: Metamaps.Active.Mapper.get('name'),
username: Metamaps.Active.Mapper.get('name'),
image: Metamaps.Active.Mapper.get('image'),
color: Metamaps.Util.getPastelColor(),
self: true
};
self.localVideo.view.$container.find('.video-cutoff').css({
border: '4px solid ' + self.activeMapper.color
});
self.room.chat.addParticipant(self.activeMapper);
},
checkForACallToJoin: function () {
var self = Metamaps.Realtime;
self.socket.emit('checkForCall', { room: self.room.room, mapid: Metamaps.Active.Map.id });
},
promptToJoin: function () {
var self = Metamaps.Realtime;
var notifyText = 'There\'s a conversation happening, want to join?';
notifyText += ' <button type="button" class="toast-button button" onclick="Metamaps.Realtime.joinCall()">Yes</button>';
notifyText += ' <button type="button" class="toast-button button btn-no" onclick="Metamaps.GlobalUI.clearNotify()">No</button>';
Metamaps.GlobalUI.notifyUser(notifyText, true);
self.room.conversationInProgress();
},
conversationHasBegun: function () {
var self = Metamaps.Realtime;
if (self.inConversation) return;
var notifyText = 'There\'s a conversation starting, want to join?';
notifyText += ' <button type="button" class="toast-button button" onclick="Metamaps.Realtime.joinCall()">Yes</button>';
notifyText += ' <button type="button" class="toast-button button btn-no" onclick="Metamaps.GlobalUI.clearNotify()">No</button>';
Metamaps.GlobalUI.notifyUser(notifyText, true);
self.room.conversationInProgress();
},
countOthersInConversation: function () {
var self = Metamaps.Realtime;
var count = 0;
for (var key in self.mappersOnMap) {
if (self.mappersOnMap[key].inConversation) count++;
}
return count;
},
mapperJoinedCall: function (id) {
var self = Metamaps.Realtime;
var mapper = self.mappersOnMap[id];
if (mapper) {
if (self.inConversation) {
var username = mapper.name;
var notifyText = username + ' joined the call';
Metamaps.GlobalUI.notifyUser(notifyText);
}
mapper.inConversation = true;
self.room.chat.mapperJoinedCall(id);
}
},
mapperLeftCall: function (id) {
var self = Metamaps.Realtime;
var mapper = self.mappersOnMap[id];
if (mapper) {
if (self.inConversation) {
var username = mapper.name;
var notifyText = username + ' left the call';
Metamaps.GlobalUI.notifyUser(notifyText);
}
mapper.inConversation = false;
self.room.chat.mapperLeftCall(id);
if ((self.inConversation && self.countOthersInConversation() === 0) ||
(!self.inConversation && self.countOthersInConversation() === 1)) {
self.callEnded();
}
}
},
callEnded: function () {
var self = Metamaps.Realtime;
self.room.conversationEnding();
self.room.leaveVideoOnly();
self.inConversation = false;
self.localVideo.view.$container.hide().css({
top: '72px',
left: '30px'
});
self.localVideo.view.audioOn();
self.localVideo.view.videoOn();
self.webrtc.webrtc.localStreams.forEach(function (stream) {
stream.getTracks().forEach(function (track) {
track.stop();
});
});
self.webrtc.webrtc.localStreams = [];
},
invitedToCall: function (inviter) {
var self = Metamaps.Realtime;
self.room.chat.sound.stop('sessioninvite');
self.room.chat.sound.play('sessioninvite');
var username = self.mappersOnMap[inviter].name;
var notifyText = "<img src='<%= asset_path('junto_spinner_darkgrey.gif') %>' style='display: inline-block; margin-top: -12px; vertical-align: top;' />";
notifyText += username + ' is inviting you to a conversation. Join live?';
notifyText += ' <button type="button" class="toast-button button" onclick="Metamaps.Realtime.acceptCall(' + inviter + ')">Yes</button>';
notifyText += ' <button type="button" class="toast-button button btn-no" onclick="Metamaps.Realtime.denyCall(' + inviter + ')">No</button>';
Metamaps.GlobalUI.notifyUser(notifyText, true);
},
invitedToJoin: function (inviter) {
var self = Metamaps.Realtime;
self.room.chat.sound.stop('sessioninvite');
self.room.chat.sound.play('sessioninvite');
var username = self.mappersOnMap[inviter].name;
var notifyText = username + ' is inviting you to the conversation. Join?';
notifyText += ' <button type="button" class="toast-button button" onclick="Metamaps.Realtime.joinCall()">Yes</button>';
notifyText += ' <button type="button" class="toast-button button btn-no" onclick="Metamaps.Realtime.denyInvite(' + inviter + ')">No</button>';
Metamaps.GlobalUI.notifyUser(notifyText, true);
},
acceptCall: function (userid) {
var self = Metamaps.Realtime;
self.room.chat.sound.stop('sessioninvite');
self.socket.emit('callAccepted', {
mapid: Metamaps.Active.Map.id,
invited: Metamaps.Active.Mapper.id,
inviter: userid
});
$.post('/maps/' + Metamaps.Active.Map.id + '/events/conversation');
self.joinCall();
Metamaps.GlobalUI.clearNotify();
},
denyCall: function (userid) {
var self = Metamaps.Realtime;
self.room.chat.sound.stop('sessioninvite');
self.socket.emit('callDenied', {
mapid: Metamaps.Active.Map.id,
invited: Metamaps.Active.Mapper.id,
inviter: userid
});
Metamaps.GlobalUI.clearNotify();
},
denyInvite: function (userid) {
var self = Metamaps.Realtime;
self.room.chat.sound.stop('sessioninvite');
self.socket.emit('inviteDenied', {
mapid: Metamaps.Active.Map.id,
invited: Metamaps.Active.Mapper.id,
inviter: userid
});
Metamaps.GlobalUI.clearNotify();
},
inviteACall: function (userid) {
var self = Metamaps.Realtime;
self.socket.emit('inviteACall', {
mapid: Metamaps.Active.Map.id,
inviter: Metamaps.Active.Mapper.id,
invited: userid
});
self.room.chat.invitationPending(userid);
Metamaps.GlobalUI.clearNotify();
},
inviteToJoin: function (userid) {
var self = Metamaps.Realtime;
self.socket.emit('inviteToJoin', {
mapid: Metamaps.Active.Map.id,
inviter: Metamaps.Active.Mapper.id,
invited: userid
});
self.room.chat.invitationPending(userid);
},
callAccepted: function (userid) {
var self = Metamaps.Realtime;
var username = self.mappersOnMap[userid].name;
Metamaps.GlobalUI.notifyUser('Conversation starting...');
self.joinCall();
self.room.chat.invitationAnswered(userid);
},
callDenied: function (userid) {
var self = Metamaps.Realtime;
var username = self.mappersOnMap[userid].name;
Metamaps.GlobalUI.notifyUser(username + ' didn\'t accept your invitation');
self.room.chat.invitationAnswered(userid);
},
inviteDenied: function (userid) {
var self = Metamaps.Realtime;
var username = self.mappersOnMap[userid].name;
Metamaps.GlobalUI.notifyUser(username + ' didn\'t accept your invitation');
self.room.chat.invitationAnswered(userid);
},
joinCall: function () {
var self = Metamaps.Realtime;
self.webrtc.off('readyToCall');
self.webrtc.once('readyToCall', function () {
self.videoInitialized = true;
self.readyToCall = true;
self.localVideo.view.manuallyPositioned = false;
self.positionVideos();
self.localVideo.view.$container.show();
if (self.localVideo && self.status) {
$('#wrapper').append(self.localVideo.view.$container);
}
self.room.join();
});
self.inConversation = true;
self.socket.emit('mapperJoinedCall', {
mapid: Metamaps.Active.Map.id,
id: Metamaps.Active.Mapper.id
});
self.webrtc.startLocalVideo();
Metamaps.GlobalUI.clearNotify();
self.room.chat.mapperJoinedCall(Metamaps.Active.Mapper.id);
},
leaveCall: function () {
var self = Metamaps.Realtime;
self.socket.emit('mapperLeftCall', {
mapid: Metamaps.Active.Map.id,
id: Metamaps.Active.Mapper.id
});
self.room.chat.mapperLeftCall(Metamaps.Active.Mapper.id);
self.room.leaveVideoOnly();
self.inConversation = false;
self.localVideo.view.$container.hide();
// if there's only two people in the room, and we're leaving
// we should shut down the call locally
if (self.countOthersInConversation() === 1) {
self.callEnded();
}
},
turnOff: function (silent) {
var self = Metamaps.Realtime;
if (self.status) {
if (!silent) self.sendRealtimeOff();
//$(".rtMapperSelf").removeClass('littleRtOn').addClass('littleRtOff');
//$('.rtOn').removeClass('active');
//$('.rtOff').addClass('active');
self.status = false;
//$(".sidebarCollaborateIcon").removeClass("blue");
$(".collabCompass").hide();
$('#' + self.videoId).remove();
}
},
setupSocket: function () {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
var myId = Metamaps.Active.Mapper.id;
socket.emit('newMapperNotify', {
userid: myId,
username: Metamaps.Active.Mapper.get("name"),
userimage: Metamaps.Active.Mapper.get("image"),
mapid: Metamaps.Active.Map.id
});
socket.on(myId + '-' + Metamaps.Active.Map.id + '-invitedToCall', self.invitedToCall); // new call
socket.on(myId + '-' + Metamaps.Active.Map.id + '-invitedToJoin', self.invitedToJoin); // call already in progress
socket.on(myId + '-' + Metamaps.Active.Map.id + '-callAccepted', self.callAccepted);
socket.on(myId + '-' + Metamaps.Active.Map.id + '-callDenied', self.callDenied);
socket.on(myId + '-' + Metamaps.Active.Map.id + '-inviteDenied', self.inviteDenied);
// receive word that there's a conversation in progress
socket.on('maps-' + Metamaps.Active.Map.id + '-callInProgress', self.promptToJoin);
socket.on('maps-' + Metamaps.Active.Map.id + '-callStarting', self.conversationHasBegun);
socket.on('maps-' + Metamaps.Active.Map.id + '-mapperJoinedCall', self.mapperJoinedCall);
socket.on('maps-' + Metamaps.Active.Map.id + '-mapperLeftCall', self.mapperLeftCall);
// if you're the 'new guy' update your list with who's already online
socket.on(myId + '-' + Metamaps.Active.Map.id + '-UpdateMapperList', self.updateMapperList);
// receive word that there's a new mapper on the map
socket.on('maps-' + Metamaps.Active.Map.id + '-newmapper', self.newPeerOnMap);
// receive word that a mapper left the map
socket.on('maps-' + Metamaps.Active.Map.id + '-lostmapper', self.lostPeerOnMap);
// receive word that there's a mapper turned on realtime
socket.on('maps-' + Metamaps.Active.Map.id + '-newrealtime', self.newCollaborator);
// receive word that there's a mapper turned on realtime
socket.on('maps-' + Metamaps.Active.Map.id + '-lostrealtime', self.lostCollaborator);
//
socket.on('maps-' + Metamaps.Active.Map.id + '-topicDrag', self.topicDrag);
//
socket.on('maps-' + Metamaps.Active.Map.id + '-newTopic', self.newTopic);
//
socket.on('maps-' + Metamaps.Active.Map.id + '-newMessage', self.newMessage);
//
socket.on('maps-' + Metamaps.Active.Map.id + '-removeTopic', self.removeTopic);
//
socket.on('maps-' + Metamaps.Active.Map.id + '-newSynapse', self.newSynapse);
//
socket.on('maps-' + Metamaps.Active.Map.id + '-removeSynapse', self.removeSynapse);
// update mapper compass position
socket.on('maps-' + Metamaps.Active.Map.id + '-updatePeerCoords', self.updatePeerCoords);
// deletions
socket.on('deleteTopicFromServer', self.removeTopic);
socket.on('deleteSynapseFromServer', self.removeSynapse);
socket.on('topicChangeFromServer', self.topicChange);
socket.on('synapseChangeFromServer', self.synapseChange);
self.attachMapListener();
// local event listeners that trigger events
var sendCoords = function (event) {
var pixels = {
x: event.pageX,
y: event.pageY
};
var coords = Metamaps.Util.pixelsToCoords(pixels);
self.sendCoords(coords);
};
$(document).mousemove(sendCoords);
var zoom = function (event, e) {
if (e) {
var pixels = {
x: e.pageX,
y: e.pageY
};
var coords = Metamaps.Util.pixelsToCoords(pixels);
self.sendCoords(coords);
}
self.positionPeerIcons();
};
$(document).on(Metamaps.JIT.events.zoom, zoom);
$(document).on(Metamaps.JIT.events.pan, self.positionPeerIcons);
var sendTopicDrag = function (event, positions) {
self.sendTopicDrag(positions);
};
$(document).on(Metamaps.JIT.events.topicDrag, sendTopicDrag);
var sendNewTopic = function (event, data) {
self.sendNewTopic(data);
};
$(document).on(Metamaps.JIT.events.newTopic, sendNewTopic);
var sendDeleteTopic = function (event, data) {
self.sendDeleteTopic(data);
};
$(document).on(Metamaps.JIT.events.deleteTopic, sendDeleteTopic);
var sendRemoveTopic = function (event, data) {
self.sendRemoveTopic(data);
};
$(document).on(Metamaps.JIT.events.removeTopic, sendRemoveTopic);
var sendNewSynapse = function (event, data) {
self.sendNewSynapse(data);
};
$(document).on(Metamaps.JIT.events.newSynapse, sendNewSynapse);
var sendDeleteSynapse = function (event, data) {
self.sendDeleteSynapse(data);
};
$(document).on(Metamaps.JIT.events.deleteSynapse, sendDeleteSynapse);
var sendRemoveSynapse = function (event, data) {
self.sendRemoveSynapse(data);
};
$(document).on(Metamaps.JIT.events.removeSynapse, sendRemoveSynapse);
var sendNewMessage = function (event, data) {
self.sendNewMessage(data);
};
$(document).on(Metamaps.Views.room.events.newMessage, sendNewMessage);
},
attachMapListener: function(){
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
socket.on('mapChangeFromServer', self.mapChange);
},
sendRealtimeOn: function () {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
// send this new mapper back your details, and the awareness that you're online
var update = {
username: Metamaps.Active.Mapper.get("name"),
userid: Metamaps.Active.Mapper.id,
mapid: Metamaps.Active.Map.id
};
socket.emit('notifyStartRealtime', update);
},
sendRealtimeOff: function () {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
// send this new mapper back your details, and the awareness that you're online
var update = {
username: Metamaps.Active.Mapper.get("name"),
userid: Metamaps.Active.Mapper.id,
mapid: Metamaps.Active.Map.id
};
socket.emit('notifyStopRealtime', update);
},
updateMapperList: function (data) {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
// data.userid
// data.username
// data.userimage
// data.userrealtime
self.mappersOnMap[data.userid] = {
id: data.userid,
name: data.username,
username: data.username,
image: data.userimage,
color: Metamaps.Util.getPastelColor(),
realtime: data.userrealtime,
inConversation: data.userinconversation,
coords: {
x: 0,
y: 0
}
};
if (data.userid !== Metamaps.Active.Mapper.id) {
self.room.chat.addParticipant(self.mappersOnMap[data.userid]);
if (data.userinconversation) self.room.chat.mapperJoinedCall(data.userid);
// create a div for the collaborators compass
self.createCompass(data.username, data.userid, data.userimage, self.mappersOnMap[data.userid].color, !self.status);
}
},
newPeerOnMap: function (data) {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
// data.userid
// data.username
// data.userimage
// data.coords
var firstOtherPerson = Object.keys(self.mappersOnMap).length === 0;
self.mappersOnMap[data.userid] = {
id: data.userid,
name: data.username,
username: data.username,
image: data.userimage,
color: Metamaps.Util.getPastelColor(),
realtime: true,
coords: {
x: 0,
y: 0
},
};
// create an item for them in the realtime box
if (data.userid !== Metamaps.Active.Mapper.id && self.status) {
self.room.chat.sound.play('joinmap');
self.room.chat.addParticipant(self.mappersOnMap[data.userid]);
// create a div for the collaborators compass
self.createCompass(data.username, data.userid, data.userimage, self.mappersOnMap[data.userid].color, !self.status);
var notifyMessage = data.username + ' just joined the map';
if (firstOtherPerson) {
notifyMessage += ' <button type="button" class="toast-button button" onclick="Metamaps.Realtime.inviteACall(' + data.userid + ')">Suggest A Video Call</button>';
}
Metamaps.GlobalUI.notifyUser(notifyMessage);
// send this new mapper back your details, and the awareness that you've loaded the map
var update = {
userToNotify: data.userid,
username: Metamaps.Active.Mapper.get("name"),
userimage: Metamaps.Active.Mapper.get("image"),
userid: Metamaps.Active.Mapper.id,
userrealtime: self.status,
userinconversation: self.inConversation,
mapid: Metamaps.Active.Map.id
};
socket.emit('updateNewMapperList', update);
}
},
createCompass: function(name, id, image, color, hide) {
var str = '<img width="28" height="28" src="'+image+'" /><p>'+name+'</p>';
str += '<div id="compassArrow'+id+'" class="compassArrow"></div>';
$('#compass' + id).remove();
$('<div/>', {
id: 'compass' + id,
class: 'collabCompass'
}).html(str).appendTo('#wrapper');
if (hide) {
$('#compass' + id).hide();
}
$('#compass' + id + ' img').css({
'border': '2px solid ' + color
});
$('#compass' + id + ' p').css({
'background-color': color
});
},
lostPeerOnMap: function (data) {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
// data.userid
// data.username
delete self.mappersOnMap[data.userid];
self.room.chat.sound.play('leavemap');
//$('#mapper' + data.userid).remove();
$('#compass' + data.userid).remove();
self.room.chat.removeParticipant(data.username);
Metamaps.GlobalUI.notifyUser(data.username + ' just left the map');
if ((self.inConversation && self.countOthersInConversation() === 0) ||
(!self.inConversation && self.countOthersInConversation() === 1)) {
self.callEnded();
}
},
newCollaborator: function (data) {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
// data.userid
// data.username
self.mappersOnMap[data.userid].realtime = true;
//$('#mapper' + data.userid).removeClass('littleRtOff').addClass('littleRtOn');
$('#compass' + data.userid).show();
Metamaps.GlobalUI.notifyUser(data.username + ' just turned on realtime');
},
lostCollaborator: function (data) {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
// data.userid
// data.username
self.mappersOnMap[data.userid].realtime = false;
//$('#mapper' + data.userid).removeClass('littleRtOn').addClass('littleRtOff');
$('#compass' + data.userid).hide();
Metamaps.GlobalUI.notifyUser(data.username + ' just turned off realtime');
},
updatePeerCoords: function (data) {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
self.mappersOnMap[data.userid].coords={x: data.usercoords.x,y:data.usercoords.y};
self.positionPeerIcon(data.userid);
},
positionPeerIcons: function () {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
if (self.status) { // if i have realtime turned on
for (var key in self.mappersOnMap) {
var mapper = self.mappersOnMap[key];
if (mapper.realtime) {
self.positionPeerIcon(key);
}
}
}
},
positionPeerIcon: function (id) {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
var boundary = self.chatOpen ? '#wrapper' : document;
var mapper = self.mappersOnMap[id];
var xMax=$(boundary).width();
var yMax=$(boundary).height();
var compassDiameter=56;
var compassArrowSize=24;
var origPixels = Metamaps.Util.coordsToPixels(mapper.coords);
var pixels = self.limitPixelsToScreen(origPixels);
$('#compass' + id).css({
left: pixels.x + 'px',
top: pixels.y + 'px'
});
/* showing the arrow if the collaborator is off of the viewport screen */
if (origPixels.x !== pixels.x || origPixels.y !== pixels.y) {
var dy = origPixels.y - pixels.y; //opposite
var dx = origPixels.x - pixels.x; // adjacent
var ratio = dy / dx;
var angle = Math.atan2(dy, dx);
$('#compassArrow' + id).show().css({
transform: 'rotate(' + angle + 'rad)',
"-webkit-transform": 'rotate(' + angle + 'rad)',
});
if (dx > 0) {
$('#compass' + id).addClass('labelLeft');
}
} else {
$('#compassArrow' + id).hide();
$('#compass' + id).removeClass('labelLeft');
}
},
limitPixelsToScreen: function (pixels) {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
var boundary = self.chatOpen ? '#wrapper' : document;
var xLimit, yLimit;
var xMax=$(boundary).width();
var yMax=$(boundary).height();
var compassDiameter=56;
var compassArrowSize=24;
xLimit = Math.max(0 + compassArrowSize, pixels.x);
xLimit = Math.min(xLimit, xMax - compassDiameter);
yLimit = Math.max(0 + compassArrowSize, pixels.y);
yLimit = Math.min(yLimit, yMax - compassDiameter);
return {x:xLimit,y:yLimit};
},
sendCoords: function (coords) {
var self = Metamaps.Realtime;
var socket = Metamaps.Realtime.socket;
var map = Metamaps.Active.Map;
var mapper = Metamaps.Active.Mapper;
if (self.status && map.authorizeToEdit(mapper) && socket) {
var update = {
usercoords: coords,
userid: Metamaps.Active.Mapper.id,
mapid: Metamaps.Active.Map.id
};
socket.emit('updateMapperCoords', update);
}
},
sendTopicDrag: function (positions) {
var self = Metamaps.Realtime;
var socket = self.socket;
if (Metamaps.Active.Map && self.status) {
positions.mapid = Metamaps.Active.Map.id;
socket.emit('topicDrag', positions);
}
},
topicDrag: function (positions) {
var self = Metamaps.Realtime;
var socket = self.socket;
var topic;
var node;
if (Metamaps.Active.Map && self.status) {
for (var key in positions) {
topic = Metamaps.Topics.get(key);
if (topic) node = topic.get('node');
if (node) node.pos.setc(positions[key].x, positions[key].y);
} //for
Metamaps.Visualize.mGraph.plot();
}
},
sendTopicChange: function (topic) {
var self = Metamaps.Realtime;
var socket = self.socket;
var data = {
topicId: topic.id
}
socket.emit('topicChangeFromClient', data);
},
topicChange: function (data) {
var topic = Metamaps.Topics.get(data.topicId);
if (topic) {
var node = topic.get('node');
topic.fetch({
success: function (model) {
model.set({ node: node });
model.trigger('changeByOther');
}
});
}
},
sendSynapseChange: function (synapse) {
var self = Metamaps.Realtime;
var socket = self.socket;
var data = {
synapseId: synapse.id
}
socket.emit('synapseChangeFromClient', data);
},
synapseChange: function (data) {
var synapse = Metamaps.Synapses.get(data.synapseId);
if (synapse) {
// edge reset necessary because fetch causes model reset
var edge = synapse.get('edge');
synapse.fetch({
success: function (model) {
model.set({ edge: edge });
model.trigger('changeByOther');
}
});
}
},
sendMapChange: function (map) {
var self = Metamaps.Realtime;
var socket = self.socket;
var data = {
mapId: map.id
}
socket.emit('mapChangeFromClient', data);
},
mapChange: function (data) {
var map = Metamaps.Active.Map;
var isActiveMap = map && data.mapId === map.id;
if (isActiveMap) {
var permBefore = map.get('permission');
var idBefore = map.id;
map.fetch({
success: function (model, response) {
var idNow = model.id;
var permNow = model.get('permission');
if (idNow !== idBefore) {
Metamaps.Map.leavePrivateMap(); // this means the map has been changed to private
}
else if (permNow === 'public' && permBefore === 'commons') {
Metamaps.Map.commonsToPublic();
}
else if (permNow === 'commons' && permBefore === 'public') {
Metamaps.Map.publicToCommons();
}
else {
model.fetchContained();
model.trigger('changeByOther');
}
}
});
}
},
// newMessage
sendNewMessage: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
var message = data.attributes;
message.mapid = Metamaps.Active.Map.id;
socket.emit('newMessage', message);
},
newMessage: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
self.room.addMessages(new Metamaps.Backbone.MessageCollection(data));
},
// newTopic
sendNewTopic: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
if (Metamaps.Active.Map && self.status) {
data.mapperid = Metamaps.Active.Mapper.id;
data.mapid = Metamaps.Active.Map.id;
socket.emit('newTopic', data);
}
},
newTopic: function (data) {
var topic, mapping, mapper, mapperCallback, cancel;
var self = Metamaps.Realtime;
var socket = self.socket;
if (!self.status) return;
function waitThenRenderTopic() {
if (topic && mapping && mapper) {
Metamaps.Topic.renderTopic(mapping, topic, false, false);
}
else if (!cancel) {
setTimeout(waitThenRenderTopic, 10);
}
}
mapper = Metamaps.Mappers.get(data.mapperid);
if (mapper === undefined) {
mapperCallback = function (m) {
Metamaps.Mappers.add(m);
mapper = m;
};
Metamaps.Mapper.get(data.mapperid, mapperCallback);
}
$.ajax({
url: "/topics/" + data.mappableid + ".json",
success: function (response) {
Metamaps.Topics.add(response);
topic = Metamaps.Topics.get(response.id);
},
error: function () {
cancel = true;
}
});
$.ajax({
url: "/mappings/" + data.mappingid + ".json",
success: function (response) {
Metamaps.Mappings.add(response);
mapping = Metamaps.Mappings.get(response.id);
},
error: function () {
cancel = true;
}
});
waitThenRenderTopic();
},
// removeTopic
sendDeleteTopic: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
if (Metamaps.Active.Map) {
socket.emit('deleteTopicFromClient', data);
}
},
// removeTopic
sendRemoveTopic: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
if (Metamaps.Active.Map) {
data.mapid = Metamaps.Active.Map.id;
socket.emit('removeTopic', data);
}
},
removeTopic: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
if (!self.status) return;
var topic = Metamaps.Topics.get(data.mappableid);
if (topic) {
var node = topic.get('node');
var mapping = topic.getMapping();
Metamaps.Control.hideNode(node.id);
Metamaps.Topics.remove(topic);
Metamaps.Mappings.remove(mapping);
}
},
// newSynapse
sendNewSynapse: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
if (Metamaps.Active.Map) {
data.mapperid = Metamaps.Active.Mapper.id;
data.mapid = Metamaps.Active.Map.id;
socket.emit('newSynapse', data);
}
},
newSynapse: function (data) {
var topic1, topic2, node1, node2, synapse, mapping, cancel;
var self = Metamaps.Realtime;
var socket = self.socket;
if (!self.status) return;
function waitThenRenderSynapse() {
if (synapse && mapping && mapper) {
topic1 = synapse.getTopic1();
node1 = topic1.get('node');
topic2 = synapse.getTopic2();
node2 = topic2.get('node');
Metamaps.Synapse.renderSynapse(mapping, synapse, node1, node2, false);
}
else if (!cancel) {
setTimeout(waitThenRenderSynapse, 10);
}
}
mapper = Metamaps.Mappers.get(data.mapperid);
if (mapper === undefined) {
mapperCallback = function (m) {
Metamaps.Mappers.add(m);
mapper = m;
};
Metamaps.Mapper.get(data.mapperid, mapperCallback);
}
$.ajax({
url: "/synapses/" + data.mappableid + ".json",
success: function (response) {
Metamaps.Synapses.add(response);
synapse = Metamaps.Synapses.get(response.id);
},
error: function () {
cancel = true;
}
});
$.ajax({
url: "/mappings/" + data.mappingid + ".json",
success: function (response) {
Metamaps.Mappings.add(response);
mapping = Metamaps.Mappings.get(response.id);
},
error: function () {
cancel = true;
}
});
waitThenRenderSynapse();
},
// deleteSynapse
sendDeleteSynapse: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
if (Metamaps.Active.Map) {
data.mapid = Metamaps.Active.Map.id;
socket.emit('deleteSynapseFromClient', data);
}
},
// removeSynapse
sendRemoveSynapse: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
if (Metamaps.Active.Map) {
data.mapid = Metamaps.Active.Map.id;
socket.emit('removeSynapse', data);
}
},
removeSynapse: function (data) {
var self = Metamaps.Realtime;
var socket = self.socket;
if (!self.status) return;
var synapse = Metamaps.Synapses.get(data.mappableid);
if (synapse) {
var edge = synapse.get('edge');
var mapping = synapse.getMapping();
if (edge.getData("mappings").length - 1 === 0) {
Metamaps.Control.hideEdge(edge);
}
var index = _.indexOf(edge.getData("synapses"), synapse);
edge.getData("mappings").splice(index, 1);
edge.getData("synapses").splice(index, 1);
if (edge.getData("displayIndex")) {
delete edge.data.$displayIndex;
}
Metamaps.Synapses.remove(synapse);
Metamaps.Mappings.remove(mapping);
}
},
}; // end Metamaps.Realtime
/*
*
* CONTROL
*
*/
Metamaps.Control = {
init: function () {
},
selectNode: function (node,e) {
var filtered = node.getData('alpha') === 0;
if (filtered || Metamaps.Selected.Nodes.indexOf(node) != -1) return;
node.selected = true;
node.setData('dim', 30, 'current');
Metamaps.Selected.Nodes.push(node);
},
deselectAllNodes: function () {
var l = Metamaps.Selected.Nodes.length;
for (var i = l - 1; i >= 0; i -= 1) {
var node = Metamaps.Selected.Nodes[i];
Metamaps.Control.deselectNode(node);
}
Metamaps.Visualize.mGraph.plot();
},
deselectNode: function (node) {
delete node.selected;
node.setData('dim', 25, 'current');
//remove the node
Metamaps.Selected.Nodes.splice(
Metamaps.Selected.Nodes.indexOf(node), 1);
},
deleteSelected: function () {
if (!Metamaps.Active.Map) return;
var n = Metamaps.Selected.Nodes.length;
var e = Metamaps.Selected.Edges.length;
var ntext = n == 1 ? "1 topic" : n + " topics";
var etext = e == 1 ? "1 synapse" : e + " synapses";
var text = "You have " + ntext + " and " + etext + " selected. ";
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper);
if (!authorized) {
Metamaps.GlobalUI.notifyUser("Cannot edit Public map.");
return;
}
var r = confirm(text + "Are you sure you want to permanently delete them all? This will remove them from all maps they appear on.");
if (r == true) {
Metamaps.Control.deleteSelectedEdges();
Metamaps.Control.deleteSelectedNodes();
}
},
deleteSelectedNodes: function () { // refers to deleting topics permanently
if (!Metamaps.Active.Map) return;
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper);
if (!authorized) {
Metamaps.GlobalUI.notifyUser("Cannot edit Public map.");
return;
}
var l = Metamaps.Selected.Nodes.length;
for (var i = l - 1; i >= 0; i -= 1) {
var node = Metamaps.Selected.Nodes[i];
Metamaps.Control.deleteNode(node.id);
}
},
deleteNode: function (nodeid) { // refers to deleting topics permanently
if (!Metamaps.Active.Map) return;
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper);
if (!authorized) {
Metamaps.GlobalUI.notifyUser("Cannot edit Public map.");
return;
}
var node = Metamaps.Visualize.mGraph.graph.getNode(nodeid);
var topic = node.getData('topic');
var permToDelete = Metamaps.Active.Mapper.id === topic.get('user_id') || Metamaps.Active.Mapper.get('admin');
if (permToDelete) {
var mappableid = topic.id;
var mapping = node.getData('mapping');
topic.destroy();
Metamaps.Mappings.remove(mapping);
$(document).trigger(Metamaps.JIT.events.deleteTopic, [{
mappableid: mappableid
}]);
Metamaps.Control.hideNode(nodeid);
} else {
Metamaps.GlobalUI.notifyUser('Only topics you created can be deleted');
}
},
removeSelectedNodes: function () { // refers to removing topics permanently from a map
if (!Metamaps.Active.Map) return;
var l = Metamaps.Selected.Nodes.length,
i,
node,
authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper);
if (!authorized) {
Metamaps.GlobalUI.notifyUser("Cannot edit Public map.");
return;
}
for (i = l - 1; i >= 0; i -= 1) {
node = Metamaps.Selected.Nodes[i];
Metamaps.Control.removeNode(node.id);
}
},
removeNode: function (nodeid) { // refers to removing topics permanently from a map
if (!Metamaps.Active.Map) return;
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper);
var node = Metamaps.Visualize.mGraph.graph.getNode(nodeid);
if (!authorized) {
Metamaps.GlobalUI.notifyUser("Cannot edit Public map.");
return;
}
var topic = node.getData('topic');
var mappableid = topic.id;
var mapping = node.getData('mapping');
mapping.destroy();
Metamaps.Topics.remove(topic);
$(document).trigger(Metamaps.JIT.events.removeTopic, [{
mappableid: mappableid
}]);
Metamaps.Control.hideNode(nodeid);
},
hideSelectedNodes: function () {
var l = Metamaps.Selected.Nodes.length,
i,
node;
for (i = l - 1; i >= 0; i -= 1) {
node = Metamaps.Selected.Nodes[i];
Metamaps.Control.hideNode(node.id);
}
},
hideNode: function (nodeid) {
var node = Metamaps.Visualize.mGraph.graph.getNode(nodeid);
var graph = Metamaps.Visualize.mGraph;
Metamaps.Control.deselectNode(node);
node.setData('alpha', 0, 'end');
node.eachAdjacency(function (adj) {
adj.setData('alpha', 0, 'end');
});
Metamaps.Visualize.mGraph.fx.animate({
modes: ['node-property:alpha',
'edge-property:alpha'
],
duration: 500
});
setTimeout(function () {
if (nodeid == Metamaps.Visualize.mGraph.root) { // && Metamaps.Visualize.type === "RGraph"
var newroot = _.find(graph.graph.nodes, function(n){ return n.id !== nodeid; });
graph.root = newroot ? newroot.id : null;
}
Metamaps.Visualize.mGraph.graph.removeNode(nodeid);
}, 500);
Metamaps.Filter.checkMetacodes();
Metamaps.Filter.checkMappers();
},
selectEdge: function (edge) {
var filtered = edge.getData('alpha') === 0; // don't select if the edge is filtered
if (filtered || Metamaps.Selected.Edges.indexOf(edge) != -1) return;
var width = Metamaps.Mouse.edgeHoveringOver === edge ? 4 : 2;
edge.setDataset('current', {
showDesc: true,
lineWidth: width,
color: Metamaps.Settings.colors.synapses.selected
});
Metamaps.Visualize.mGraph.plot();
Metamaps.Selected.Edges.push(edge);
},
deselectAllEdges: function () {
var l = Metamaps.Selected.Edges.length;
for (var i = l - 1; i >= 0; i -= 1) {
var edge = Metamaps.Selected.Edges[i];
Metamaps.Control.deselectEdge(edge);
}
Metamaps.Visualize.mGraph.plot();
},
deselectEdge: function (edge) {
edge.setData('showDesc', false, 'current');
edge.setDataset('current', {
lineWidth: 2,
color: Metamaps.Settings.colors.synapses.normal
});
if (Metamaps.Mouse.edgeHoveringOver == edge) {
edge.setDataset('current', {
showDesc: true,
lineWidth: 4
});
}
Metamaps.Visualize.mGraph.plot();
//remove the edge
Metamaps.Selected.Edges.splice(
Metamaps.Selected.Edges.indexOf(edge), 1);
},
deleteSelectedEdges: function () { // refers to deleting topics permanently
var edge,
l = Metamaps.Selected.Edges.length;
if (!Metamaps.Active.Map) return;
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper);
if (!authorized) {
Metamaps.GlobalUI.notifyUser("Cannot edit Public map.");
return;
}
for (var i = l - 1; i >= 0; i -= 1) {
edge = Metamaps.Selected.Edges[i];
Metamaps.Control.deleteEdge(edge);
}
},
deleteEdge: function (edge) {
if (!Metamaps.Active.Map) return;
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper);
if (!authorized) {
Metamaps.GlobalUI.notifyUser("Cannot edit Public map.");
return;
}
var index = edge.getData("displayIndex") ? edge.getData("displayIndex") : 0;
var synapse = edge.getData("synapses")[index];
var mapping = edge.getData("mappings")[index];
var permToDelete = Metamaps.Active.Mapper.id === synapse.get('user_id') || Metamaps.Active.Mapper.get('admin');
if (permToDelete) {
if (edge.getData("synapses").length - 1 === 0) {
Metamaps.Control.hideEdge(edge);
}
var mappableid = synapse.id;
synapse.destroy();
// the server will destroy the mapping, we just need to remove it here
Metamaps.Mappings.remove(mapping);
edge.getData("mappings").splice(index, 1);
edge.getData("synapses").splice(index, 1);
if (edge.getData("displayIndex")) {
delete edge.data.$displayIndex;
}
$(document).trigger(Metamaps.JIT.events.deleteSynapse, [{
mappableid: mappableid
}]);
} else {
Metamaps.GlobalUI.notifyUser('Only synapses you created can be deleted');
}
},
removeSelectedEdges: function () {
var l = Metamaps.Selected.Edges.length,
i,
edge;
if (!Metamaps.Active.Map) return;
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper);
if (!authorized) {
Metamaps.GlobalUI.notifyUser("Cannot edit Public map.");
return;
}
for (i = l - 1; i >= 0; i -= 1) {
edge = Metamaps.Selected.Edges[i];
Metamaps.Control.removeEdge(edge);
}
Metamaps.Selected.Edges = new Array();
},
removeEdge: function (edge) {
if (!Metamaps.Active.Map) return;
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper);
if (!authorized) {
Metamaps.GlobalUI.notifyUser("Cannot edit Public map.");
return;
}
if (edge.getData("mappings").length - 1 === 0) {
Metamaps.Control.hideEdge(edge);
}
var index = edge.getData("displayIndex") ? edge.getData("displayIndex") : 0;
var synapse = edge.getData("synapses")[index];
var mapping = edge.getData("mappings")[index];
var mappableid = synapse.id;
mapping.destroy();
Metamaps.Synapses.remove(synapse);
edge.getData("mappings").splice(index, 1);
edge.getData("synapses").splice(index, 1);
if (edge.getData("displayIndex")) {
delete edge.data.$displayIndex;
}
$(document).trigger(Metamaps.JIT.events.removeSynapse, [{
mappableid: mappableid
}]);
},
hideSelectedEdges: function () {
var edge,
l = Metamaps.Selected.Edges.length,
i;
for (i = l - 1; i >= 0; i -= 1) {
edge = Metamaps.Selected.Edges[i];
Metamaps.Control.hideEdge(edge);
}
Metamaps.Selected.Edges = new Array();
},
hideEdge: function (edge) {
var from = edge.nodeFrom.id;
var to = edge.nodeTo.id;
edge.setData('alpha', 0, 'end');
Metamaps.Control.deselectEdge(edge);
Metamaps.Visualize.mGraph.fx.animate({
modes: ['edge-property:alpha'],
duration: 500
});
setTimeout(function () {
Metamaps.Visualize.mGraph.graph.removeAdjacence(from, to);
}, 500);
Metamaps.Filter.checkSynapses();
Metamaps.Filter.checkMappers();
},
updateSelectedPermissions: function (permission) {
var edge, synapse, node, topic;
Metamaps.GlobalUI.notifyUser('Working...');
// variables to keep track of how many nodes and synapses you had the ability to change the permission of
var nCount = 0,
sCount = 0;
// change the permission of the selected synapses, if logged in user is the original creator
var l = Metamaps.Selected.Edges.length;
for (var i = l - 1; i >= 0; i -= 1) {
edge = Metamaps.Selected.Edges[i];
synapse = edge.getData('synapses')[0];
if (synapse.authorizePermissionChange(Metamaps.Active.Mapper)) {
synapse.save({
permission: permission
});
sCount++;
}
}
// change the permission of the selected topics, if logged in user is the original creator
var l = Metamaps.Selected.Nodes.length;
for (var i = l - 1; i >= 0; i -= 1) {
node = Metamaps.Selected.Nodes[i];
topic = node.getData('topic');
if (topic.authorizePermissionChange(Metamaps.Active.Mapper)) {
topic.save({
permission: permission
});
nCount++;
}
}
var nString = nCount == 1 ? (nCount.toString() + ' topic and ') : (nCount.toString() + ' topics and ');
var sString = sCount == 1 ? (sCount.toString() + ' synapse') : (sCount.toString() + ' synapses');
var message = nString + sString + ' you created updated to ' + permission;
Metamaps.GlobalUI.notifyUser(message);
},
updateSelectedMetacodes: function (metacode_id) {
var node, topic;
Metamaps.GlobalUI.notifyUser('Working...');
var metacode = Metamaps.Metacodes.get(metacode_id);
// variables to keep track of how many nodes and synapses you had the ability to change the permission of
var nCount = 0;
// change the permission of the selected topics, if logged in user is the original creator
var l = Metamaps.Selected.Nodes.length;
for (var i = l - 1; i >= 0; i -= 1) {
node = Metamaps.Selected.Nodes[i];
topic = node.getData('topic');
if (topic.authorizeToEdit(Metamaps.Active.Mapper)) {
topic.save({
'metacode_id': metacode_id
});
nCount++;
}
}
var nString = nCount == 1 ? (nCount.toString() + ' topic') : (nCount.toString() + ' topics');
var message = nString + ' you can edit updated to ' + metacode.get('name');
Metamaps.GlobalUI.notifyUser(message);
Metamaps.Visualize.mGraph.plot();
},
}; // end Metamaps.Control