Merge pull request #649 from metamaps/feature/Metamaps-in-frontend

move Metamaps code to webpack
This commit is contained in:
Devin Howard 2016-09-23 14:22:46 +08:00 committed by GitHub
commit eef8a281cb
61 changed files with 4147 additions and 6820 deletions

View file

@ -18,4 +18,4 @@ before_script:
- nvm use stable
- npm install
script:
- bundle exec rspec && npm test && bundle exec brakeman -q -z
- bundle exec rspec && bundle exec brakeman -q -z && npm test

View file

@ -332,8 +332,5 @@ DEPENDENCIES
uglifier
uservoice-ruby
RUBY VERSION
ruby 2.3.0p0
BUNDLED WITH
1.12.5

View file

@ -13,37 +13,7 @@
//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require ./webpacked/metamaps.bundle
//= require_directory ./lib
//= require ./src/Metamaps.GlobalUI
//= require ./src/Metamaps.Router
//= require ./src/Metamaps.Backbone
//= require ./src/Metamaps.Views
//= require ./src/views/chatView
//= require ./src/views/videoView
//= require ./src/views/room
//= require ./src/JIT
//= require ./src/Metamaps.Erb
//= require ./webpacked/metamaps.bundle
//= require ./src/check-canvas-support
//= require ./src/Metamaps
//= require ./src/Metamaps.Create
//= require ./src/Metamaps.TopicCard
//= require ./src/Metamaps.SynapseCard
//= require ./src/Metamaps.Visualize
//= require ./src/Metamaps.Util
//= require ./src/Metamaps.Realtime
//= require ./src/Metamaps.Control
//= require ./src/Metamaps.Filter
//= require ./src/Metamaps.Listeners
//= require ./src/Metamaps.Organize
//= require ./src/Metamaps.Topic
//= require ./src/Metamaps.Synapse
//= require ./src/Metamaps.Map
//= require ./src/Metamaps.Account
//= require ./src/Metamaps.Mapper
//= require ./src/Metamaps.Mobile
//= require ./src/Metamaps.Admin
//= require ./src/Metamaps.Import
//= require ./src/Metamaps.AutoLayout
//= require ./src/Metamaps.PasteInput
//= require ./src/Metamaps.JIT
//= require ./src/Metamaps.Debug

File diff suppressed because it is too large Load diff

View file

@ -1,13 +0,0 @@
/*
* Metamaps.Debug.js.erb
*
* Dependencies: none!
*/
Metamaps.Debug = function () {
console.debug(Metamaps)
console.debug('Metamaps Version: ' + Metamaps.VERSION)
}
Metamaps.debug = function () {
Metamaps.Debug()
}

View file

@ -0,0 +1,20 @@
/* global Metamaps */
/*
* Metamaps.Erb.js.erb
*/
/* erb variables from rails */
window.Metamaps = window.Metamaps || {}
Metamaps.Erb = {}
Metamaps.Erb['REALTIME_SERVER'] = '<%= ENV['REALTIME_SERVER'] %>'
Metamaps.Erb['junto_spinner_darkgrey.gif'] = '<%= asset_path('junto_spinner_darkgrey.gif') %>'
Metamaps.Erb['user.png'] = '<%= asset_path('user.png') %>'
Metamaps.Erb['icons/wildcard.png'] = '<%= asset_path('icons/wildcard.png') %>'
Metamaps.Erb['topic_description_signifier.png'] = '<%= asset_path('topic_description_signifier.png') %>'
Metamaps.Erb['topic_link_signifier.png'] = '<%= asset_path('topic_link_signifier.png') %>'
Metamaps.Erb['synapse16.png'] = '<%= asset_path('synapse16.png') %>'
Metamaps.Erb['sounds/MM_sounds.mp3'] = '<%= asset_path 'sounds/MM_sounds.mp3' %>'
Metamaps.Erb['sounds/MM_sounds.ogg'] = '<%= asset_path 'sounds/MM_sounds.ogg' %>'
Metamaps.Metacodes = <%= Metacode.all.to_json.gsub(%r[(icon.*?)(\"},)], '\1?purple=stupid\2').html_safe %>
Metamaps.VERSION = '<%= METAMAPS_VERSION %>'

View file

@ -1,717 +0,0 @@
var Metamaps = window.Metamaps || {}; // this variable declaration defines a Javascript object that will contain all the variables and functions used by us, broken down into 'sub-modules' that look something like this
/*
* unless you are on a page with the Javascript InfoVis Toolkit (Topic or Map) the only section in the metamaps
* object will be these
GlobalUI
Active
Maps
Mappers
Backbone
* all these get added when you are on a page with the Javascript Infovis Toolkit
Settings
Touch
Mouse
Selected
Metacodes
Topics
Synapses
Mappings
Create
TopicCard
SynapseCard
Visualize
Util
Realtime
Control
Filter
Listeners
Organize
Map
Mapper
Topic
Synapse
JIT
*/
Metamaps.Active = {
Map: null,
Topic: null,
Mapper: null
};
Metamaps.Maps = {};
$(document).ready(function () {
// initialize all the modules
for (var prop in Metamaps) {
// this runs the init function within each sub-object on the Metamaps one
if (Metamaps.hasOwnProperty(prop) &&
Metamaps[prop] != null &&
Metamaps[prop].hasOwnProperty('init') &&
typeof (Metamaps[prop].init) == 'function'
) {
Metamaps[prop].init()
}
}
// load whichever page you are on
if (Metamaps.currentSection === "explore") {
var capitalize = Metamaps.currentPage.charAt(0).toUpperCase() + Metamaps.currentPage.slice(1)
Metamaps.Views.exploreMaps.setCollection( Metamaps.Maps[capitalize] )
if (Metamaps.currentPage === "mapper") {
Metamaps.Views.exploreMaps.fetchUserThenRender()
}
else {
Metamaps.Views.exploreMaps.render()
}
Metamaps.GlobalUI.showDiv('#explore')
}
else if (Metamaps.currentSection === "" && Metamaps.Active.Mapper) {
Metamaps.Views.exploreMaps.setCollection(Metamaps.Maps.Active)
Metamaps.Views.exploreMaps.render()
Metamaps.GlobalUI.showDiv('#explore')
}
else if (Metamaps.Active.Map || Metamaps.Active.Topic) {
Metamaps.Loading.show()
Metamaps.JIT.prepareVizData()
Metamaps.GlobalUI.showDiv('#infovis')
}
});
Metamaps.GlobalUI = {
notifyTimeout: null,
lightbox: null,
init: function () {
var self = Metamaps.GlobalUI;
self.Search.init();
self.CreateMap.init();
self.Account.init();
if ($('#toast').html().trim()) self.notifyUser($('#toast').html())
//bind lightbox clicks
$('.openLightbox').click(function (event) {
self.openLightbox($(this).attr('data-open'));
event.preventDefault();
return false;
});
$('#lightbox_screen, #lightbox_close').click(self.closeLightbox);
// initialize global backbone models and collections
if (Metamaps.Active.Mapper) Metamaps.Active.Mapper = new Metamaps.Backbone.Mapper(Metamaps.Active.Mapper);
var myCollection = Metamaps.Maps.Mine ? Metamaps.Maps.Mine : [];
var sharedCollection = Metamaps.Maps.Shared ? Metamaps.Maps.Shared : [];
var starredCollection = Metamaps.Maps.Starred ? Metamaps.Maps.Starred : [];
var mapperCollection = [];
var mapperOptionsObj = {id: 'mapper', sortBy: 'updated_at' };
if (Metamaps.Maps.Mapper) {
mapperCollection = Metamaps.Maps.Mapper.models;
mapperOptionsObj.mapperId = Metamaps.Maps.Mapper.id;
}
var featuredCollection = Metamaps.Maps.Featured ? Metamaps.Maps.Featured : [];
var activeCollection = Metamaps.Maps.Active ? Metamaps.Maps.Active : [];
Metamaps.Maps.Mine = new Metamaps.Backbone.MapsCollection(myCollection, {id: 'mine', sortBy: 'updated_at' });
Metamaps.Maps.Shared = new Metamaps.Backbone.MapsCollection(sharedCollection, {id: 'shared', sortBy: 'updated_at' });
Metamaps.Maps.Starred = new Metamaps.Backbone.MapsCollection(starredCollection, {id: 'starred', sortBy: 'updated_at' });
// 'Mapper' refers to another mapper
Metamaps.Maps.Mapper = new Metamaps.Backbone.MapsCollection(mapperCollection, mapperOptionsObj);
Metamaps.Maps.Featured = new Metamaps.Backbone.MapsCollection(featuredCollection, {id: 'featured', sortBy: 'updated_at' });
Metamaps.Maps.Active = new Metamaps.Backbone.MapsCollection(activeCollection, {id: 'active', sortBy: 'updated_at' });
},
showDiv: function (selector) {
$(selector).show()
$(selector).animate({
opacity: 1
}, 200, 'easeOutCubic')
},
hideDiv: function (selector) {
$(selector).animate({
opacity: 0
}, 200, 'easeInCubic', function () { $(this).hide() })
},
openLightbox: function (which) {
var self = Metamaps.GlobalUI;
$('.lightboxContent').hide();
$('#' + which).show();
self.lightbox = which;
$('#lightbox_overlay').show();
var heightOfContent = '-' + ($('#lightbox_main').height() / 2) + 'px';
// animate the content in from the bottom
$('#lightbox_main').animate({
'top': '50%',
'margin-top': heightOfContent
}, 200, 'easeOutCubic');
// fade the black overlay in
$('#lightbox_screen').animate({
'opacity': '0.42'
}, 200);
if (which == "switchMetacodes") {
Metamaps.Create.isSwitchingSet = true;
}
},
closeLightbox: function (event) {
var self = Metamaps.GlobalUI;
if (event) event.preventDefault();
// animate the lightbox content offscreen
$('#lightbox_main').animate({
'top': '100%',
'margin-top': '0'
}, 200, 'easeInCubic');
// fade the black overlay out
$('#lightbox_screen').animate({
'opacity': '0.0'
}, 200, function () {
$('#lightbox_overlay').hide();
});
if (self.lightbox === 'forkmap') Metamaps.GlobalUI.CreateMap.reset('fork_map');
if (self.lightbox === 'newmap') Metamaps.GlobalUI.CreateMap.reset('new_map');
if (Metamaps.Create && Metamaps.Create.isSwitchingSet) {
Metamaps.Create.cancelMetacodeSetSwitch();
}
self.lightbox = null;
},
notifyUser: function (message, leaveOpen) {
var self = Metamaps.GlobalUI;
$('#toast').html(message)
self.showDiv('#toast')
clearTimeout(self.notifyTimeOut);
if (!leaveOpen) {
self.notifyTimeOut = setTimeout(function () {
self.hideDiv('#toast')
}, 8000);
}
},
clearNotify: function() {
var self = Metamaps.GlobalUI;
clearTimeout(self.notifyTimeOut);
self.hideDiv('#toast')
},
shareInvite: function(inviteLink) {
window.prompt("To copy the invite link, press: Ctrl+C, Enter", inviteLink);
}
};
Metamaps.GlobalUI.CreateMap = {
newMap: null,
emptyMapForm: "",
emptyForkMapForm: "",
topicsToMap: [],
synapsesToMap: [],
init: function () {
var self = Metamaps.GlobalUI.CreateMap;
self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' });
self.bindFormEvents();
self.emptyMapForm = $('#new_map').html();
},
bindFormEvents: function () {
var self = Metamaps.GlobalUI.CreateMap;
$('.new_map input, .new_map div').unbind('keypress').bind('keypress', function(event) {
if (event.keyCode === 13) self.submit()
})
$('.new_map button.cancel').unbind().bind('click', function (event) {
event.preventDefault();
Metamaps.GlobalUI.closeLightbox();
});
$('.new_map button.submitMap').unbind().bind('click', self.submit);
// bind permission changer events on the createMap form
$('.permIcon').unbind().bind('click', self.switchPermission);
},
closeSuccess: function () {
$('#mapCreatedSuccess').fadeOut(300, function(){
$(this).remove();
});
},
generateSuccessMessage: function (id) {
var stringStart = "<div id='mapCreatedSuccess'><h6>SUCCESS!</h6>Your map has been created. Do you want to: <a id='mapGo' href='/maps/";
stringStart += id;
stringStart += "' onclick='Metamaps.GlobalUI.CreateMap.closeSuccess();'>Go to your new map</a>";
stringStart += "<span>OR</span><a id='mapStay' href='#' onclick='Metamaps.GlobalUI.CreateMap.closeSuccess(); return false;'>Stay on this ";
var page = Metamaps.Active.Map ? 'map' : 'page';
var stringEnd = "</a></div>";
return stringStart + page + stringEnd;
},
switchPermission: function () {
var self = Metamaps.GlobalUI.CreateMap;
self.newMap.set('permission', $(this).attr('data-permission'));
$(this).siblings('.permIcon').find('.mapPermIcon').removeClass('selected');
$(this).find('.mapPermIcon').addClass('selected');
var permText = $(this).find('.tip').html();
$(this).parents('.new_map').find('.permText').html(permText);
},
submit: function (event) {
if (event) event.preventDefault();
var self = Metamaps.GlobalUI.CreateMap;
if (Metamaps.GlobalUI.lightbox === 'forkmap') {
self.newMap.set('topicsToMap', self.topicsToMap);
self.newMap.set('synapsesToMap', self.synapsesToMap);
}
var formId = Metamaps.GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map';
var $form = $(formId);
self.newMap.set('name', $form.find('#map_name').val());
self.newMap.set('desc', $form.find('#map_desc').val());
if (self.newMap.get('name').length===0){
self.throwMapNameError();
return;
}
self.newMap.save(null, {
success: self.success
// TODO add error message
});
Metamaps.GlobalUI.closeLightbox();
Metamaps.GlobalUI.notifyUser('Working...');
},
throwMapNameError: function () {
var self = Metamaps.GlobalUI.CreateMap;
var formId = Metamaps.GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map';
var $form = $(formId);
var message = $("<div class='feedback_message'>Please enter a map name...</div>");
$form.find('#map_name').after(message);
setTimeout(function(){
message.fadeOut('fast', function(){
message.remove();
});
}, 5000);
},
success: function (model) {
var self = Metamaps.GlobalUI.CreateMap;
//push the new map onto the collection of 'my maps'
Metamaps.Maps.Mine.add(model);
var formId = Metamaps.GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map';
var form = $(formId);
Metamaps.GlobalUI.clearNotify();
$('#wrapper').append(self.generateSuccessMessage(model.id));
},
reset: function (id) {
var self = Metamaps.GlobalUI.CreateMap;
var form = $('#' + id);
if (id === "fork_map") {
self.topicsToMap = [];
self.synapsesToMap = [];
form.html(self.emptyForkMapForm);
}
else {
form.html(self.emptyMapForm);
}
self.bindFormEvents();
self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' });
return false;
},
};
Metamaps.GlobalUI.Account = {
isOpen: false,
changing: false,
init: function () {
var self = Metamaps.GlobalUI.Account;
$('.sidebarAccountIcon').click(self.toggleBox);
$('.sidebarAccountBox').click(function(event){
event.stopPropagation();
});
$('body').click(self.close);
},
toggleBox: function (event) {
var self = Metamaps.GlobalUI.Account;
if (self.isOpen) self.close();
else self.open();
event.stopPropagation();
},
open: function () {
var self = Metamaps.GlobalUI.Account;
Metamaps.Filter.close();
$('.sidebarAccountIcon .tooltipsUnder').addClass('hide');
if (!self.isOpen && !self.changing) {
self.changing = true;
$('.sidebarAccountBox').fadeIn(200, function () {
self.changing = false;
self.isOpen = true;
$('.sidebarAccountBox #user_email').focus();
});
}
},
close: function () {
var self = Metamaps.GlobalUI.Account;
$('.sidebarAccountIcon .tooltipsUnder').removeClass('hide');
if (!self.changing) {
self.changing = true;
$('.sidebarAccountBox #user_email').blur();
$('.sidebarAccountBox').fadeOut(200, function () {
self.changing = false;
self.isOpen = false;
});
}
}
};
Metamaps.GlobalUI.Search = {
locked: false,
isOpen: false,
limitTopicsToMe: false,
limitMapsToMe: false,
timeOut: null,
changing: false,
optionsInitialized: false,
init: function () {
var self = Metamaps.GlobalUI.Search;
var loader = new CanvasLoader('searchLoading');
loader.setColor('#4fb5c0'); // default is '#000000'
loader.setDiameter(24); // default is 40
loader.setDensity(41); // default is 40
loader.setRange(0.9); // default is 1.3
loader.show(); // Hidden by default
// bind the hover events
$(".sidebarSearch").hover(function () {
self.open()
}, function () {
self.close(800, false)
});
$('.sidebarSearchIcon').click(function (e) {
$('.sidebarSearchField').focus();
});
$('.sidebarSearch').click(function (e) {
e.stopPropagation();
});
$('body').click(function (e) {
self.close(0, false);
});
// open if the search is closed and user hits ctrl+/
// close if they hit ESC
$('body').bind('keyup', function (e) {
switch (e.which) {
case 191:
if ((e.ctrlKey && !self.isOpen) || (e.ctrlKey && self.locked)) {
self.open(true); // true for focus
}
break;
case 27:
if (self.isOpen) {
self.close(0, true);
}
break;
default:
break; //console.log(e.which);
}
});
self.startTypeahead();
},
lock: function() {
var self = Metamaps.GlobalUI.Search;
self.locked = true;
},
unlock: function() {
var self = Metamaps.GlobalUI.Search;
self.locked = false;
},
open: function (focus) {
var self = Metamaps.GlobalUI.Search;
clearTimeout(self.timeOut);
if (!self.isOpen && !self.changing && !self.locked) {
self.changing = true;
$('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({
width: '400px'
}, 300, function () {
if (focus) $('.sidebarSearchField').focus();
$('.sidebarSearchField, .sidebarSearch .tt-hint').css({
padding: '7px 10px 3px 10px',
width: '380px'
});
self.changing = false;
self.isOpen = true;
});
}
},
close: function (closeAfter, bypass) {
// for now
return
var self = Metamaps.GlobalUI.Search;
self.timeOut = setTimeout(function () {
if (!self.locked && !self.changing && self.isOpen && (bypass || $('.sidebarSearchField.tt-input').val() == '')) {
self.changing = true;
$('.sidebarSearchField, .sidebarSearch .tt-hint').css({
padding: '7px 0 3px 0',
width: '400px'
});
$('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({
width: '0'
}, 300, function () {
$('.sidebarSearchField').typeahead('val', '');
$('.sidebarSearchField').blur();
self.changing = false;
self.isOpen = false;
});
}
}, closeAfter);
},
startTypeahead: function () {
var self = Metamaps.GlobalUI.Search;
var mapheader = Metamaps.Active.Mapper ? '<div class="searchMapsHeader searchHeader"><h3 class="search-heading">Maps</h3><input type="checkbox" class="limitToMe" id="limitMapsToMe"></input><label for="limitMapsToMe" class="limitToMeLabel">added by me</label><div class="minimizeResults minimizeMapResults"></div><div class="clearfloat"></div></div>' : '<div class="searchMapsHeader searchHeader"><h3 class="search-heading">Maps</h3><div class="minimizeResults minimizeMapResults"></div><div class="clearfloat"></div></div>';
var topicheader = Metamaps.Active.Mapper ? '<div class="searchTopicsHeader searchHeader"><h3 class="search-heading">Topics</h3><input type="checkbox" class="limitToMe" id="limitTopicsToMe"></input><label for="limitTopicsToMe" class="limitToMeLabel">added by me</label><div class="minimizeResults minimizeTopicResults"></div><div class="clearfloat"></div></div>' : '<div class="searchTopicsHeader searchHeader"><h3 class="search-heading">Topics</h3><div class="minimizeResults minimizeTopicResults"></div><div class="clearfloat"></div></div>';
var mapperheader = '<div class="searchMappersHeader searchHeader"><h3 class="search-heading">Mappers</h3><div class="minimizeResults minimizeMapperResults"></div><div class="clearfloat"></div></div>';
var topics = {
name: 'topics',
limit: 9999,
display: function(s) { return s.label; },
templates: {
notFound: function(s) {
return Hogan.compile(topicheader + $('#topicSearchTemplate').html()).render({
value: "No results",
label: "No results",
typeImageURL: Metamaps.Erb['icons/wildcard.png'],
rtype: "noresult"
});
},
header: topicheader,
suggestion: function(s) {
return Hogan.compile($('#topicSearchTemplate').html()).render(s);
},
},
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/topics',
prepare: function(query, settings) {
settings.url += '?term=' + query;
if (Metamaps.Active.Mapper && self.limitTopicsToMe) {
settings.url += "&user=" + Metamaps.Active.Mapper.id.toString();
}
return settings;
},
},
}),
};
var maps = {
name: 'maps',
limit: 9999,
display: function(s) { return s.label; },
templates: {
notFound: function(s) {
return Hogan.compile(mapheader + $('#mapSearchTemplate').html()).render({
value: "No results",
label: "No results",
rtype: "noresult"
});
},
header: mapheader,
suggestion: function(s) {
return Hogan.compile($('#mapSearchTemplate').html()).render(s);
},
},
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/maps',
prepare: function(query, settings) {
settings.url += '?term=' + query;
if (Metamaps.Active.Mapper && self.limitMapsToMe) {
settings.url += "&user=" + Metamaps.Active.Mapper.id.toString();
}
return settings;
},
},
}),
};
var mappers = {
name: 'mappers',
limit: 9999,
display: function(s) { return s.label; },
templates: {
notFound: function(s) {
return Hogan.compile(mapperheader + $('#mapperSearchTemplate').html()).render({
value: "No results",
label: "No results",
rtype: "noresult",
profile: Metamaps.Erb['user.png']
});
},
header: mapperheader,
suggestion: function(s) {
return Hogan.compile($('#mapperSearchTemplate').html()).render(s);
},
},
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/mappers?term=%QUERY',
wildcard: '%QUERY',
},
}),
};
// Take all that crazy setup data and put it together into one beautiful typeahead call!
$('.sidebarSearchField').typeahead(
{
highlight: true,
},
[topics, maps, mappers]
);
//Set max height of the search results box to prevent it from covering bottom left footer
$('.sidebarSearchField').bind('typeahead:render', function (event) {
self.initSearchOptions();
self.hideLoader();
var h = $(window).height();
$(".tt-dropdown-menu").css('max-height', h - 100);
if (self.limitTopicsToMe) {
$('#limitTopicsToMe').prop('checked', true);
}
if (self.limitMapsToMe) {
$('#limitMapsToMe').prop('checked', true);
}
});
$(window).resize(function () {
var h = $(window).height();
$(".tt-dropdown-menu").css('max-height', h - 100);
});
// tell the autocomplete to launch a new tab with the topic, map, or mapper you clicked on
$('.sidebarSearchField').bind('typeahead:select', self.handleResultClick);
// don't do it, if they clicked on a 'addToMap' button
$('.sidebarSearch button.addToMap').click(function (event) {
event.stopPropagation();
});
// make sure that when you click on 'limit to me' or 'toggle section' it works
$('.sidebarSearchField.tt-input').keyup(function(){
if ($('.sidebarSearchField.tt-input').val() === '') {
self.hideLoader();
} else {
self.showLoader();
}
});
},
handleResultClick: function (event, datum, dataset) {
var self = Metamaps.GlobalUI.Search;
self.hideLoader();
if (["topic", "map", "mapper"].indexOf(datum.rtype) !== -1) {
self.close(0, true);
var win;
if (datum.rtype == "topic") {
Metamaps.Router.topics(datum.id);
} else if (datum.rtype == "map") {
Metamaps.Router.maps(datum.id);
} else if (datum.rtype == "mapper") {
Metamaps.Router.explore("mapper", datum.id);
}
}
},
initSearchOptions: function () {
var self = Metamaps.GlobalUI.Search;
function toggleResultSet(set) {
var s = $('.tt-dataset-' + set + ' .tt-suggestion, .tt-dataset-' + set + ' .resultnoresult');
if (s.is(':visible')) {
s.hide();
$(this).removeClass('minimizeResults').addClass('maximizeResults');
} else {
s.show();
$(this).removeClass('maximizeResults').addClass('minimizeResults');
}
}
$('.limitToMe').unbind().bind("change", function (e) {
if ($(this).attr('id') == 'limitTopicsToMe') {
self.limitTopicsToMe = !self.limitTopicsToMe;
}
if ($(this).attr('id') == 'limitMapsToMe') {
self.limitMapsToMe = !self.limitMapsToMe;
}
// set the value of the search equal to itself to retrigger the
// autocomplete event
var searchQuery = $('.sidebarSearchField.tt-input').val();
$(".sidebarSearchField").typeahead('val', '')
.typeahead('val', searchQuery);
});
// when the user clicks minimize section, hide the results for that section
$('.minimizeMapperResults').unbind().click(function (e) {
toggleResultSet.call(this, 'mappers');
});
$('.minimizeTopicResults').unbind().click(function (e) {
toggleResultSet.call(this, 'topics');
});
$('.minimizeMapResults').unbind().click(function (e) {
toggleResultSet.call(this, 'maps');
});
},
hideLoader: function () {
$('#searchLoading').hide();
},
showLoader: function () {
$('#searchLoading').show();
}
};

View file

@ -1,122 +0,0 @@
/* global Metamaps, $ */
/*
* Metamaps.Listeners.js.erb
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.Control
* - Metamaps.JIT
* - Metamaps.Visualize
*/
Metamaps.Listeners = {
init: function () {
var self = this
$(document).on('keydown', function (e) {
if (!(Metamaps.Active.Map || Metamaps.Active.Topic)) return
switch (e.which) {
case 13: // if enter key is pressed
Metamaps.JIT.enterKeyHandler()
e.preventDefault()
break
case 27: // if esc key is pressed
Metamaps.JIT.escKeyHandler()
break
case 65: // if a or A is pressed
if (e.ctrlKey) {
Metamaps.Control.deselectAllNodes()
Metamaps.Control.deselectAllEdges()
e.preventDefault()
Metamaps.Visualize.mGraph.graph.eachNode(function (n) {
Metamaps.Control.selectNode(n, e)
})
Metamaps.Visualize.mGraph.plot()
}
break
case 68: // if d or D is pressed
if (e.ctrlKey) {
e.preventDefault()
Metamaps.Control.deleteSelected()
}
break
case 69: // if e or E is pressed
if (e.ctrlKey && Metamaps.Active.Map) {
e.preventDefault()
Metamaps.JIT.zoomExtents(null, Metamaps.Visualize.mGraph.canvas)
break
}
if (e.altKey && Metamaps.Active.Topic) {
e.preventDefault()
if (Metamaps.Active.Topic) {
self.centerAndReveal(Metamaps.Selected.Nodes, {
center: true,
reveal: false
})
}
break
}
break
case 72: // if h or H is pressed
if (e.ctrlKey) {
e.preventDefault()
Metamaps.Control.hideSelectedNodes()
Metamaps.Control.hideSelectedEdges()
}
break
case 77: // if m or M is pressed
if (e.ctrlKey) {
e.preventDefault()
Metamaps.Control.removeSelectedNodes()
Metamaps.Control.removeSelectedEdges()
}
break
case 82: // if r or R is pressed
if (e.altKey && Metamaps.Active.Topic) {
e.preventDefault()
self.centerAndReveal(Metamaps.Selected.Nodes, {
center: false,
reveal: true
})
}
break
case 84: // if t or T is pressed
if (e.altKey && Metamaps.Active.Topic) {
e.preventDefault()
self.centerAndReveal(Metamaps.Selected.Nodes, {
center: true,
reveal: true
})
}
break
default:
// console.log(e.which)
break
}
})
$(window).resize(function () {
if (Metamaps.Visualize && Metamaps.Visualize.mGraph) Metamaps.Visualize.mGraph.canvas.resize($(window).width(), $(window).height())
if ((Metamaps.Active.Map || Metamaps.Active.Topic) && Metamaps.Famous && Metamaps.Famous.maps.surf) Metamaps.Famous.maps.reposition()
if (Metamaps.Active.Map && Metamaps.Realtime.inConversation) Metamaps.Realtime.positionVideos()
Metamaps.Mobile.resizeTitle()
})
},
centerAndReveal: function(nodes, opts) {
if (nodes.length < 1) return
var node = nodes[nodes.length - 1]
if (opts.center && opts.reveal) {
Metamaps.Topic.centerOn(node.id, function() {
Metamaps.Topic.fetchRelatives(nodes)
})
} else if (opts.center) {
Metamaps.Topic.centerOn(node.id)
} else if (opts.reveal) {
Metamaps.Topic.fetchRelatives(nodes)
}
}
}; // end Metamaps.Listeners

View file

@ -1,754 +0,0 @@
/* global Metamaps, $ */
/*
* Metamaps.Map.js.erb
*
* Dependencies:
* - Metamaps.AutoLayout
* - Metamaps.Create
* - Metamaps.Erb
* - Metamaps.Filter
* - Metamaps.JIT
* - Metamaps.Loading
* - Metamaps.Maps
* - Metamaps.Realtime
* - Metamaps.Router
* - Metamaps.Selected
* - Metamaps.SynapseCard
* - Metamaps.TopicCard
* - Metamaps.Visualize
* - Metamaps.Active
* - Metamaps.Backbone
* - Metamaps.GlobalUI
* - Metamaps.Mappers
* - Metamaps.Mappings
* - Metamaps.Messages
* - Metamaps.Synapses
* - Metamaps.Topics
*
* Major sub-modules:
* - Metamaps.Map.CheatSheet
* - Metamaps.Map.InfoBox
*/
Metamaps.Map = {
events: {
editedByActiveMapper: 'Metamaps:Map:events:editedByActiveMapper'
},
init: function () {
var self = Metamaps.Map
// prevent right clicks on the main canvas, so as to not get in the way of our right clicks
$('#center-container').bind('contextmenu', function (e) {
return false
})
$('.starMap').click(function () {
if ($(this).is('.starred')) self.unstar()
else self.star()
})
$('.sidebarFork').click(function () {
self.fork()
})
Metamaps.GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html()
self.updateStar()
self.InfoBox.init()
self.CheatSheet.init()
$(document).on(Metamaps.Map.events.editedByActiveMapper, self.editedByActiveMapper)
},
launch: function (id) {
var bb = Metamaps.Backbone
var start = function (data) {
Metamaps.Active.Map = new bb.Map(data.map)
Metamaps.Mappers = new bb.MapperCollection(data.mappers)
Metamaps.Collaborators = new bb.MapperCollection(data.collaborators)
Metamaps.Topics = new bb.TopicCollection(data.topics)
Metamaps.Synapses = new bb.SynapseCollection(data.synapses)
Metamaps.Mappings = new bb.MappingCollection(data.mappings)
Metamaps.Messages = data.messages
Metamaps.Stars = data.stars
Metamaps.Backbone.attachCollectionEvents()
var map = Metamaps.Active.Map
var mapper = Metamaps.Active.Mapper
// add class to .wrapper for specifying whether you can edit the map
if (map.authorizeToEdit(mapper)) {
$('.wrapper').addClass('canEditMap')
}
// add class to .wrapper for specifying if the map can
// be collaborated on
if (map.get('permission') === 'commons') {
$('.wrapper').addClass('commonsMap')
}
Metamaps.Map.updateStar()
// set filter mapper H3 text
$('#filter_by_mapper h3').html('MAPPERS')
// build and render the visualization
Metamaps.Visualize.type = 'ForceDirected'
Metamaps.JIT.prepareVizData()
// update filters
Metamaps.Filter.reset()
// reset selected arrays
Metamaps.Selected.reset()
// set the proper mapinfobox content
Metamaps.Map.InfoBox.load()
// these three update the actual filter box with the right list items
Metamaps.Filter.checkMetacodes()
Metamaps.Filter.checkSynapses()
Metamaps.Filter.checkMappers()
Metamaps.Realtime.startActiveMap()
Metamaps.Loading.hide()
// for mobile
$('#header_content').html(map.get('name'))
}
$.ajax({
url: '/maps/' + id + '/contains.json',
success: start
})
},
end: function () {
if (Metamaps.Active.Map) {
$('.wrapper').removeClass('canEditMap commonsMap')
Metamaps.AutoLayout.resetSpiral()
$('.rightclickmenu').remove()
Metamaps.TopicCard.hideCard()
Metamaps.SynapseCard.hideCard()
Metamaps.Create.newTopic.hide(true) // true means force (and override pinned)
Metamaps.Create.newSynapse.hide()
Metamaps.Filter.close()
Metamaps.Map.InfoBox.close()
Metamaps.Realtime.endActiveMap()
}
},
updateStar: function () {
if (!Metamaps.Active.Mapper || !Metamaps.Stars) return
// update the star/unstar icon
if (Metamaps.Stars.find(function (s) { return s.user_id === Metamaps.Active.Mapper.id })) {
$('.starMap').addClass('starred')
$('.starMap .tooltipsAbove').html('Unstar')
} else {
$('.starMap').removeClass('starred')
$('.starMap .tooltipsAbove').html('Star')
}
},
star: function () {
var self = Metamaps.Map
if (!Metamaps.Active.Map) return
$.post('/maps/' + Metamaps.Active.Map.id + '/star')
Metamaps.Stars.push({ user_id: Metamaps.Active.Mapper.id, map_id: Metamaps.Active.Map.id })
Metamaps.Maps.Starred.add(Metamaps.Active.Map)
Metamaps.GlobalUI.notifyUser('Map is now starred')
self.updateStar()
},
unstar: function () {
var self = Metamaps.Map
if (!Metamaps.Active.Map) return
$.post('/maps/' + Metamaps.Active.Map.id + '/unstar')
Metamaps.Stars = Metamaps.Stars.filter(function (s) { return s.user_id != Metamaps.Active.Mapper.id })
Metamaps.Maps.Starred.remove(Metamaps.Active.Map)
self.updateStar()
},
fork: function () {
Metamaps.GlobalUI.openLightbox('forkmap')
var nodes_data = '',
synapses_data = ''
var nodes_array = []
var synapses_array = []
// collect the unfiltered topics
Metamaps.Visualize.mGraph.graph.eachNode(function (n) {
// if the opacity is less than 1 then it's filtered
if (n.getData('alpha') === 1) {
var id = n.getData('topic').id
nodes_array.push(id)
var x, y
if (n.pos.x && n.pos.y) {
x = n.pos.x
y = n.pos.y
} else {
var x = Math.cos(n.pos.theta) * n.pos.rho
var y = Math.sin(n.pos.theta) * n.pos.rho
}
nodes_data += id + '/' + x + '/' + y + ','
}
})
// collect the unfiltered synapses
Metamaps.Synapses.each(function (synapse) {
var desc = synapse.get('desc')
var descNotFiltered = Metamaps.Filter.visible.synapses.indexOf(desc) > -1
// make sure that both topics are being added, otherwise, it
// doesn't make sense to add the synapse
var topicsNotFiltered = nodes_array.indexOf(synapse.get('node1_id')) > -1
topicsNotFiltered = topicsNotFiltered && nodes_array.indexOf(synapse.get('node2_id')) > -1
if (descNotFiltered && topicsNotFiltered) {
synapses_array.push(synapse.id)
}
})
synapses_data = synapses_array.join()
nodes_data = nodes_data.slice(0, -1)
Metamaps.GlobalUI.CreateMap.topicsToMap = nodes_data
Metamaps.GlobalUI.CreateMap.synapsesToMap = synapses_data
},
leavePrivateMap: function () {
var map = Metamaps.Active.Map
Metamaps.Maps.Active.remove(map)
Metamaps.Maps.Featured.remove(map)
Metamaps.Router.home()
Metamaps.GlobalUI.notifyUser('Sorry! That map has been changed to Private.')
},
cantEditNow: function () {
Metamaps.Realtime.turnOff(true); // true is for 'silence'
Metamaps.GlobalUI.notifyUser('Map was changed to Public. Editing is disabled.')
Metamaps.Active.Map.trigger('changeByOther')
},
canEditNow: function () {
var confirmString = "You've been granted permission to edit this map. "
confirmString += 'Do you want to reload and enable realtime collaboration?'
var c = confirm(confirmString)
if (c) {
Metamaps.Router.maps(Metamaps.Active.Map.id)
}
},
editedByActiveMapper: function () {
if (Metamaps.Active.Mapper) {
Metamaps.Mappers.add(Metamaps.Active.Mapper)
}
},
exportImage: function () {
var canvas = {}
canvas.canvas = document.createElement('canvas')
canvas.canvas.width = 1880 // 960
canvas.canvas.height = 1260 // 630
canvas.scaleOffsetX = 1
canvas.scaleOffsetY = 1
canvas.translateOffsetY = 0
canvas.translateOffsetX = 0
canvas.denySelected = true
canvas.getSize = function () {
if (this.size) return this.size
var canvas = this.canvas
return this.size = {
width: canvas.width,
height: canvas.height
}
}
canvas.scale = function (x, y) {
var px = this.scaleOffsetX * x,
py = this.scaleOffsetY * y
var dx = this.translateOffsetX * (x - 1) / px,
dy = this.translateOffsetY * (y - 1) / py
this.scaleOffsetX = px
this.scaleOffsetY = py
this.getCtx().scale(x, y)
this.translate(dx, dy)
}
canvas.translate = function (x, y) {
var sx = this.scaleOffsetX,
sy = this.scaleOffsetY
this.translateOffsetX += x * sx
this.translateOffsetY += y * sy
this.getCtx().translate(x, y)
}
canvas.getCtx = function () {
return this.canvas.getContext('2d')
}
// center it
canvas.getCtx().translate(1880 / 2, 1260 / 2)
var mGraph = Metamaps.Visualize.mGraph
var id = mGraph.root
var root = mGraph.graph.getNode(id)
var T = !!root.visited
// pass true to avoid basing it on a selection
Metamaps.JIT.zoomExtents(null, canvas, true)
var c = canvas.canvas,
ctx = canvas.getCtx(),
scale = canvas.scaleOffsetX
// draw a grey background
ctx.fillStyle = '#d8d9da'
var xPoint = (-(c.width / scale) / 2) - (canvas.translateOffsetX / scale),
yPoint = (-(c.height / scale) / 2) - (canvas.translateOffsetY / scale)
ctx.fillRect(xPoint, yPoint, c.width / scale, c.height / scale)
// draw the graph
mGraph.graph.eachNode(function (node) {
var nodeAlpha = node.getData('alpha')
node.eachAdjacency(function (adj) {
var nodeTo = adj.nodeTo
if (!!nodeTo.visited === T && node.drawn && nodeTo.drawn) {
mGraph.fx.plotLine(adj, canvas)
}
})
if (node.drawn) {
mGraph.fx.plotNode(node, canvas)
}
if (!mGraph.labelsHidden) {
if (node.drawn && nodeAlpha >= 0.95) {
mGraph.labels.plotLabel(canvas, node)
} else {
mGraph.labels.hideLabel(node, false)
}
}
node.visited = !T
})
var imageData = {
encoded_image: canvas.canvas.toDataURL()
}
var map = Metamaps.Active.Map
var today = new Date()
var dd = today.getDate()
var mm = today.getMonth() + 1; // January is 0!
var yyyy = today.getFullYear()
if (dd < 10) {
dd = '0' + dd
}
if (mm < 10) {
mm = '0' + mm
}
today = mm + '/' + dd + '/' + yyyy
var mapName = map.get('name').split(' ').join([separator = '-'])
var downloadMessage = ''
downloadMessage += 'Captured map screenshot! '
downloadMessage += "<a href='" + imageData.encoded_image + "' "
downloadMessage += "download='metamap-" + map.id + '-' + mapName + '-' + today + ".png'>DOWNLOAD</a>"
Metamaps.GlobalUI.notifyUser(downloadMessage)
$.ajax({
type: 'POST',
dataType: 'json',
url: '/maps/' + Metamaps.Active.Map.id + '/upload_screenshot',
data: imageData,
success: function (data) {
console.log('successfully uploaded map screenshot')
},
error: function () {
console.log('failed to save map screenshot')
}
})
}
}
/*
*
* CHEATSHEET
*
*/
Metamaps.Map.CheatSheet = {
init: function () {
// tab the cheatsheet
$('#cheatSheet').tabs()
$('#quickReference').tabs().addClass('ui-tabs-vertical ui-helper-clearfix')
$('#quickReference .ui-tabs-nav li').removeClass('ui-corner-top').addClass('ui-corner-left')
// id = the id of a vimeo video
var switchVideo = function (element, id) {
$('.tutorialItem').removeClass('active')
$(element).addClass('active')
$('#tutorialVideo').attr('src', '//player.vimeo.com/video/' + id)
}
$('#gettingStarted').click(function () {
// switchVideo(this,'88334167')
})
$('#upYourSkillz').click(function () {
// switchVideo(this,'100118167')
})
$('#advancedMapping').click(function () {
// switchVideo(this,'88334167')
})
}
}; // end Metamaps.Map.CheatSheet
/*
*
* INFOBOX
*
*/
Metamaps.Map.InfoBox = {
isOpen: false,
changing: false,
selectingPermission: false,
changePermissionText: "<div class='tooltips'>As the creator, you can change the permission of this map, and the permission of all the topics and synapses you have authority to change will change as well.</div>",
nameHTML: '<span class="best_in_place best_in_place_name" id="best_in_place_map_{{id}}_name" data-url="/maps/{{id}}" data-object="map" data-attribute="name" data-type="textarea" data-activator="#mapInfoName">{{name}}</span>',
descHTML: '<span class="best_in_place best_in_place_desc" id="best_in_place_map_{{id}}_desc" data-url="/maps/{{id}}" data-object="map" data-attribute="desc" data-nil="Click to add description..." data-type="textarea" data-activator="#mapInfoDesc">{{desc}}</span>',
init: function () {
var self = Metamaps.Map.InfoBox
$('.mapInfoIcon').click(self.toggleBox)
$('.mapInfoBox').click(function (event) {
event.stopPropagation()
})
$('body').click(self.close)
self.attachEventListeners()
self.generateBoxHTML = Hogan.compile($('#mapInfoBoxTemplate').html())
var querystring = window.location.search.replace(/^\?/, '')
if (querystring == 'new') {
self.open()
$('.mapInfoBox').addClass('mapRequestTitle')
}
},
toggleBox: function (event) {
var self = Metamaps.Map.InfoBox
if (self.isOpen) self.close()
else self.open()
event.stopPropagation()
},
open: function () {
var self = Metamaps.Map.InfoBox
$('.mapInfoIcon div').addClass('hide')
if (!self.isOpen && !self.changing) {
self.changing = true
$('.mapInfoBox').fadeIn(200, function () {
self.changing = false
self.isOpen = true
})
}
},
close: function () {
var self = Metamaps.Map.InfoBox
$('.mapInfoIcon div').removeClass('hide')
if (!self.changing) {
self.changing = true
$('.mapInfoBox').fadeOut(200, function () {
self.changing = false
self.isOpen = false
self.hidePermissionSelect()
$('.mapContributors .tip').hide()
})
}
},
load: function () {
var self = Metamaps.Map.InfoBox
var map = Metamaps.Active.Map
var obj = map.pick('permission', 'topic_count', 'synapse_count')
var isCreator = map.authorizePermissionChange(Metamaps.Active.Mapper)
var canEdit = map.authorizeToEdit(Metamaps.Active.Mapper)
var relevantPeople = map.get('permission') === 'commons' ? Metamaps.Mappers : Metamaps.Collaborators
var shareable = map.get('permission') !== 'private'
obj['name'] = canEdit ? Hogan.compile(self.nameHTML).render({id: map.id, name: map.get('name')}) : map.get('name')
obj['desc'] = canEdit ? Hogan.compile(self.descHTML).render({id: map.id, desc: map.get('desc')}) : map.get('desc')
obj['map_creator_tip'] = isCreator ? self.changePermissionText : ''
obj['contributor_count'] = relevantPeople.length
obj['contributors_class'] = relevantPeople.length > 1 ? 'multiple' : ''
obj['contributors_class'] += relevantPeople.length === 2 ? ' mTwo' : ''
obj['contributor_image'] = relevantPeople.length > 0 ? relevantPeople.models[0].get('image') : Metamaps.Erb['user.png']
obj['contributor_list'] = self.createContributorList()
obj['user_name'] = isCreator ? 'You' : map.get('user_name')
obj['created_at'] = map.get('created_at_clean')
obj['updated_at'] = map.get('updated_at_clean')
var classes = isCreator ? 'yourMap' : ''
classes += canEdit ? ' canEdit' : ''
classes += shareable ? ' shareable' : ''
$('.mapInfoBox').removeClass('shareable yourMap canEdit')
.addClass(classes)
.html(self.generateBoxHTML.render(obj))
self.attachEventListeners()
},
attachEventListeners: function () {
var self = Metamaps.Map.InfoBox
$('.mapInfoBox.canEdit .best_in_place').best_in_place()
// because anyone who can edit the map can change the map title
var bipName = $('.mapInfoBox .best_in_place_name')
bipName.unbind('best_in_place:activate').bind('best_in_place:activate', function () {
var $el = bipName.find('textarea')
var el = $el[0]
$el.attr('maxlength', '140')
$('.mapInfoName').append('<div class="nameCounter forMap"></div>')
var callback = function (data) {
$('.nameCounter.forMap').html(data.all + '/140')
}
Countable.live(el, callback)
})
bipName.unbind('best_in_place:deactivate').bind('best_in_place:deactivate', function () {
$('.nameCounter.forMap').remove()
})
$('.mapInfoName .best_in_place_name').unbind('ajax:success').bind('ajax:success', function () {
var name = $(this).html()
Metamaps.Active.Map.set('name', name)
Metamaps.Active.Map.trigger('saved')
// mobile menu
$('#header_content').html(name)
$('.mapInfoBox').removeClass('mapRequestTitle')
document.title = name + ' | Metamaps'
})
$('.mapInfoDesc .best_in_place_desc').unbind('ajax:success').bind('ajax:success', function () {
var desc = $(this).html()
Metamaps.Active.Map.set('desc', desc)
Metamaps.Active.Map.trigger('saved')
})
$('.yourMap .mapPermission').unbind().click(self.onPermissionClick)
// .yourMap in the unbind/bind is just a namespace for the events
// not a reference to the class .yourMap on the .mapInfoBox
$('.mapInfoBox.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect)
$('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap)
$('.mapContributors span, #mapContribs').unbind().click(function (event) {
$('.mapContributors .tip').toggle()
event.stopPropagation()
})
$('.mapContributors .tip').unbind().click(function (event) {
event.stopPropagation()
})
$('.mapContributors .tip li a').click(Metamaps.Router.intercept)
$('.mapInfoBox').unbind('.hideTip').bind('click.hideTip', function () {
$('.mapContributors .tip').hide()
})
self.addTypeahead()
},
addTypeahead: function () {
var self = Metamaps.Map.InfoBox
if (!Metamaps.Active.Map) return
// for autocomplete
var collaborators = {
name: 'collaborators',
limit: 9999,
display: function(s) { return s.label; },
templates: {
notFound: function(s) {
return Hogan.compile($('#collaboratorSearchTemplate').html()).render({
value: "No results",
label: "No results",
rtype: "noresult",
profile: Metamaps.Erb['user.png'],
});
},
suggestion: function(s) {
return Hogan.compile($('#collaboratorSearchTemplate').html()).render(s);
},
},
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/mappers?term=%QUERY',
wildcard: '%QUERY',
},
})
}
// for adding map collaborators, who will have edit rights
if (Metamaps.Active.Mapper && Metamaps.Active.Mapper.id === Metamaps.Active.Map.get('user_id')) {
$('.collaboratorSearchField').typeahead(
{
highlight: false,
},
[collaborators]
)
$('.collaboratorSearchField').bind('typeahead:select', self.handleResultClick)
$('.mapContributors .removeCollaborator').click(function () {
self.removeCollaborator(parseInt($(this).data('id')))
})
}
},
removeCollaborator: function (collaboratorId) {
var self = Metamaps.Map.InfoBox
Metamaps.Collaborators.remove(Metamaps.Collaborators.get(collaboratorId))
var mapperIds = Metamaps.Collaborators.models.map(function (mapper) { return mapper.id })
$.post('/maps/' + Metamaps.Active.Map.id + '/access', { access: mapperIds })
self.updateNumbers()
},
addCollaborator: function (newCollaboratorId) {
var self = Metamaps.Map.InfoBox
if (Metamaps.Collaborators.get(newCollaboratorId)) {
Metamaps.GlobalUI.notifyUser('That user already has access')
return
}
function callback(mapper) {
Metamaps.Collaborators.add(mapper)
var mapperIds = Metamaps.Collaborators.models.map(function (mapper) { return mapper.id })
$.post('/maps/' + Metamaps.Active.Map.id + '/access', { access: mapperIds })
var name = Metamaps.Collaborators.get(newCollaboratorId).get('name')
Metamaps.GlobalUI.notifyUser(name + ' will be notified by email')
self.updateNumbers()
}
$.getJSON('/users/' + newCollaboratorId + '.json', callback)
},
handleResultClick: function (event, item) {
var self = Metamaps.Map.InfoBox
self.addCollaborator(item.id)
$('.collaboratorSearchField').typeahead('val', '')
},
updateNameDescPerm: function (name, desc, perm) {
$('.mapInfoBox').removeClass('mapRequestTitle')
$('.mapInfoName .best_in_place_name').html(name)
$('.mapInfoDesc .best_in_place_desc').html(desc)
$('.mapInfoBox .mapPermission').removeClass('commons public private').addClass(perm)
},
createContributorList: function () {
var self = Metamaps.Map.InfoBox
var relevantPeople = Metamaps.Active.Map.get('permission') === 'commons' ? Metamaps.Mappers : Metamaps.Collaborators
var activeMapperIsCreator = Metamaps.Active.Mapper && Metamaps.Active.Mapper.id === Metamaps.Active.Map.get('user_id')
var string = ''
string += '<ul>'
relevantPeople.each(function (m) {
var isCreator = Metamaps.Active.Map.get('user_id') === m.get('id')
string += '<li><a href="/explore/mapper/' + m.get('id') + '">' + '<img class="rtUserImage" width="25" height="25" src="' + m.get('image') + '" />' + m.get('name')
if (isCreator) string += ' (creator)'
string += '</a>'
if (activeMapperIsCreator && !isCreator) string += '<span class="removeCollaborator" data-id="' + m.get('id') + '"></span>'
string += '</li>'
})
string += '</ul>'
if (activeMapperIsCreator) {
string += '<div class="collabSearchField"><span class="addCollab"></span><input class="collaboratorSearchField" placeholder="Add a collaborator!"></input></div>'
}
return string
},
updateNumbers: function () {
if (!Metamaps.Active.Map) return
var self = Metamaps.Map.InfoBox
var mapper = Metamaps.Active.Mapper
var relevantPeople = Metamaps.Active.Map.get('permission') === 'commons' ? Metamaps.Mappers : Metamaps.Collaborators
var contributors_class = ''
if (relevantPeople.length === 2) contributors_class = 'multiple mTwo'
else if (relevantPeople.length > 2) contributors_class = 'multiple'
var contributors_image = Metamaps.Erb['user.png']
if (relevantPeople.length > 0) {
// get the first contributor and use their image
contributors_image = relevantPeople.models[0].get('image')
}
$('.mapContributors img').attr('src', contributors_image).removeClass('multiple mTwo').addClass(contributors_class)
$('.mapContributors span').text(relevantPeople.length)
$('.mapContributors .tip').html(self.createContributorList())
self.addTypeahead()
$('.mapContributors .tip').unbind().click(function (event) {
event.stopPropagation()
})
$('.mapTopics').text(Metamaps.Topics.length)
$('.mapSynapses').text(Metamaps.Synapses.length)
$('.mapEditedAt').html('<span>Last edited: </span>' + Metamaps.Util.nowDateFormatted())
},
onPermissionClick: function (event) {
var self = Metamaps.Map.InfoBox
if (!self.selectingPermission) {
self.selectingPermission = true
$(this).addClass('minimize') // this line flips the drop down arrow to a pull up arrow
if ($(this).hasClass('commons')) {
$(this).append('<ul class="permissionSelect"><li class="public"></li><li class="private"></li></ul>')
} else if ($(this).hasClass('public')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="private"></li></ul>')
} else if ($(this).hasClass('private')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="public"></li></ul>')
}
$('.mapPermission .permissionSelect li').click(self.selectPermission)
event.stopPropagation()
}
},
hidePermissionSelect: function () {
var self = Metamaps.Map.InfoBox
self.selectingPermission = false
$('.mapPermission').removeClass('minimize') // this line flips the pull up arrow to a drop down arrow
$('.mapPermission .permissionSelect').remove()
},
selectPermission: function (event) {
var self = Metamaps.Map.InfoBox
self.selectingPermission = false
var permission = $(this).attr('class')
Metamaps.Active.Map.save({
permission: permission
})
Metamaps.Active.Map.updateMapWrapper()
shareable = permission === 'private' ? '' : 'shareable'
$('.mapPermission').removeClass('commons public private minimize').addClass(permission)
$('.mapPermission .permissionSelect').remove()
$('.mapInfoBox').removeClass('shareable').addClass(shareable)
event.stopPropagation()
},
deleteActiveMap: function () {
var confirmString = 'Are you sure you want to delete this map? '
confirmString += 'This action is irreversible. It will not delete the topics and synapses on the map.'
var doIt = confirm(confirmString)
var map = Metamaps.Active.Map
var mapper = Metamaps.Active.Mapper
var authorized = map.authorizePermissionChange(mapper)
if (doIt && authorized) {
Metamaps.Map.InfoBox.close()
Metamaps.Maps.Active.remove(map)
Metamaps.Maps.Featured.remove(map)
Metamaps.Maps.Mine.remove(map)
Metamaps.Maps.Shared.remove(map)
map.destroy()
Metamaps.Router.home()
Metamaps.GlobalUI.notifyUser('Map eliminated!')
}
else if (!authorized) {
alert("Hey now. We can't just go around willy nilly deleting other people's maps now can we? Run off and find something constructive to do, eh?")
}
}
}; // end Metamaps.Map.InfoBox

View file

@ -1,20 +0,0 @@
/* global Metamaps, $ */
/*
* Metamaps.Mapper.js.erb
*
* Dependencies: none!
*/
Metamaps.Mapper = {
// this function is to retrieve a mapper JSON object from the database
// @param id = the id of the mapper to retrieve
get: function (id, callback) {
return $.ajax({
url: '/users/' + id + '.json',
success: function (data) {
callback(new Metamaps.Backbone.Mapper(data))
}
})
}
}; // end Metamaps.Mapper

View file

@ -1,245 +0,0 @@
/* global Metamaps, Backbone, $ */
/*
* Metamaps.Router.js.erb
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.GlobalUI
* - Metamaps.JIT
* - Metamaps.Loading
* - Metamaps.Map
* - Metamaps.Maps
* - Metamaps.Topic
* - Metamaps.Views
* - Metamaps.Visualize
*/
;(function () {
var Router = Backbone.Router.extend({
routes: {
'': 'home', // #home
'explore/:section': 'explore', // #explore/active
'explore/:section/:id': 'explore', // #explore/mapper/1234
'maps/:id': 'maps' // #maps/7
},
home: function () {
clearTimeout(Metamaps.Router.timeoutId)
if (Metamaps.Active.Mapper) document.title = 'Explore Active Maps | Metamaps'
else document.title = 'Home | Metamaps'
Metamaps.Router.currentSection = ''
Metamaps.Router.currentPage = ''
$('.wrapper').removeClass('mapPage topicPage')
var classes = Metamaps.Active.Mapper ? 'homePage explorePage' : 'homePage'
$('.wrapper').addClass(classes)
var navigate = function () {
Metamaps.Router.timeoutId = setTimeout(function () {
Metamaps.Router.navigate('')
}, 300)
}
// all this only for the logged in home page
if (Metamaps.Active.Mapper) {
$('.homeButton a').attr('href', '/')
Metamaps.GlobalUI.hideDiv('#yield')
Metamaps.GlobalUI.showDiv('#explore')
Metamaps.Views.exploreMaps.setCollection(Metamaps.Maps.Active)
if (Metamaps.Maps.Active.length === 0) {
Metamaps.Maps.Active.getMaps(navigate) // this will trigger an explore maps render
} else {
Metamaps.Views.exploreMaps.render(navigate)
}
} else {
// logged out home page
Metamaps.GlobalUI.hideDiv('#explore')
Metamaps.GlobalUI.showDiv('#yield')
Metamaps.Router.timeoutId = setTimeout(navigate, 500)
}
Metamaps.GlobalUI.hideDiv('#infovis')
Metamaps.GlobalUI.hideDiv('#instructions')
Metamaps.Map.end()
Metamaps.Topic.end()
Metamaps.Active.Map = null
Metamaps.Active.Topic = null
},
explore: function (section, id) {
clearTimeout(Metamaps.Router.timeoutId)
// just capitalize the variable section
// either 'featured', 'mapper', or 'active'
var capitalize = section.charAt(0).toUpperCase() + section.slice(1)
if (section === 'shared' || section === 'featured' || section === 'active' || section === 'starred') {
document.title = 'Explore ' + capitalize + ' Maps | Metamaps'
} else if (section === 'mapper') {
$.ajax({
url: '/users/' + id + '.json',
success: function (response) {
document.title = response.name + ' | Metamaps'
},
error: function () {}
})
} else if (section === 'mine') {
document.title = 'Explore My Maps | Metamaps'
}
if (Metamaps.Active.Mapper && section != 'mapper') $('.homeButton a').attr('href', '/explore/' + section)
$('.wrapper').removeClass('homePage mapPage topicPage')
$('.wrapper').addClass('explorePage')
Metamaps.Router.currentSection = 'explore'
Metamaps.Router.currentPage = section
// this will mean it's a mapper page being loaded
if (id) {
if (Metamaps.Maps.Mapper.mapperId !== id) {
// empty the collection if we are trying to load the maps
// collection of a different mapper than we had previously
Metamaps.Maps.Mapper.reset()
Metamaps.Maps.Mapper.page = 1
}
Metamaps.Maps.Mapper.mapperId = id
}
Metamaps.Views.exploreMaps.setCollection(Metamaps.Maps[capitalize])
var navigate = function () {
var path = '/explore/' + Metamaps.Router.currentPage
// alter url if for mapper profile page
if (Metamaps.Router.currentPage === 'mapper') {
path += '/' + Metamaps.Maps.Mapper.mapperId
}
Metamaps.Router.navigate(path)
}
var navigateTimeout = function () {
Metamaps.Router.timeoutId = setTimeout(navigate, 300)
}
if (Metamaps.Maps[capitalize].length === 0) {
Metamaps.Loading.show()
setTimeout(function () {
Metamaps.Maps[capitalize].getMaps(navigate) // this will trigger an explore maps render
}, 300) // wait 300 milliseconds till the other animations are done to do the fetch
} else {
if (id) {
Metamaps.Views.exploreMaps.fetchUserThenRender(navigateTimeout)
} else {
Metamaps.Views.exploreMaps.render(navigateTimeout)
}
}
Metamaps.GlobalUI.showDiv('#explore')
Metamaps.GlobalUI.hideDiv('#yield')
Metamaps.GlobalUI.hideDiv('#infovis')
Metamaps.GlobalUI.hideDiv('#instructions')
Metamaps.Map.end()
Metamaps.Topic.end()
Metamaps.Active.Map = null
Metamaps.Active.Topic = null
},
maps: function (id) {
clearTimeout(Metamaps.Router.timeoutId)
document.title = 'Map ' + id + ' | Metamaps'
Metamaps.Router.currentSection = 'map'
Metamaps.Router.currentPage = id
$('.wrapper').removeClass('homePage explorePage topicPage')
$('.wrapper').addClass('mapPage')
// another class will be added to wrapper if you
// can edit this map '.canEditMap'
Metamaps.GlobalUI.hideDiv('#yield')
Metamaps.GlobalUI.hideDiv('#explore')
// clear the visualization, if there was one, before showing its div again
if (Metamaps.Visualize.mGraph) {
Metamaps.Visualize.mGraph.graph.empty()
Metamaps.Visualize.mGraph.plot()
Metamaps.JIT.centerMap(Metamaps.Visualize.mGraph.canvas)
}
Metamaps.GlobalUI.showDiv('#infovis')
Metamaps.Topic.end()
Metamaps.Active.Topic = null
Metamaps.Loading.show()
Metamaps.Map.end()
Metamaps.Map.launch(id)
},
topics: function (id) {
clearTimeout(Metamaps.Router.timeoutId)
document.title = 'Topic ' + id + ' | Metamaps'
Metamaps.Router.currentSection = 'topic'
Metamaps.Router.currentPage = id
$('.wrapper').removeClass('homePage explorePage mapPage')
$('.wrapper').addClass('topicPage')
Metamaps.GlobalUI.hideDiv('#yield')
Metamaps.GlobalUI.hideDiv('#explore')
// clear the visualization, if there was one, before showing its div again
if (Metamaps.Visualize.mGraph) {
Metamaps.Visualize.mGraph.graph.empty()
Metamaps.Visualize.mGraph.plot()
Metamaps.JIT.centerMap(Metamaps.Visualize.mGraph.canvas)
}
Metamaps.GlobalUI.showDiv('#infovis')
Metamaps.Map.end()
Metamaps.Active.Map = null
Metamaps.Topic.end()
Metamaps.Topic.launch(id)
}
})
Metamaps.Router = new Router()
Metamaps.Router.currentPage = ''
Metamaps.Router.currentSection = undefined
Metamaps.Router.timeoutId = undefined
Metamaps.Router.intercept = function (evt) {
var segments
var href = {
prop: $(this).prop('href'),
attr: $(this).attr('href')
}
var root = window.location.protocol + '//' + window.location.host + Backbone.history.options.root
if (href.prop && href.prop === root) href.attr = ''
if (href.prop && href.prop.slice(0, root.length) === root) {
evt.preventDefault()
segments = href.attr.split('/')
segments.splice(0, 1) // pop off the element created by the first /
if (href.attr === '') {
Metamaps.Router.home()
} else {
Metamaps.Router[segments[0]](segments[1], segments[2])
}
}
}
Metamaps.Router.init = function () {
Backbone.history.start({
silent: true,
pushState: true,
root: '/'
})
$(document).on('click', 'a[data-router="true"]', Metamaps.Router.intercept)
}
})()

View file

@ -1,87 +0,0 @@
/* global Metamaps, $ */
/*
* Metamaps.Views.js.erb
*
* Dependencies:
* - Metamaps.Loading
* - Metamaps.Active
* - Metamaps.ReactComponents
*/
Metamaps.Views = {
exploreMaps: {
setCollection: function (collection) {
var self = Metamaps.Views.exploreMaps
if (self.collection) {
self.collection.off('add', self.render)
self.collection.off('successOnFetch', self.handleSuccess)
self.collection.off('errorOnFetch', self.handleError)
}
self.collection = collection
self.collection.on('add', self.render)
self.collection.on('successOnFetch', self.handleSuccess)
self.collection.on('errorOnFetch', self.handleError)
},
render: function (mapperObj, cb) {
var self = Metamaps.Views.exploreMaps
if (typeof mapperObj === 'function') {
cb = mapperObj
mapperObj = null
}
var exploreObj = {
currentUser: Metamaps.Active.Mapper,
section: self.collection.id,
displayStyle: 'grid',
maps: self.collection,
moreToLoad: self.collection.page != 'loadedAll',
user: mapperObj,
loadMore: self.loadMore
}
ReactDOM.render(
React.createElement(Metamaps.ReactComponents.Maps, exploreObj),
document.getElementById('explore')
)
if (cb) cb()
Metamaps.Loading.hide()
},
loadMore: function () {
var self = Metamaps.Views.exploreMaps
if (self.collection.page != "loadedAll") {
self.collection.getMaps()
}
else self.render()
},
handleSuccess: function (cb) {
var self = Metamaps.Views.exploreMaps
if (self.collection && self.collection.id === 'mapper') {
self.fetchUserThenRender(cb)
} else {
self.render(cb)
}
},
handleError: function () {
console.log('error loading maps!') // TODO
},
fetchUserThenRender: function (cb) {
var self = Metamaps.Views.exploreMaps
// first load the mapper object and then call the render function
$.ajax({
url: '/users/' + self.collection.mapperId + '/details.json',
success: function (response) {
self.render(response, cb)
},
error: function () {
self.render(cb)
}
})
}
}
}

View file

@ -1,74 +0,0 @@
/* global Metamaps */
/*
* Metamaps.js.erb
*/
// TODO eliminate these 5 top-level variables
Metamaps.panningInt = null
Metamaps.tempNode = null
Metamaps.tempInit = false
Metamaps.tempNode2 = null
Metamaps.VERSION = '<%= METAMAPS_VERSION %>'
/* erb variables from rails */
Metamaps.Erb = {}
Metamaps.Erb['REALTIME_SERVER'] = '<%= ENV['REALTIME_SERVER'] %>'
Metamaps.Erb['junto_spinner_darkgrey.gif'] = '<%= asset_path('junto_spinner_darkgrey.gif') %>'
Metamaps.Erb['user.png'] = '<%= asset_path('user.png') %>'
Metamaps.Erb['icons/wildcard.png'] = '<%= asset_path('icons/wildcard.png') %>'
Metamaps.Erb['topic_description_signifier.png'] = '<%= asset_path('topic_description_signifier.png') %>'
Metamaps.Erb['topic_link_signifier.png'] = '<%= asset_path('topic_link_signifier.png') %>'
Metamaps.Erb['synapse16.png'] = '<%= asset_path('synapse16.png') %>'
Metamaps.Metacodes = <%= Metacode.all.to_json.gsub(%r[(icon.*?)(\"},)], '\1?purple=stupid\2').html_safe %>
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: []
}

View file

@ -1,343 +0,0 @@
Metamaps.Views = Metamaps.Views || {};
Metamaps.Views.chatView = (function () {
var
chatView,
linker = new Autolinker({ newWindow: true, truncate: 50, email: false, phone: false, twitter: false });
var Private = {
messageHTML: "<div class='chat-message'>" +
"<div class='chat-message-user'><img src='{{ user_image }}' title='{{user_name }}'/></div>" +
"<div class='chat-message-text'>{{ message }}</div>" +
"<div class='chat-message-time'>{{ timestamp }}</div>" +
"<div class='clearfloat'></div>" +
"</div>",
participantHTML: "<div class='participant participant-{{ id }} {{ selfClass }}'>" +
"<div class='chat-participant-image'><img src='{{ image }}' style='border: 2px solid {{ color }};' /></div>" +
"<div class='chat-participant-name'>{{ username }} {{ selfName }}</div>" +
"<button type='button' class='button chat-participant-invite-call' onclick='Metamaps.Realtime.inviteACall({{ id}});'></button>" +
"<button type='button' class='button chat-participant-invite-join' onclick='Metamaps.Realtime.inviteToJoin({{ id}});'></button>" +
"<span class='chat-participant-participating'><div class='green-dot'></div></span>" +
"<div class='clearfloat'></div>" +
"</div>",
templates: function() {
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g
};
this.messageTemplate = _.template(Private.messageHTML);
this.participantTemplate = _.template(Private.participantHTML);
},
createElements: function() {
this.$unread = $('<div class="chat-unread"></div>');
this.$button = $('<div class="chat-button"><div class="tooltips">Chat</div></div>');
this.$messageInput = $('<textarea placeholder="Send a message..." class="chat-input"></textarea>');
this.$juntoHeader = $('<div class="junto-header">PARTICIPANTS</div>');
this.$videoToggle = $('<div class="video-toggle"></div>');
this.$cursorToggle = $('<div class="cursor-toggle"></div>');
this.$participants = $('<div class="participants"></div>');
this.$conversationInProgress = $('<div class="conversation-live">LIVE <span class="call-action leave" onclick="Metamaps.Realtime.leaveCall();">LEAVE</span><span class="call-action join" onclick="Metamaps.Realtime.joinCall();">JOIN</span></div>');
this.$chatHeader = $('<div class="chat-header">CHAT</div>');
this.$soundToggle = $('<div class="sound-toggle"></div>');
this.$messages = $('<div class="chat-messages"></div>');
this.$container = $('<div class="chat-box"></div>');
},
attachElements: function() {
this.$button.append(this.$unread);
this.$juntoHeader.append(this.$videoToggle);
this.$juntoHeader.append(this.$cursorToggle);
this.$chatHeader.append(this.$soundToggle);
this.$participants.append(this.$conversationInProgress);
this.$container.append(this.$juntoHeader);
this.$container.append(this.$participants);
this.$container.append(this.$chatHeader);
this.$container.append(this.$button);
this.$container.append(this.$messages);
this.$container.append(this.$messageInput);
},
addEventListeners: function() {
var self = this;
this.participants.on('add', function (participant) {
Private.addParticipant.call(self, participant);
});
this.participants.on('remove', function (participant) {
Private.removeParticipant.call(self, participant);
});
this.$button.on('click', function () {
Handlers.buttonClick.call(self);
});
this.$videoToggle.on('click', function () {
Handlers.videoToggleClick.call(self);
});
this.$cursorToggle.on('click', function () {
Handlers.cursorToggleClick.call(self);
});
this.$soundToggle.on('click', function () {
Handlers.soundToggleClick.call(self);
});
this.$messageInput.on('keyup', function (event) {
Handlers.keyUp.call(self, event);
});
this.$messageInput.on('focus', function () {
Handlers.inputFocus.call(self);
});
this.$messageInput.on('blur', function () {
Handlers.inputBlur.call(self);
});
},
initializeSounds: function() {
this.sound = new Howl({
urls: ["<%= asset_path 'sounds/MM_sounds.mp3' %>", "<%= asset_path 'sounds/MM_sounds.ogg' %>"],
sprite: {
joinmap: [0, 561],
leavemap: [1000, 592],
receivechat: [2000, 318],
sendchat: [3000, 296],
sessioninvite: [4000, 5393, true]
}
});
},
incrementUnread: function() {
this.unreadMessages++;
this.$unread.html(this.unreadMessages);
this.$unread.show();
},
addMessage: function(message, isInitial, wasMe) {
if (!this.isOpen && !isInitial) Private.incrementUnread.call(this);
function addZero(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
var m = _.clone(message.attributes);
var today = new Date();
m.timestamp = new Date(m.created_at);
var date = (m.timestamp.getMonth() + 1) + '/' + m.timestamp.getDate();
date += " " + addZero(m.timestamp.getHours()) + ":" + addZero(m.timestamp.getMinutes());
m.timestamp = date;
m.image = m.user_image || 'http://www.hotpepper.ca/wp-content/uploads/2014/11/default_profile_1_200x200.png'; // TODO: remove
m.message = linker.link(m.message);
var $html = $(this.messageTemplate(m));
this.$messages.append($html);
if (!isInitial) this.scrollMessages(200);
if (!wasMe && !isInitial && this.alertSound) this.sound.play('receivechat');
},
initialMessages: function() {
var messages = this.messages.models;
for (var i = 0; i < messages.length; i++) {
Private.addMessage.call(this, messages[i], true);
}
},
handleInputMessage: function() {
var message = {
message: this.$messageInput.val(),
};
this.$messageInput.val('');
$(document).trigger(chatView.events.message + '-' + this.room, [message]);
},
addParticipant: function(participant) {
var p = _.clone(participant.attributes);
if (p.self) {
p.selfClass = 'is-self';
p.selfName = '(me)';
} else {
p.selfClass = '';
p.selfName = '';
}
var html = this.participantTemplate(p);
this.$participants.append(html);
},
removeParticipant: function(participant) {
this.$container.find('.participant-' + participant.get('id')).remove();
}
};
var Handlers = {
buttonClick: function() {
if (this.isOpen) this.close();
else if (!this.isOpen) this.open();
},
videoToggleClick: function() {
this.$videoToggle.toggleClass('active');
this.videosShowing = !this.videosShowing;
$(document).trigger(this.videosShowing ? chatView.events.videosOn : chatView.events.videosOff);
},
cursorToggleClick: function() {
this.$cursorToggle.toggleClass('active');
this.cursorsShowing = !this.cursorsShowing;
$(document).trigger(this.cursorsShowing ? chatView.events.cursorsOn : chatView.events.cursorsOff);
},
soundToggleClick: function() {
this.alertSound = !this.alertSound;
this.$soundToggle.toggleClass('active');
},
keyUp: function(event) {
switch(event.which) {
case 13: // enter
Private.handleInputMessage.call(this);
break;
}
},
inputFocus: function() {
$(document).trigger(chatView.events.inputFocus);
},
inputBlur: function() {
$(document).trigger(chatView.events.inputBlur);
}
};
chatView = function(messages, mapper, room) {
var self = this;
this.room = room;
this.mapper = mapper;
this.messages = messages; // backbone collection
this.isOpen = false;
this.alertSound = true; // whether to play sounds on arrival of new messages or not
this.cursorsShowing = true;
this.videosShowing = true;
this.unreadMessages = 0;
this.participants = new Backbone.Collection();
Private.templates.call(this);
Private.createElements.call(this);
Private.attachElements.call(this);
Private.addEventListeners.call(this);
Private.initialMessages.call(this);
Private.initializeSounds.call(this);
this.$container.css({
right: '-300px'
});
};
chatView.prototype.conversationInProgress = function (participating) {
this.$conversationInProgress.show();
this.$participants.addClass('is-live');
if (participating) this.$participants.addClass('is-participating');
this.$button.addClass('active');
// hide invite to call buttons
}
chatView.prototype.conversationEnded = function () {
this.$conversationInProgress.hide();
this.$participants.removeClass('is-live');
this.$participants.removeClass('is-participating');
this.$button.removeClass('active');
this.$participants.find('.participant').removeClass('active');
this.$participants.find('.participant').removeClass('pending');
}
chatView.prototype.leaveConversation = function () {
this.$participants.removeClass('is-participating');
}
chatView.prototype.mapperJoinedCall = function (id) {
this.$participants.find('.participant-' + id).addClass('active');
}
chatView.prototype.mapperLeftCall = function (id) {
this.$participants.find('.participant-' + id).removeClass('active');
}
chatView.prototype.invitationPending = function (id) {
this.$participants.find('.participant-' + id).addClass('pending');
}
chatView.prototype.invitationAnswered = function (id) {
this.$participants.find('.participant-' + id).removeClass('pending');
}
chatView.prototype.addParticipant = function (participant) {
this.participants.add(participant);
}
chatView.prototype.removeParticipant = function (username) {
var p = this.participants.find(function (p) { return p.get('username') === username; });
if (p) {
this.participants.remove(p);
}
}
chatView.prototype.removeParticipants = function () {
this.participants.remove(this.participants.models);
}
chatView.prototype.open = function () {
this.$container.css({
right: '0'
});
this.$messageInput.focus();
this.isOpen = true;
this.unreadMessages = 0;
this.$unread.hide();
this.scrollMessages(0);
$(document).trigger(chatView.events.openTray);
}
chatView.prototype.addMessage = function(message, isInitial, wasMe) {
this.messages.add(message);
Private.addMessage.call(this, message, isInitial, wasMe);
}
chatView.prototype.scrollMessages = function(duration) {
duration = duration || 0;
this.$messages.animate({
scrollTop: this.$messages[0].scrollHeight
}, duration);
}
chatView.prototype.clearMessages = function () {
this.unreadMessages = 0;
this.$unread.hide();
this.$messages.empty();
}
chatView.prototype.close = function () {
this.$container.css({
right: '-300px'
});
this.$messageInput.blur();
this.isOpen = false;
$(document).trigger(chatView.events.closeTray);
}
chatView.prototype.remove = function () {
this.$button.off();
this.$container.remove();
}
/**
* @class
* @static
*/
chatView.events = {
message: 'ChatView:message',
openTray: 'ChatView:openTray',
closeTray: 'ChatView:closeTray',
inputFocus: 'ChatView:inputFocus',
inputBlur: 'ChatView:inputBlur',
cursorsOff: 'ChatView:cursorsOff',
cursorsOn: 'ChatView:cursorsOn',
videosOff: 'ChatView:videosOff',
videosOn: 'ChatView:videosOn'
};
return chatView;
})();

View file

@ -1,195 +0,0 @@
Metamaps.Views = Metamaps.Views || {};
Metamaps.Views.room = (function () {
var ChatView = Metamaps.Views.chatView;
var VideoView = Metamaps.Views.videoView;
var room = function(opts) {
var self = this;
this.isActiveRoom = false;
this.socket = opts.socket;
this.webrtc = opts.webrtc;
//this.roomRef = opts.firebase;
this.room = opts.room;
this.config = opts.config;
this.peopleCount = 0;
this.$myVideo = opts.$video;
this.myVideo = opts.myVideoView;
this.messages = new Backbone.Collection();
this.currentMapper = new Backbone.Model({ name: opts.username, image: opts.image });
this.chat = new ChatView(this.messages, this.currentMapper, this.room);
this.videos = {};
this.init();
};
room.prototype.join = function(cb) {
this.isActiveRoom = true;
this.webrtc.joinRoom(this.room, cb);
this.chat.conversationInProgress(true); // true indicates participation
}
room.prototype.conversationInProgress = function() {
this.chat.conversationInProgress(false); // false indicates not participating
}
room.prototype.conversationEnding = function() {
this.chat.conversationEnded();
}
room.prototype.leaveVideoOnly = function() {
this.chat.leaveConversation(); // the conversation will carry on without you
for (var id in this.videos) {
this.removeVideo(id);
}
this.isActiveRoom = false;
this.webrtc.leaveRoom();
}
room.prototype.leave = function() {
for (var id in this.videos) {
this.removeVideo(id);
}
this.isActiveRoom = false;
this.webrtc.leaveRoom();
this.chat.conversationEnded();
this.chat.removeParticipants();
this.chat.clearMessages();
this.messages.reset();
}
room.prototype.setPeopleCount = function(count) {
this.peopleCount = count;
}
room.prototype.init = function () {
var self = this;
$(document).on(VideoView.events.audioControlClick, function (event, videoView) {
if (!videoView.audioStatus) self.webrtc.mute();
else if (videoView.audioStatus) self.webrtc.unmute();
});
$(document).on(VideoView.events.videoControlClick, function (event, videoView) {
if (!videoView.videoStatus) self.webrtc.pauseVideo();
else if (videoView.videoStatus) self.webrtc.resumeVideo();
});
this.webrtc.webrtc.off('peerStreamAdded');
this.webrtc.webrtc.off('peerStreamRemoved');
this.webrtc.on('peerStreamAdded', function (peer) {
var mapper = Metamaps.Realtime.mappersOnMap[peer.nick];
peer.avatar = mapper.image;
peer.username = mapper.name;
if (self.isActiveRoom) {
self.addVideo(peer);
}
});
this.webrtc.on('peerStreamRemoved', function (peer) {
if (self.isActiveRoom) {
self.removeVideo(peer);
}
});
this.webrtc.on('mute', function (data) {
var v = self.videos[data.id];
if (!v) return;
if (data.name === 'audio') {
v.audioStatus = false;
}
else if (data.name === 'video') {
v.videoStatus = false;
v.$avatar.show();
}
if (!v.audioStatus && !v.videoStatus) v.$container.hide();
});
this.webrtc.on('unmute', function (data) {
var v = self.videos[data.id];
if (!v) return;
if (data.name === 'audio') {
v.audioStatus = true;
}
else if (data.name === 'video') {
v.videoStatus = true;
v.$avatar.hide();
}
v.$container.show();
});
var sendChatMessage = function (event, data) {
self.sendChatMessage(data);
};
$(document).on(ChatView.events.message + '-' + this.room, sendChatMessage);
}
room.prototype.videoAdded = function (callback) {
this._videoAdded = callback;
}
room.prototype.addVideo = function (peer) {
var
id = this.webrtc.getDomId(peer),
video = attachMediaStream(peer.stream);
var
v = new VideoView(video, null, id, false, { DOUBLE_CLICK_TOLERANCE: 200, avatar: peer.avatar, username: peer.username });
this.videos[peer.id] = v;
if (this._videoAdded) this._videoAdded(v, peer.nick);
}
room.prototype.removeVideo = function (peer) {
var id = typeof peer == 'string' ? peer : peer.id;
if (this.videos[id]) {
this.videos[id].remove();
delete this.videos[id];
}
}
room.prototype.sendChatMessage = function (data) {
var self = this;
//this.roomRef.child('messages').push(data);
if (self.chat.alertSound) self.chat.sound.play('sendchat');
var m = new Metamaps.Backbone.Message({
message: data.message,
resource_id: Metamaps.Active.Map.id,
resource_type: "Map"
});
m.save(null, {
success: function (model, response) {
self.addMessages(new Metamaps.Backbone.MessageCollection(model), false, true);
$(document).trigger(room.events.newMessage, [model]);
},
error: function (model, response) {
console.log('error!', response);
}
});
}
// they should be instantiated as backbone models before they get
// passed to this function
room.prototype.addMessages = function (messages, isInitial, wasMe) {
var self = this;
messages.models.forEach(function (message) {
self.chat.addMessage(message, isInitial, wasMe);
});
}
/**
* @class
* @static
*/
room.events = {
newMessage: "Room:newMessage"
};
return room;
})();

View file

@ -1,207 +0,0 @@
Metamaps.Views = Metamaps.Views || {};
Metamaps.Views.videoView = (function () {
var videoView;
var Private = {
addControls: function() {
var self = this;
this.$audioControl = $('<div class="video-audio"></div>');
this.$videoControl = $('<div class="video-video"></div>');
this.$audioControl.on('click', function () {
Handlers.audioControlClick.call(self);
});
this.$videoControl.on('click', function () {
Handlers.videoControlClick.call(self);
});
this.$container.append(this.$audioControl);
this.$container.append(this.$videoControl);
},
cancelClick: function() {
this.mouseIsDown = false;
if (this.hasMoved) {
}
$(document).trigger(videoView.events.dragEnd);
}
};
var Handlers = {
mousedown: function(event) {
this.mouseIsDown = true;
this.hasMoved = false;
this.mouseMoveStart = {
x: event.pageX,
y: event.pageY
};
this.posStart = {
x: parseInt(this.$container.css('left'), '10'),
y: parseInt(this.$container.css('top'), '10')
}
$(document).trigger(videoView.events.mousedown);
},
mouseup: function(event) {
$(document).trigger(videoView.events.mouseup, [this]);
var storedTime = this.lastClick;
var now = Date.now();
this.lastClick = now;
if (now - storedTime < this.config.DOUBLE_CLICK_TOLERANCE) {
$(document).trigger(videoView.events.doubleClick, [this]);
}
},
mousemove: function(event) {
var
diffX,
diffY,
newX,
newY;
if (this.$parent && this.mouseIsDown) {
this.manuallyPositioned = true;
this.hasMoved = true;
diffX = event.pageX - this.mouseMoveStart.x;
diffY = this.mouseMoveStart.y - event.pageY;
newX = this.posStart.x + diffX;
newY = this.posStart.y - diffY;
this.$container.css({
top: newY,
left: newX
});
}
},
audioControlClick: function() {
if (this.audioStatus) {
this.audioOff();
} else {
this.audioOn();
}
$(document).trigger(videoView.events.audioControlClick, [this]);
},
videoControlClick: function() {
if (this.videoStatus) {
this.videoOff();
} else {
this.videoOn();
}
$(document).trigger(videoView.events.videoControlClick, [this]);
},
};
var videoView = function(video, $parent, id, isMyself, config) {
var self = this;
this.$parent = $parent; // mapView
this.video = video;
this.id = id;
this.config = config;
this.mouseIsDown = false;
this.mouseDownOffset = { x: 0, y: 0 };
this.lastClick = null;
this.hasMoved = false;
this.audioStatus = true;
this.videoStatus = true;
this.$container = $('<div></div>');
this.$container.addClass('collaborator-video' + (isMyself ? ' my-video' : ''));
this.$container.attr('id', 'container_' + id);
var $vidContainer = $('<div></div>');
$vidContainer.addClass('video-cutoff');
$vidContainer.append(this.video);
this.avatar = config.avatar;
this.$avatar = $('<img draggable="false" class="collaborator-video-avatar" src="' + config.avatar + '" width="150" height="150" />');
$vidContainer.append(this.$avatar);
this.$container.append($vidContainer);
this.$container.on('mousedown', function (event) {
Handlers.mousedown.call(self, event);
});
if (isMyself) {
Private.addControls.call(this);
}
// suppress contextmenu
this.video.oncontextmenu = function () { return false; };
if (this.$parent) this.setParent(this.$parent);
};
videoView.prototype.setParent = function($parent) {
var self = this;
this.$parent = $parent;
this.$parent.off('.video' + this.id);
this.$parent.on('mouseup.video' + this.id, function (event) {
Handlers.mouseup.call(self, event);
Private.cancelClick.call(self);
});
this.$parent.on('mousemove.video' + this.id, function (event) {
Handlers.mousemove.call(self, event);
});
}
videoView.prototype.setAvatar = function (src) {
this.$avatar.attr('src', src);
this.avatar = src;
}
videoView.prototype.remove = function () {
this.$container.off();
if (this.$parent) this.$parent.off('.video' + this.id);
this.$container.remove();
}
videoView.prototype.videoOff = function () {
this.$videoControl.addClass('active');
this.$avatar.show();
this.videoStatus = false;
}
videoView.prototype.videoOn = function () {
this.$videoControl.removeClass('active');
this.$avatar.hide();
this.videoStatus = true;
}
videoView.prototype.audioOff = function () {
this.$audioControl.addClass('active');
this.audioStatus = false;
}
videoView.prototype.audioOn = function () {
this.$audioControl.removeClass('active');
this.audioStatus = true;
}
/**
* @class
* @static
*/
videoView.events = {
mousedown: "VideoView:mousedown",
mouseup: "VideoView:mouseup",
doubleClick: "VideoView:doubleClick",
dragEnd: "VideoView:dragEnd",
audioControlClick: "VideoView:audioControlClick",
videoControlClick: "VideoView:videoControlClick",
};
return videoView;
})();

View file

@ -87,8 +87,6 @@ server to see what problems show up:
sudo npm install -g forever
(crontab -u metamaps -l 2>/dev/null; echo "@reboot $(which forever) --append -l /home/metamaps/logs/forever.realtime.log start /home/metamaps/metamaps/realtime/realtime-server.js") | crontab -u metamaps -
cd /home/metamaps/metamaps/realtime
npm install
mkdir -p /home/metamaps/logs
forever --append -l /home/metamaps/logs/forever.realtime.log \
start /home/metamaps/metamaps/realtime/realtime-server.js

View file

@ -29,9 +29,7 @@ Now that you have the code, run these commands:
rake perms:fix
passenger-config restart-app .
cd realtime
npm install
forever list #find the uid, e.g. xQKv
forever list #find the uid of the realtime server, e.g. xQKv
forever restart xQKv
sudo service metamaps_delayed_job restart

View file

@ -1,42 +1,34 @@
/* global Metamaps, $ */
/*
* Metamaps.Account.js.erb
*
* Dependencies:
* - Metamaps.Erb
/*
* Metamaps.Erb
*/
Metamaps.Account = {
const Account = {
listenersInitialized: false,
init: function () {
var self = Metamaps.Account
},
initListeners: function () {
var self = Metamaps.Account
var self = Account
$('#user_image').change(self.showImagePreview)
self.listenersInitialized = true
},
toggleChangePicture: function () {
var self = Metamaps.Account
var self = Account
$('.userImageMenu').toggle()
if (!self.listenersInitialized) self.initListeners()
},
openChangePicture: function () {
var self = Metamaps.Account
var self = Account
$('.userImageMenu').show()
if (!self.listenersInitialized) self.initListeners()
},
closeChangePicture: function () {
var self = Metamaps.Account
var self = Account
$('.userImageMenu').hide()
},
showLoading: function () {
var self = Metamaps.Account
var self = Account
var loader = new CanvasLoader('accountPageLoading')
loader.setColor('#4FC059'); // default is '#000000'
@ -47,7 +39,7 @@ Metamaps.Account = {
$('#accountPageLoading').show()
},
showImagePreview: function () {
var self = Metamaps.Account
var self = Account
var file = $('#user_image')[0].files[0]
@ -93,10 +85,10 @@ Metamaps.Account = {
}
},
removePicture: function () {
var self = Metamaps.Account
var self = Account
$('.userImageDiv canvas').remove()
$('.userImageDiv img').attr('src', Metamaps.Erb['user.png']).show()
$('.userImageDiv img').attr('src', window.Metamaps.Erb['user.png']).show()
$('.userImageMenu').hide()
var input = $('#user_image')
@ -120,3 +112,5 @@ Metamaps.Account = {
$('#user_password_confirmation').val('')
}
}
export default Account

View file

@ -0,0 +1,7 @@
const Active = {
Map: null,
Topic: null,
Mapper: null
};
export default Active

View file

@ -1,35 +1,29 @@
/* global Metamaps, $ */
/* global $ */
/*
* Metamaps.Admin.js.erb
*
* Dependencies: none!
*/
Metamaps.Admin = {
const Admin = {
selectMetacodes: [],
allMetacodes: [],
init: function () {
var self = Metamaps.Admin
var self = Admin
$('#metacodes_value').val(self.selectMetacodes.toString())
},
selectAll: function () {
var self = Metamaps.Admin
var self = Admin
$('.editMetacodes li').removeClass('toggledOff')
self.selectMetacodes = self.allMetacodes.slice(0)
$('#metacodes_value').val(self.selectMetacodes.toString())
},
deselectAll: function () {
var self = Metamaps.Admin
var self = Admin
$('.editMetacodes li').addClass('toggledOff')
self.selectMetacodes = []
$('#metacodes_value').val(0)
},
liClickHandler: function () {
var self = Metamaps.Admin
var self = Admin
if ($(this).attr('class') != 'toggledOff') {
$(this).addClass('toggledOff')
@ -44,7 +38,7 @@ Metamaps.Admin = {
}
},
validate: function () {
var self = Metamaps.Admin
var self = Admin
if (self.selectMetacodes.length == 0) {
alert('Would you pretty please select at least one metacode for the set?')
@ -52,3 +46,5 @@ Metamaps.Admin = {
}
}
}
export default Admin

View file

@ -1,12 +1,4 @@
/* global Metamaps */
/*
* Metmaaps.AutoLayout.js
*
* Dependencies: none!
*/
Metamaps.AutoLayout = {
const AutoLayout = {
nextX: 0,
nextY: 0,
sideLength: 1,
@ -16,7 +8,7 @@ Metamaps.AutoLayout = {
timeToTurn: 0,
getNextCoord: function () {
var self = Metamaps.AutoLayout
var self = AutoLayout
var nextX = self.nextX
var nextY = self.nextY
@ -63,7 +55,7 @@ Metamaps.AutoLayout = {
}
},
resetSpiral: function () {
var self = Metamaps.AutoLayout
var self = AutoLayout
self.nextX = 0
self.nextY = 0
self.nextXshift = 1
@ -73,3 +65,5 @@ Metamaps.AutoLayout = {
self.turnCount = 0
}
}
export default AutoLayout

View file

@ -1,33 +1,38 @@
/* global Metamaps, Backbone, _, $ */
/* global Metamaps, Backbone, $ */
import _ from 'lodash'
import Backbone from 'backbone'
Backbone.$ = window.$
import Active from '../Active'
import Filter from '../Filter'
import JIT from '../JIT'
import Map, { InfoBox } from '../Map'
import Mapper from '../Mapper'
import Realtime from '../Realtime'
import Synapse from '../Synapse'
import SynapseCard from '../SynapseCard'
import Topic from '../Topic'
import TopicCard from '../TopicCard'
import Visualize from '../Visualize'
/*
* Metamaps.Backbone.js.erb
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.Collaborators
* - Metamaps.Creators
* - Metamaps.Filter
* - Metamaps.JIT
* - Metamaps.Loading
* - Metamaps.Map
* - Metamaps.Mapper
* - Metamaps.Mappers
* - Metamaps.Mappings
* - Metamaps.Metacodes
* - Metamaps.Realtime
* - Metamaps.Synapse
* - Metamaps.SynapseCard
* - Metamaps.Synapses
* - Metamaps.Topic
* - Metamaps.TopicCard
* - Metamaps.Topics
* - Metamaps.Visualize
*/
Metamaps.Backbone = {}
const _Backbone = {}
Metamaps.Backbone.Map = Backbone.Model.extend({
_Backbone.Map = Backbone.Model.extend({
urlRoot: '/maps',
blacklist: ['created_at', 'updated_at', 'created_at_clean', 'updated_at_clean', 'user_name', 'contributor_count', 'topic_count', 'synapse_count', 'topics', 'synapses', 'mappings', 'mappers'],
toJSON: function (options) {
@ -58,7 +63,7 @@ Metamaps.Backbone.Map = Backbone.Model.extend({
this.on('saved', this.savedEvent)
},
savedEvent: function () {
Metamaps.Realtime.sendMapChange(this)
Realtime.sendMapChange(this)
},
authorizeToEdit: function (mapper) {
if (mapper && (
@ -78,10 +83,10 @@ Metamaps.Backbone.Map = Backbone.Model.extend({
}
},
getUser: function () {
return Metamaps.Mapper.get(this.get('user_id'))
return Mapper.get(this.get('user_id'))
},
fetchContained: function () {
var bb = Metamaps.Backbone
var bb = _Backbone
var that = this
var start = function (data) {
that.set('mappers', new bb.MapperCollection(data.mappers))
@ -122,10 +127,10 @@ Metamaps.Backbone.Map = Backbone.Model.extend({
return this.get('mappers')
},
updateView: function () {
var map = Metamaps.Active.Map
var map = Active.Map
var isActiveMap = this.id === map.id
if (isActiveMap) {
Metamaps.Map.InfoBox.updateNameDescPerm(this.get('name'), this.get('desc'), this.get('permission'))
InfoBox.updateNameDescPerm(this.get('name'), this.get('desc'), this.get('permission'))
this.updateMapWrapper()
// mobile menu
$('#header_content').html(this.get('name'))
@ -133,17 +138,17 @@ Metamaps.Backbone.Map = Backbone.Model.extend({
}
},
updateMapWrapper: function () {
var map = Metamaps.Active.Map
var map = Active.Map
var isActiveMap = this.id === map.id
var authorized = map && map.authorizeToEdit(Metamaps.Active.Mapper) ? 'canEditMap' : ''
var authorized = map && map.authorizeToEdit(Active.Mapper) ? 'canEditMap' : ''
var commonsMap = map && map.get('permission') === 'commons' ? 'commonsMap' : ''
if (isActiveMap) {
$('.wrapper').removeClass('canEditMap commonsMap').addClass(authorized + ' ' + commonsMap)
}
}
})
Metamaps.Backbone.MapsCollection = Backbone.Collection.extend({
model: Metamaps.Backbone.Map,
_Backbone.MapsCollection = Backbone.Collection.extend({
model: _Backbone.Map,
initialize: function (models, options) {
this.id = options.id
this.sortBy = options.sortBy
@ -210,7 +215,7 @@ Metamaps.Backbone.MapsCollection = Backbone.Collection.extend({
}
})
Metamaps.Backbone.Message = Backbone.Model.extend({
_Backbone.Message = Backbone.Model.extend({
urlRoot: '/messages',
blacklist: ['created_at', 'updated_at'],
toJSON: function (options) {
@ -226,12 +231,12 @@ Metamaps.Backbone.Message = Backbone.Model.extend({
*/
}
})
Metamaps.Backbone.MessageCollection = Backbone.Collection.extend({
model: Metamaps.Backbone.Message,
_Backbone.MessageCollection = Backbone.Collection.extend({
model: _Backbone.Message,
url: '/messages'
})
Metamaps.Backbone.Mapper = Backbone.Model.extend({
_Backbone.Mapper = Backbone.Model.extend({
urlRoot: '/users',
blacklist: ['created_at', 'updated_at'],
toJSON: function (options) {
@ -247,13 +252,13 @@ Metamaps.Backbone.Mapper = Backbone.Model.extend({
}
})
Metamaps.Backbone.MapperCollection = Backbone.Collection.extend({
model: Metamaps.Backbone.Mapper,
_Backbone.MapperCollection = Backbone.Collection.extend({
model: _Backbone.Mapper,
url: '/users'
})
Metamaps.Backbone.init = function () {
var self = Metamaps.Backbone
_Backbone.init = function () {
var self = _Backbone
self.Metacode = Backbone.Model.extend({
initialize: function () {
@ -320,10 +325,10 @@ Metamaps.Backbone.init = function () {
initialize: function () {
if (this.isNew()) {
this.set({
'user_id': Metamaps.Active.Mapper.id,
'user_id': Active.Mapper.id,
'desc': this.get('desc') || '',
'link': this.get('link') || '',
'permission': Metamaps.Active.Map ? Metamaps.Active.Map.get('permission') : 'commons'
'permission': Active.Map ? Active.Map.get('permission') : 'commons'
})
}
@ -335,7 +340,7 @@ Metamaps.Backbone.init = function () {
mappableid: this.id
}
$(document).trigger(Metamaps.JIT.events.removeTopic, [removeTopicData])
$(document).trigger(JIT.events.removeTopic, [removeTopicData])
})
this.on('noLongerPrivate', function () {
var newTopicData = {
@ -343,10 +348,10 @@ Metamaps.Backbone.init = function () {
mappableid: this.id
}
$(document).trigger(Metamaps.JIT.events.newTopic, [newTopicData])
$(document).trigger(JIT.events.newTopic, [newTopicData])
})
this.on('change:metacode_id', Metamaps.Filter.checkMetacodes, this)
this.on('change:metacode_id', Filter.checkMetacodes, this)
},
authorizeToEdit: function (mapper) {
if (mapper &&
@ -367,10 +372,10 @@ Metamaps.Backbone.init = function () {
return Metamaps.Metacodes.get(this.get('metacode_id'))
},
getMapping: function () {
if (!Metamaps.Active.Map) return false
if (!Active.Map) return false
return Metamaps.Mappings.findWhere({
map_id: Metamaps.Active.Map.id,
map_id: Active.Map.id,
mappable_type: 'Topic',
mappable_id: this.isNew() ? this.cid : this.id
})
@ -383,7 +388,7 @@ Metamaps.Backbone.init = function () {
name: this.get('name')
}
if (Metamaps.Active.Map) {
if (Active.Map) {
mapping = this.getMapping()
node.data = {
$mapping: null,
@ -398,7 +403,7 @@ Metamaps.Backbone.init = function () {
var node = this.get('node')
node.setData('topic', this)
if (Metamaps.Active.Map) {
if (Active.Map) {
mapping = this.getMapping()
node.setData('mapping', mapping)
}
@ -406,38 +411,38 @@ Metamaps.Backbone.init = function () {
return node
},
savedEvent: function () {
Metamaps.Realtime.sendTopicChange(this)
Realtime.sendTopicChange(this)
},
updateViews: function () {
var onPageWithTopicCard = Metamaps.Active.Map || Metamaps.Active.Topic
var onPageWithTopicCard = Active.Map || 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)
if (onPageWithTopicCard && this == TopicCard.openTopicCard) {
TopicCard.showCard(node)
}
// update the node on the map
if (onPageWithTopicCard && node) {
node.name = this.get('name')
Metamaps.Visualize.mGraph.plot()
Visualize.mGraph.plot()
}
},
updateCardView: function () {
var onPageWithTopicCard = Metamaps.Active.Map || Metamaps.Active.Topic
var onPageWithTopicCard = Active.Map || 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)
if (onPageWithTopicCard && this == TopicCard.openTopicCard) {
TopicCard.showCard(node)
}
},
updateNodeView: function () {
var onPageWithTopicCard = Metamaps.Active.Map || Metamaps.Active.Topic
var onPageWithTopicCard = Active.Map || 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()
Visualize.mGraph.plot()
}
}
})
@ -485,8 +490,8 @@ Metamaps.Backbone.init = function () {
initialize: function () {
if (this.isNew()) {
this.set({
'user_id': Metamaps.Active.Mapper.id,
'permission': Metamaps.Active.Map ? Metamaps.Active.Map.get('permission') : 'commons',
'user_id': Active.Mapper.id,
'permission': Active.Map ? Active.Map.get('permission') : 'commons',
'category': 'from-to'
})
}
@ -500,15 +505,15 @@ Metamaps.Backbone.init = function () {
mappableid: this.id
}
$(document).trigger(Metamaps.JIT.events.newSynapse, [newSynapseData])
$(document).trigger(JIT.events.newSynapse, [newSynapseData])
})
this.on('nowPrivate', function () {
$(document).trigger(Metamaps.JIT.events.removeSynapse, [{
$(document).trigger(JIT.events.removeSynapse, [{
mappableid: this.id
}])
})
this.on('change:desc', Metamaps.Filter.checkSynapses, this)
this.on('change:desc', Filter.checkSynapses, this)
},
prepareLiForFilter: function () {
var li = ''
@ -542,10 +547,10 @@ Metamaps.Backbone.init = function () {
] : false
},
getMapping: function () {
if (!Metamaps.Active.Map) return false
if (!Active.Map) return false
return Metamaps.Mappings.findWhere({
map_id: Metamaps.Active.Map.id,
map_id: Active.Map.id,
mappable_type: 'Synapse',
mappable_id: this.isNew() ? this.cid : this.id
})
@ -563,7 +568,7 @@ Metamaps.Backbone.init = function () {
}
}
if (Metamaps.Active.Map) {
if (Active.Map) {
mapping = providedMapping || this.getMapping()
mappingID = mapping.isNew() ? mapping.cid : mapping.id
edge.data.$mappings = []
@ -577,7 +582,7 @@ Metamaps.Backbone.init = function () {
var edge = this.get('edge')
edge.getData('synapses').push(this)
if (Metamaps.Active.Map) {
if (Active.Map) {
mapping = this.getMapping()
edge.getData('mappings').push(mapping)
}
@ -585,28 +590,28 @@ Metamaps.Backbone.init = function () {
return edge
},
savedEvent: function () {
Metamaps.Realtime.sendSynapseChange(this)
Realtime.sendSynapseChange(this)
},
updateViews: function () {
this.updateCardView()
this.updateEdgeView()
},
updateCardView: function () {
var onPageWithSynapseCard = Metamaps.Active.Map || Metamaps.Active.Topic
var onPageWithSynapseCard = Active.Map || 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)
if (onPageWithSynapseCard && edge == SynapseCard.openSynapseCard) {
SynapseCard.showCard(edge)
}
},
updateEdgeView: function () {
var onPageWithSynapseCard = Metamaps.Active.Map || Metamaps.Active.Topic
var onPageWithSynapseCard = Active.Map || Active.Topic
var edge = this.get('edge')
// update the edge on the map
if (onPageWithSynapseCard && edge) {
Metamaps.Visualize.mGraph.plot()
Visualize.mGraph.plot()
}
}
})
@ -625,20 +630,20 @@ Metamaps.Backbone.init = function () {
initialize: function () {
if (this.isNew()) {
this.set({
'user_id': Metamaps.Active.Mapper.id,
'map_id': Metamaps.Active.Map ? Metamaps.Active.Map.id : null
'user_id': Active.Mapper.id,
'map_id': Active.Map ? Active.Map.id : null
})
}
},
getMap: function () {
return Metamaps.Map.get(this.get('map_id'))
return Map.get(this.get('map_id'))
},
getTopic: function () {
if (this.get('mappable_type') === 'Topic') return Metamaps.Topic.get(this.get('mappable_id'))
if (this.get('mappable_type') === 'Topic') return 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'))
if (this.get('mappable_type') === 'Synapse') return Synapse.get(this.get('mappable_id'))
else return false
}
})
@ -661,36 +666,38 @@ Metamaps.Backbone.init = function () {
// this is for topic view
Metamaps.Creators = Metamaps.Creators ? new self.MapperCollection(Metamaps.Creators) : new self.MapperCollection()
if (Metamaps.Active.Map) {
if (Active.Map) {
Metamaps.Mappings = Metamaps.Mappings ? new self.MappingCollection(Metamaps.Mappings) : new self.MappingCollection()
Metamaps.Active.Map = new self.Map(Metamaps.Active.Map)
Active.Map = new self.Map(Active.Map)
}
if (Metamaps.Active.Topic) Metamaps.Active.Topic = new self.Topic(Metamaps.Active.Topic)
if (Active.Topic) Active.Topic = new self.Topic(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()
InfoBox.updateNumbers()
Filter.checkMetacodes()
Filter.checkMappers()
})
Metamaps.Synapses.on('add remove', function (synapse) {
Metamaps.Map.InfoBox.updateNumbers()
Metamaps.Filter.checkSynapses()
Metamaps.Filter.checkMappers()
InfoBox.updateNumbers()
Filter.checkSynapses()
Filter.checkMappers()
})
if (Metamaps.Active.Map) {
if (Active.Map) {
Metamaps.Mappings.on('add remove', function (mapping) {
Metamaps.Map.InfoBox.updateNumbers()
Metamaps.Filter.checkSynapses()
Metamaps.Filter.checkMetacodes()
Metamaps.Filter.checkMappers()
InfoBox.updateNumbers()
Filter.checkSynapses()
Filter.checkMetacodes()
Filter.checkMappers()
})
}
}
self.attachCollectionEvents()
}; // end Metamaps.Backbone.init
}; // end _Backbone.init
export default _Backbone

View file

@ -1,154 +1,156 @@
/* global Metamaps, $ */
import _ from 'lodash'
import Active from './Active'
import Filter from './Filter'
import GlobalUI from './GlobalUI'
import JIT from './JIT'
import Mouse from './Mouse'
import Selected from './Selected'
import Settings from './Settings'
import Visualize from './Visualize'
/*
* Metamaps.Control.js.erb
* Metamaps.Control.js
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.Control
* - Metamaps.Filter
* - Metamaps.GlobalUI
* - Metamaps.JIT
* - Metamaps.Mappings
* - Metamaps.Metacodes
* - Metamaps.Mouse
* - Metamaps.Selected
* - Metamaps.Settings
* - Metamaps.Synapses
* - Metamaps.Topics
* - Metamaps.Visualize
*/
Metamaps.Control = {
const Control = {
init: function () {},
selectNode: function (node, e) {
var filtered = node.getData('alpha') === 0
if (filtered || Metamaps.Selected.Nodes.indexOf(node) != -1) return
if (filtered || Selected.Nodes.indexOf(node) != -1) return
node.selected = true
node.setData('dim', 30, 'current')
Metamaps.Selected.Nodes.push(node)
Selected.Nodes.push(node)
},
deselectAllNodes: function () {
var l = Metamaps.Selected.Nodes.length
var l = Selected.Nodes.length
for (var i = l - 1; i >= 0; i -= 1) {
var node = Metamaps.Selected.Nodes[i]
Metamaps.Control.deselectNode(node)
var node = Selected.Nodes[i]
Control.deselectNode(node)
}
Metamaps.Visualize.mGraph.plot()
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)
Selected.Nodes.splice(
Selected.Nodes.indexOf(node), 1)
},
deleteSelected: function () {
if (!Metamaps.Active.Map) return
if (!Active.Map) return
var n = Metamaps.Selected.Nodes.length
var e = Metamaps.Selected.Edges.length
var n = Selected.Nodes.length
var e = Selected.Edges.length
var ntext = n == 1 ? '1 topic' : n + ' topics'
var etext = e == 1 ? '1 synapse' : e + ' synapses'
var text = 'You have ' + ntext + ' and ' + etext + ' selected. '
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper)
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
Metamaps.GlobalUI.notifyUser('Cannot edit Public map.')
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()
Control.deleteSelectedEdges()
Control.deleteSelectedNodes()
}
},
deleteSelectedNodes: function () { // refers to deleting topics permanently
if (!Metamaps.Active.Map) return
if (!Active.Map) return
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper)
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
Metamaps.GlobalUI.notifyUser('Cannot edit Public map.')
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
var l = Metamaps.Selected.Nodes.length
var l = Selected.Nodes.length
for (var i = l - 1; i >= 0; i -= 1) {
var node = Metamaps.Selected.Nodes[i]
Metamaps.Control.deleteNode(node.id)
var node = Selected.Nodes[i]
Control.deleteNode(node.id)
}
},
deleteNode: function (nodeid) { // refers to deleting topics permanently
if (!Metamaps.Active.Map) return
if (!Active.Map) return
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper)
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
Metamaps.GlobalUI.notifyUser('Cannot edit Public map.')
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
var node = Metamaps.Visualize.mGraph.graph.getNode(nodeid)
var node = 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')
var permToDelete = Active.Mapper.id === topic.get('user_id') || 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, [{
$(document).trigger(JIT.events.deleteTopic, [{
mappableid: mappableid
}])
Metamaps.Control.hideNode(nodeid)
Control.hideNode(nodeid)
} else {
Metamaps.GlobalUI.notifyUser('Only topics you created can be deleted')
GlobalUI.notifyUser('Only topics you created can be deleted')
}
},
removeSelectedNodes: function () { // refers to removing topics permanently from a map
if (Metamaps.Active.Topic) {
if (Active.Topic) {
// hideNode will handle synapses as well
var nodeids = _.map(Metamaps.Selected.Nodes, function(node) {
var nodeids = _.map(Selected.Nodes, function(node) {
return node.id
})
_.each(nodeids, function(nodeid) {
if (Metamaps.Active.Topic.id !== nodeid) {
if (Active.Topic.id !== nodeid) {
Metamaps.Topics.remove(nodeid)
Metamaps.Control.hideNode(nodeid)
Control.hideNode(nodeid)
}
})
return
}
if (!Metamaps.Active.Map) return
if (!Active.Map) return
var l = Metamaps.Selected.Nodes.length,
var l = Selected.Nodes.length,
i,
node,
authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper)
authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
Metamaps.GlobalUI.notifyUser('Cannot edit Public map.')
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)
node = Selected.Nodes[i]
Control.removeNode(node.id)
}
},
removeNode: function (nodeid) { // refers to removing topics permanently from a map
if (!Metamaps.Active.Map) return
if (!Active.Map) return
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper)
var node = Metamaps.Visualize.mGraph.graph.getNode(nodeid)
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
var node = Visualize.mGraph.graph.getNode(nodeid)
if (!authorized) {
Metamaps.GlobalUI.notifyUser('Cannot edit Public map.')
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
@ -157,116 +159,116 @@ Metamaps.Control = {
var mapping = node.getData('mapping')
mapping.destroy()
Metamaps.Topics.remove(topic)
$(document).trigger(Metamaps.JIT.events.removeTopic, [{
$(document).trigger(JIT.events.removeTopic, [{
mappableid: mappableid
}])
Metamaps.Control.hideNode(nodeid)
Control.hideNode(nodeid)
},
hideSelectedNodes: function () {
var l = Metamaps.Selected.Nodes.length,
var l = Selected.Nodes.length,
i,
node
for (i = l - 1; i >= 0; i -= 1) {
node = Metamaps.Selected.Nodes[i]
Metamaps.Control.hideNode(node.id)
node = Selected.Nodes[i]
Control.hideNode(node.id)
}
},
hideNode: function (nodeid) {
var node = Metamaps.Visualize.mGraph.graph.getNode(nodeid)
var graph = Metamaps.Visualize.mGraph
var node = Visualize.mGraph.graph.getNode(nodeid)
var graph = Visualize.mGraph
Metamaps.Control.deselectNode(node)
Control.deselectNode(node)
node.setData('alpha', 0, 'end')
node.eachAdjacency(function (adj) {
adj.setData('alpha', 0, 'end')
})
Metamaps.Visualize.mGraph.fx.animate({
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"
if (nodeid == Visualize.mGraph.root) { // && Visualize.type === "RGraph"
var newroot = _.find(graph.graph.nodes, function (n) { return n.id !== nodeid; })
graph.root = newroot ? newroot.id : null
}
Metamaps.Visualize.mGraph.graph.removeNode(nodeid)
Visualize.mGraph.graph.removeNode(nodeid)
}, 500)
Metamaps.Filter.checkMetacodes()
Metamaps.Filter.checkMappers()
Filter.checkMetacodes()
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
if (filtered || Selected.Edges.indexOf(edge) != -1) return
var width = Metamaps.Mouse.edgeHoveringOver === edge ? 4 : 2
var width = Mouse.edgeHoveringOver === edge ? 4 : 2
edge.setDataset('current', {
showDesc: true,
lineWidth: width,
color: Metamaps.Settings.colors.synapses.selected
color: Settings.colors.synapses.selected
})
Metamaps.Visualize.mGraph.plot()
Visualize.mGraph.plot()
Metamaps.Selected.Edges.push(edge)
Selected.Edges.push(edge)
},
deselectAllEdges: function () {
var l = Metamaps.Selected.Edges.length
var l = Selected.Edges.length
for (var i = l - 1; i >= 0; i -= 1) {
var edge = Metamaps.Selected.Edges[i]
Metamaps.Control.deselectEdge(edge)
var edge = Selected.Edges[i]
Control.deselectEdge(edge)
}
Metamaps.Visualize.mGraph.plot()
Visualize.mGraph.plot()
},
deselectEdge: function (edge) {
edge.setData('showDesc', false, 'current')
edge.setDataset('current', {
lineWidth: 2,
color: Metamaps.Settings.colors.synapses.normal
color: Settings.colors.synapses.normal
})
if (Metamaps.Mouse.edgeHoveringOver == edge) {
if (Mouse.edgeHoveringOver == edge) {
edge.setDataset('current', {
showDesc: true,
lineWidth: 4
})
}
Metamaps.Visualize.mGraph.plot()
Visualize.mGraph.plot()
// remove the edge
Metamaps.Selected.Edges.splice(
Metamaps.Selected.Edges.indexOf(edge), 1)
Selected.Edges.splice(
Selected.Edges.indexOf(edge), 1)
},
deleteSelectedEdges: function () { // refers to deleting topics permanently
var edge,
l = Metamaps.Selected.Edges.length
l = Selected.Edges.length
if (!Metamaps.Active.Map) return
if (!Active.Map) return
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper)
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
Metamaps.GlobalUI.notifyUser('Cannot edit Public map.')
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)
edge = Selected.Edges[i]
Control.deleteEdge(edge)
}
},
deleteEdge: function (edge) {
if (!Metamaps.Active.Map) return
if (!Active.Map) return
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper)
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
Metamaps.GlobalUI.notifyUser('Cannot edit Public map.')
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
@ -275,10 +277,10 @@ Metamaps.Control = {
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')
var permToDelete = Active.Mapper.id === synapse.get('user_id') || Active.Mapper.get('admin')
if (permToDelete) {
if (edge.getData('synapses').length - 1 === 0) {
Metamaps.Control.hideEdge(edge)
Control.hideEdge(edge)
}
var mappableid = synapse.id
synapse.destroy()
@ -290,46 +292,46 @@ Metamaps.Control = {
if (edge.getData('displayIndex')) {
delete edge.data.$displayIndex
}
$(document).trigger(Metamaps.JIT.events.deleteSynapse, [{
$(document).trigger(JIT.events.deleteSynapse, [{
mappableid: mappableid
}])
} else {
Metamaps.GlobalUI.notifyUser('Only synapses you created can be deleted')
GlobalUI.notifyUser('Only synapses you created can be deleted')
}
},
removeSelectedEdges: function () {
// Topic view is handled by removeSelectedNodes
if (!Metamaps.Active.Map) return
if (!Active.Map) return
var l = Metamaps.Selected.Edges.length,
var l = Selected.Edges.length,
i,
edge
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper)
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
Metamaps.GlobalUI.notifyUser('Cannot edit Public map.')
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
for (i = l - 1; i >= 0; i -= 1) {
edge = Metamaps.Selected.Edges[i]
Metamaps.Control.removeEdge(edge)
edge = Selected.Edges[i]
Control.removeEdge(edge)
}
Metamaps.Selected.Edges = [ ]
Selected.Edges = [ ]
},
removeEdge: function (edge) {
if (!Metamaps.Active.Map) return
if (!Active.Map) return
var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper)
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
Metamaps.GlobalUI.notifyUser('Cannot edit Public map.')
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
if (edge.getData('mappings').length - 1 === 0) {
Metamaps.Control.hideEdge(edge)
Control.hideEdge(edge)
}
var index = edge.getData('displayIndex') ? edge.getData('displayIndex') : 0
@ -346,51 +348,51 @@ Metamaps.Control = {
if (edge.getData('displayIndex')) {
delete edge.data.$displayIndex
}
$(document).trigger(Metamaps.JIT.events.removeSynapse, [{
$(document).trigger(JIT.events.removeSynapse, [{
mappableid: mappableid
}])
},
hideSelectedEdges: function () {
var edge,
l = Metamaps.Selected.Edges.length,
l = Selected.Edges.length,
i
for (i = l - 1; i >= 0; i -= 1) {
edge = Metamaps.Selected.Edges[i]
Metamaps.Control.hideEdge(edge)
edge = Selected.Edges[i]
Control.hideEdge(edge)
}
Metamaps.Selected.Edges = [ ]
Selected.Edges = [ ]
},
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({
Control.deselectEdge(edge)
Visualize.mGraph.fx.animate({
modes: ['edge-property:alpha'],
duration: 500
})
setTimeout(function () {
Metamaps.Visualize.mGraph.graph.removeAdjacence(from, to)
Visualize.mGraph.graph.removeAdjacence(from, to)
}, 500)
Metamaps.Filter.checkSynapses()
Metamaps.Filter.checkMappers()
Filter.checkSynapses()
Filter.checkMappers()
},
updateSelectedPermissions: function (permission) {
var edge, synapse, node, topic
Metamaps.GlobalUI.notifyUser('Working...')
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
var l = Selected.Edges.length
for (var i = l - 1; i >= 0; i -= 1) {
edge = Metamaps.Selected.Edges[i]
edge = Selected.Edges[i]
synapse = edge.getData('synapses')[0]
if (synapse.authorizePermissionChange(Metamaps.Active.Mapper)) {
if (synapse.authorizePermissionChange(Active.Mapper)) {
synapse.save({
permission: permission
})
@ -399,12 +401,12 @@ Metamaps.Control = {
}
// change the permission of the selected topics, if logged in user is the original creator
var l = Metamaps.Selected.Nodes.length
var l = Selected.Nodes.length
for (var i = l - 1; i >= 0; i -= 1) {
node = Metamaps.Selected.Nodes[i]
node = Selected.Nodes[i]
topic = node.getData('topic')
if (topic.authorizePermissionChange(Metamaps.Active.Mapper)) {
if (topic.authorizePermissionChange(Active.Mapper)) {
topic.save({
permission: permission
})
@ -416,12 +418,12 @@ Metamaps.Control = {
var sString = sCount == 1 ? (sCount.toString() + ' synapse') : (sCount.toString() + ' synapses')
var message = nString + sString + ' you created updated to ' + permission
Metamaps.GlobalUI.notifyUser(message)
GlobalUI.notifyUser(message)
},
updateSelectedMetacodes: function (metacode_id) {
var node, topic
Metamaps.GlobalUI.notifyUser('Working...')
GlobalUI.notifyUser('Working...')
var metacode = Metamaps.Metacodes.get(metacode_id)
@ -429,12 +431,12 @@ Metamaps.Control = {
var nCount = 0
// change the permission of the selected topics, if logged in user is the original creator
var l = Metamaps.Selected.Nodes.length
var l = Selected.Nodes.length
for (var i = l - 1; i >= 0; i -= 1) {
node = Metamaps.Selected.Nodes[i]
node = Selected.Nodes[i]
topic = node.getData('topic')
if (topic.authorizeToEdit(Metamaps.Active.Mapper)) {
if (topic.authorizeToEdit(Active.Mapper)) {
topic.save({
'metacode_id': metacode_id
})
@ -445,7 +447,9 @@ Metamaps.Control = {
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()
GlobalUI.notifyUser(message)
Visualize.mGraph.plot()
},
}; // end Metamaps.Control
}
export default Control

View file

@ -1,20 +1,21 @@
/* global Metamaps, $ */
import Mouse from './Mouse'
import Selected from './Selected'
import Synapse from './Synapse'
import Topic from './Topic'
import Visualize from './Visualize'
import GlobalUI from './GlobalUI'
/*
* Metamaps.Create.js
*
* Dependencies:
* - Metamaps.Backbone
* - Metamaps.GlobalUI
* - Metamaps.Metacodes
* - Metamaps.Mouse
* - Metamaps.Selected
* - Metamaps.Synapse
* - Metamaps.Topic
* - Metamaps.Visualize
*/
Metamaps.Create = {
const Create = {
isSwitchingSet: false, // indicates whether the metacode set switch lightbox is open
selectedMetacodeSet: null,
selectedMetacodeSetIndex: null,
@ -23,7 +24,7 @@ Metamaps.Create = {
selectedMetacodes: [],
newSelectedMetacodes: [],
init: function () {
var self = Metamaps.Create
var self = Create
self.newTopic.init()
self.newSynapse.init()
@ -36,7 +37,7 @@ Metamaps.Create = {
$('.customMetacodeList li').click(self.toggleMetacodeSelected) // within the custom metacode set tab
},
toggleMetacodeSelected: function () {
var self = Metamaps.Create
var self = Create
if ($(this).attr('class') != 'toggledOff') {
$(this).addClass('toggledOff')
@ -51,29 +52,29 @@ Metamaps.Create = {
}
},
updateMetacodeSet: function (set, index, custom) {
if (custom && Metamaps.Create.newSelectedMetacodes.length == 0) {
if (custom && 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
Create.selectedMetacodeSetIndex = index
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 = []
Create.selectedMetacodes = []
Create.selectedMetacodeNames = []
Create.newSelectedMetacodes = []
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)
Create.selectedMetacodes = Create.newSelectedMetacodes.slice(0)
Create.selectedMetacodeNames = Create.newSelectedMetacodeNames.slice(0)
codesToSwitchToIds = Create.selectedMetacodes.slice(0)
}
// sort by name
@ -100,12 +101,12 @@ Metamaps.Create = {
bringToFront: true
})
Metamaps.GlobalUI.closeLightbox()
GlobalUI.closeLightbox()
$('#topic_name').focus()
var mdata = {
'metacodes': {
'value': custom ? Metamaps.Create.selectedMetacodes.toString() : Metamaps.Create.selectedMetacodeSet
'value': custom ? Create.selectedMetacodes.toString() : Create.selectedMetacodeSet
}
}
$.ajax({
@ -123,7 +124,7 @@ Metamaps.Create = {
},
cancelMetacodeSetSwitch: function () {
var self = Metamaps.Create
var self = Create
self.isSwitchingSet = false
if (self.selectedMetacodeSet != 'metacodeset-custom') {
@ -148,17 +149,17 @@ Metamaps.Create = {
newTopic: {
init: function () {
$('#topic_name').keyup(function () {
Metamaps.Create.newTopic.name = $(this).val()
Create.newTopic.name = $(this).val()
})
$('.pinCarousel').click(function() {
if (Metamaps.Create.newTopic.pinned) {
if (Create.newTopic.pinned) {
$('.pinCarousel').removeClass('isPinned')
Metamaps.Create.newTopic.pinned = false
Create.newTopic.pinned = false
}
else {
$('.pinCarousel').addClass('isPinned')
Metamaps.Create.newTopic.pinned = true
Create.newTopic.pinned = true
}
})
@ -192,7 +193,7 @@ Metamaps.Create = {
// tell the autocomplete to submit the form with the topic you clicked on if you pick from the autocomplete
$('#topic_name').bind('typeahead:select', function (event, datum, dataset) {
Metamaps.Topic.getTopicFromAutocomplete(datum.id)
Topic.getTopicFromAutocomplete(datum.id)
})
// initialize metacode spinner and then hide it
@ -220,24 +221,24 @@ Metamaps.Create = {
$('#new_topic').fadeIn('fast', function () {
$('#topic_name').focus()
})
Metamaps.Create.newTopic.beingCreated = true
Metamaps.Create.newTopic.name = ''
Create.newTopic.beingCreated = true
Create.newTopic.name = ''
},
hide: function (force) {
if (force || !Metamaps.Create.newTopic.pinned) {
if (force || !Create.newTopic.pinned) {
$('#new_topic').fadeOut('fast')
Metamaps.Create.newTopic.beingCreated = false
Create.newTopic.beingCreated = false
}
if (force) {
$('.pinCarousel').removeClass('isPinned')
Metamaps.Create.newTopic.pinned = false
Create.newTopic.pinned = false
}
$('#topic_name').typeahead('val', '')
}
},
newSynapse: {
init: function () {
var self = Metamaps.Create.newSynapse
var self = Create.newSynapse
var synapseBloodhound = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
@ -253,8 +254,8 @@ Metamaps.Create = {
remote: {
url: '/search/synapses?topic1id=%TOPIC1&topic2id=%TOPIC2',
prepare: function (query, settings) {
var self = Metamaps.Create.newSynapse
if (Metamaps.Selected.Nodes.length < 2) {
var self = Create.newSynapse
if (Selected.Nodes.length < 2) {
settings.url = settings.url.replace('%TOPIC1', self.topic1id).replace('%TOPIC2', self.topic2id)
return settings
} else {
@ -299,23 +300,23 @@ Metamaps.Create = {
if (e.keyCode === BACKSPACE && $(this).val() === '' ||
e.keyCode === DELETE && $(this).val() === '' ||
e.keyCode === ESC) {
Metamaps.Create.newSynapse.hide()
Create.newSynapse.hide()
} // if
Metamaps.Create.newSynapse.description = $(this).val()
Create.newSynapse.description = $(this).val()
})
$('#synapse_desc').focusout(function () {
if (Metamaps.Create.newSynapse.beingCreated) {
Metamaps.Synapse.createSynapseLocally()
if (Create.newSynapse.beingCreated) {
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)
Synapse.getSynapseFromAutocomplete(datum.id)
} else {
Metamaps.Create.newSynapse.description = datum.value
Metamaps.Synapse.createSynapseLocally()
Create.newSynapse.description = datum.value
Synapse.createSynapseLocally()
}
})
},
@ -328,17 +329,19 @@ Metamaps.Create = {
$('#new_synapse').fadeIn(100, function () {
$('#synapse_desc').focus()
})
Metamaps.Create.newSynapse.beingCreated = true
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()
Create.newSynapse.beingCreated = false
Create.newTopic.addSynapse = false
Create.newSynapse.topic1id = 0
Create.newSynapse.topic2id = 0
Mouse.synapseStartCoordinates = []
Visualize.mGraph.plot()
},
}
}; // end Metamaps.Create
}
export default Create

View file

@ -0,0 +1,6 @@
const Debug = (arg = window.Metamaps) => {
console.debug(arg)
console.debug(`Metamaps Version: ${arg.VERSION}`)
}
export default Debug

View file

@ -1,21 +1,24 @@
/* global Metamaps, $ */
import _ from 'lodash'
import Active from './Active'
import Control from './Control'
import GlobalUI from './GlobalUI'
import Settings from './Settings'
import Visualize from './Visualize'
/*
* Metamaps.Filter.js.erb
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.Control
* - Metamaps.Creators
* - Metamaps.GlobalUI
* - Metamaps.Mappers
* - Metamaps.Metacodes
* - Metamaps.Settings
* - Metamaps.Synapses
* - Metamaps.Topics
* - Metamaps.Visualize
*/
Metamaps.Filter = {
const Filter = {
filters: {
name: '',
metacodes: [],
@ -30,7 +33,7 @@ Metamaps.Filter = {
isOpen: false,
changing: false,
init: function () {
var self = Metamaps.Filter
var self = Filter
$('.sidebarFilterIcon').click(self.toggleBox)
@ -45,7 +48,7 @@ Metamaps.Filter = {
self.getFilterData()
},
toggleBox: function (event) {
var self = Metamaps.Filter
var self = Filter
if (self.isOpen) self.close()
else self.open()
@ -53,9 +56,9 @@ Metamaps.Filter = {
event.stopPropagation()
},
open: function () {
var self = Metamaps.Filter
var self = Filter
Metamaps.GlobalUI.Account.close()
GlobalUI.Account.close()
$('.sidebarFilterIcon div').addClass('hide')
if (!self.isOpen && !self.changing) {
@ -69,7 +72,7 @@ Metamaps.Filter = {
}
},
close: function () {
var self = Metamaps.Filter
var self = Filter
$('.sidebarFilterIcon div').removeClass('hide')
if (!self.changing) {
@ -82,7 +85,7 @@ Metamaps.Filter = {
}
},
reset: function () {
var self = Metamaps.Filter
var self = Filter
self.filters.metacodes = []
self.filters.mappers = []
@ -102,7 +105,7 @@ Metamaps.Filter = {
But what these function do is load this data into three accessible array within java : metacodes, mappers and synapses
*/
getFilterData: function () {
var self = Metamaps.Filter
var self = Filter
var metacode, mapper, synapse
@ -125,7 +128,7 @@ Metamaps.Filter = {
})
},
bindLiClicks: function () {
var self = Metamaps.Filter
var self = Filter
$('#filter_by_metacode ul li').unbind().click(self.toggleMetacode)
$('#filter_by_mapper ul li').unbind().click(self.toggleMapper)
$('#filter_by_synapse ul li').unbind().click(self.toggleSynapse)
@ -136,7 +139,7 @@ Metamaps.Filter = {
@param
*/
updateFilters: function (collection, propertyToCheck, correlatedModel, filtersToUse, listToModify) {
var self = Metamaps.Filter
var self = Filter
var newList = []
var removed = []
@ -211,12 +214,12 @@ Metamaps.Filter = {
self.bindLiClicks()
},
checkMetacodes: function () {
var self = Metamaps.Filter
var self = Filter
self.updateFilters('Topics', 'metacode_id', 'Metacodes', 'metacodes', 'metacode')
},
checkMappers: function () {
var self = Metamaps.Filter
var onMap = Metamaps.Active.Map ? true : false
var self = Filter
var onMap = Active.Map ? true : false
if (onMap) {
self.updateFilters('Mappings', 'user_id', 'Mappers', 'mappers', 'mapper')
} else {
@ -225,11 +228,11 @@ Metamaps.Filter = {
}
},
checkSynapses: function () {
var self = Metamaps.Filter
var self = Filter
self.updateFilters('Synapses', 'desc', 'Synapses', 'synapses', 'synapse')
},
filterAllMetacodes: function (e) {
var self = Metamaps.Filter
var self = Filter
$('#filter_by_metacode ul li').addClass('toggledOff')
$('.showAllMetacodes').removeClass('active')
$('.hideAllMetacodes').addClass('active')
@ -237,7 +240,7 @@ Metamaps.Filter = {
self.passFilters()
},
filterNoMetacodes: function (e) {
var self = Metamaps.Filter
var self = Filter
$('#filter_by_metacode ul li').removeClass('toggledOff')
$('.showAllMetacodes').addClass('active')
$('.hideAllMetacodes').removeClass('active')
@ -245,7 +248,7 @@ Metamaps.Filter = {
self.passFilters()
},
filterAllMappers: function (e) {
var self = Metamaps.Filter
var self = Filter
$('#filter_by_mapper ul li').addClass('toggledOff')
$('.showAllMappers').removeClass('active')
$('.hideAllMappers').addClass('active')
@ -253,7 +256,7 @@ Metamaps.Filter = {
self.passFilters()
},
filterNoMappers: function (e) {
var self = Metamaps.Filter
var self = Filter
$('#filter_by_mapper ul li').removeClass('toggledOff')
$('.showAllMappers').addClass('active')
$('.hideAllMappers').removeClass('active')
@ -261,7 +264,7 @@ Metamaps.Filter = {
self.passFilters()
},
filterAllSynapses: function (e) {
var self = Metamaps.Filter
var self = Filter
$('#filter_by_synapse ul li').addClass('toggledOff')
$('.showAllSynapses').removeClass('active')
$('.hideAllSynapses').addClass('active')
@ -269,7 +272,7 @@ Metamaps.Filter = {
self.passFilters()
},
filterNoSynapses: function (e) {
var self = Metamaps.Filter
var self = Filter
$('#filter_by_synapse ul li').removeClass('toggledOff')
$('.showAllSynapses').addClass('active')
$('.hideAllSynapses').removeClass('active')
@ -280,7 +283,7 @@ Metamaps.Filter = {
// to reduce code redundancy
// gets called in the context of a list item in a filter box
toggleLi: function (whichToFilter) {
var self = Metamaps.Filter, index
var self = Filter, index
var id = $(this).attr('data-id')
if (self.visible[whichToFilter].indexOf(id) == -1) {
self.visible[whichToFilter].push(id)
@ -293,7 +296,7 @@ Metamaps.Filter = {
self.passFilters()
},
toggleMetacode: function () {
var self = Metamaps.Filter
var self = Filter
self.toggleLi.call(this, 'metacodes')
if (self.visible.metacodes.length === self.filters.metacodes.length) {
@ -309,7 +312,7 @@ Metamaps.Filter = {
}
},
toggleMapper: function () {
var self = Metamaps.Filter
var self = Filter
self.toggleLi.call(this, 'mappers')
if (self.visible.mappers.length === self.filters.mappers.length) {
@ -325,7 +328,7 @@ Metamaps.Filter = {
}
},
toggleSynapse: function () {
var self = Metamaps.Filter
var self = Filter
self.toggleLi.call(this, 'synapses')
if (self.visible.synapses.length === self.filters.synapses.length) {
@ -341,16 +344,16 @@ Metamaps.Filter = {
}
},
passFilters: function () {
var self = Metamaps.Filter
var self = Filter
var visible = self.visible
var passesMetacode, passesMapper, passesSynapse
var onMap
if (Metamaps.Active.Map) {
if (Active.Map) {
onMap = true
}
else if (Metamaps.Active.Topic) {
else if (Active.Topic) {
onMap = false
}
@ -386,10 +389,10 @@ Metamaps.Filter = {
else console.log(topic)
} else {
if (n) {
Metamaps.Control.deselectNode(n, true)
Control.deselectNode(n, true)
n.setData('alpha', opacityForFilter, 'end')
n.eachAdjacency(function (e) {
Metamaps.Control.deselectEdge(e, true)
Control.deselectEdge(e, true)
})
}
else console.log(topic)
@ -442,12 +445,12 @@ Metamaps.Filter = {
if (visible.mappers.indexOf(user_id) == -1) passesMapper = false
else passesMapper = true
var color = Metamaps.Settings.colors.synapses.normal
var color = Settings.colors.synapses.normal
if (passesSynapse && passesMapper) {
e.setData('alpha', 1, 'end')
e.setData('color', color, 'end')
} else {
Metamaps.Control.deselectEdge(e, true)
Control.deselectEdge(e, true)
e.setData('alpha', opacityForFilter, 'end')
}
@ -457,10 +460,12 @@ Metamaps.Filter = {
})
// run the animation
Metamaps.Visualize.mGraph.fx.animate({
Visualize.mGraph.fx.animate({
modes: ['node-property:alpha',
'edge-property:alpha'],
duration: 200
})
}
}; // end Metamaps.Filter
}
export default Filter

View file

@ -0,0 +1,646 @@
import Active from './Active'
import Create from './Create'
import Filter from './Filter'
import Router from './Router'
/*
* Metamaps.Backbone
* Metamaps.Erb
* Metamaps.Maps
*/
const GlobalUI = {
notifyTimeout: null,
lightbox: null,
init: function () {
var self = GlobalUI;
self.Search.init();
self.CreateMap.init();
self.Account.init();
if ($('#toast').html().trim()) self.notifyUser($('#toast').html())
//bind lightbox clicks
$('.openLightbox').click(function (event) {
self.openLightbox($(this).attr('data-open'));
event.preventDefault();
return false;
});
$('#lightbox_screen, #lightbox_close').click(self.closeLightbox);
// initialize global backbone models and collections
if (Active.Mapper) Active.Mapper = new Metamaps.Backbone.Mapper(Active.Mapper);
var myCollection = Metamaps.Maps.Mine ? Metamaps.Maps.Mine : [];
var sharedCollection = Metamaps.Maps.Shared ? Metamaps.Maps.Shared : [];
var starredCollection = Metamaps.Maps.Starred ? Metamaps.Maps.Starred : [];
var mapperCollection = [];
var mapperOptionsObj = {id: 'mapper', sortBy: 'updated_at' };
if (Metamaps.Maps.Mapper) {
mapperCollection = Metamaps.Maps.Mapper.models;
mapperOptionsObj.mapperId = Metamaps.Maps.Mapper.id;
}
var featuredCollection = Metamaps.Maps.Featured ? Metamaps.Maps.Featured : [];
var activeCollection = Metamaps.Maps.Active ? Metamaps.Maps.Active : [];
Metamaps.Maps.Mine = new Metamaps.Backbone.MapsCollection(myCollection, {id: 'mine', sortBy: 'updated_at' });
Metamaps.Maps.Shared = new Metamaps.Backbone.MapsCollection(sharedCollection, {id: 'shared', sortBy: 'updated_at' });
Metamaps.Maps.Starred = new Metamaps.Backbone.MapsCollection(starredCollection, {id: 'starred', sortBy: 'updated_at' });
// 'Mapper' refers to another mapper
Metamaps.Maps.Mapper = new Metamaps.Backbone.MapsCollection(mapperCollection, mapperOptionsObj);
Metamaps.Maps.Featured = new Metamaps.Backbone.MapsCollection(featuredCollection, {id: 'featured', sortBy: 'updated_at' });
Metamaps.Maps.Active = new Metamaps.Backbone.MapsCollection(activeCollection, {id: 'active', sortBy: 'updated_at' });
},
showDiv: function (selector) {
$(selector).show()
$(selector).animate({
opacity: 1
}, 200, 'easeOutCubic')
},
hideDiv: function (selector) {
$(selector).animate({
opacity: 0
}, 200, 'easeInCubic', function () { $(this).hide() })
},
openLightbox: function (which) {
var self = GlobalUI;
$('.lightboxContent').hide();
$('#' + which).show();
self.lightbox = which;
$('#lightbox_overlay').show();
var heightOfContent = '-' + ($('#lightbox_main').height() / 2) + 'px';
// animate the content in from the bottom
$('#lightbox_main').animate({
'top': '50%',
'margin-top': heightOfContent
}, 200, 'easeOutCubic');
// fade the black overlay in
$('#lightbox_screen').animate({
'opacity': '0.42'
}, 200);
if (which == "switchMetacodes") {
Create.isSwitchingSet = true;
}
},
closeLightbox: function (event) {
var self = GlobalUI;
if (event) event.preventDefault();
// animate the lightbox content offscreen
$('#lightbox_main').animate({
'top': '100%',
'margin-top': '0'
}, 200, 'easeInCubic');
// fade the black overlay out
$('#lightbox_screen').animate({
'opacity': '0.0'
}, 200, function () {
$('#lightbox_overlay').hide();
});
if (self.lightbox === 'forkmap') GlobalUI.CreateMap.reset('fork_map');
if (self.lightbox === 'newmap') GlobalUI.CreateMap.reset('new_map');
if (Create && Create.isSwitchingSet) {
Create.cancelMetacodeSetSwitch();
}
self.lightbox = null;
},
notifyUser: function (message, leaveOpen) {
var self = GlobalUI;
$('#toast').html(message)
self.showDiv('#toast')
clearTimeout(self.notifyTimeOut);
if (!leaveOpen) {
self.notifyTimeOut = setTimeout(function () {
self.hideDiv('#toast')
}, 8000);
}
},
clearNotify: function() {
var self = GlobalUI;
clearTimeout(self.notifyTimeOut);
self.hideDiv('#toast')
},
shareInvite: function(inviteLink) {
window.prompt("To copy the invite link, press: Ctrl+C, Enter", inviteLink);
}
}
GlobalUI.CreateMap = {
newMap: null,
emptyMapForm: "",
emptyForkMapForm: "",
topicsToMap: [],
synapsesToMap: [],
init: function () {
var self = GlobalUI.CreateMap;
self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' });
self.bindFormEvents();
self.emptyMapForm = $('#new_map').html();
},
bindFormEvents: function () {
var self = GlobalUI.CreateMap;
$('.new_map input, .new_map div').unbind('keypress').bind('keypress', function(event) {
if (event.keyCode === 13) self.submit()
})
$('.new_map button.cancel').unbind().bind('click', function (event) {
event.preventDefault();
GlobalUI.closeLightbox();
});
$('.new_map button.submitMap').unbind().bind('click', self.submit);
// bind permission changer events on the createMap form
$('.permIcon').unbind().bind('click', self.switchPermission);
},
closeSuccess: function () {
$('#mapCreatedSuccess').fadeOut(300, function(){
$(this).remove();
});
},
generateSuccessMessage: function (id) {
var stringStart = "<div id='mapCreatedSuccess'><h6>SUCCESS!</h6>Your map has been created. Do you want to: <a id='mapGo' href='/maps/";
stringStart += id;
stringStart += "' onclick='GlobalUI.CreateMap.closeSuccess();'>Go to your new map</a>";
stringStart += "<span>OR</span><a id='mapStay' href='#' onclick='GlobalUI.CreateMap.closeSuccess(); return false;'>Stay on this ";
var page = Active.Map ? 'map' : 'page';
var stringEnd = "</a></div>";
return stringStart + page + stringEnd;
},
switchPermission: function () {
var self = GlobalUI.CreateMap;
self.newMap.set('permission', $(this).attr('data-permission'));
$(this).siblings('.permIcon').find('.mapPermIcon').removeClass('selected');
$(this).find('.mapPermIcon').addClass('selected');
var permText = $(this).find('.tip').html();
$(this).parents('.new_map').find('.permText').html(permText);
},
submit: function (event) {
if (event) event.preventDefault();
var self = GlobalUI.CreateMap;
if (GlobalUI.lightbox === 'forkmap') {
self.newMap.set('topicsToMap', self.topicsToMap);
self.newMap.set('synapsesToMap', self.synapsesToMap);
}
var formId = GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map';
var $form = $(formId);
self.newMap.set('name', $form.find('#map_name').val());
self.newMap.set('desc', $form.find('#map_desc').val());
if (self.newMap.get('name').length===0){
self.throwMapNameError();
return;
}
self.newMap.save(null, {
success: self.success
// TODO add error message
});
GlobalUI.closeLightbox();
GlobalUI.notifyUser('Working...');
},
throwMapNameError: function () {
var self = GlobalUI.CreateMap;
var formId = GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map';
var $form = $(formId);
var message = $("<div class='feedback_message'>Please enter a map name...</div>");
$form.find('#map_name').after(message);
setTimeout(function(){
message.fadeOut('fast', function(){
message.remove();
});
}, 5000);
},
success: function (model) {
var self = GlobalUI.CreateMap;
//push the new map onto the collection of 'my maps'
Metamaps.Maps.Mine.add(model);
var formId = GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map';
var form = $(formId);
GlobalUI.clearNotify();
$('#wrapper').append(self.generateSuccessMessage(model.id));
},
reset: function (id) {
var self = GlobalUI.CreateMap;
var form = $('#' + id);
if (id === "fork_map") {
self.topicsToMap = [];
self.synapsesToMap = [];
form.html(self.emptyForkMapForm);
}
else {
form.html(self.emptyMapForm);
}
self.bindFormEvents();
self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' });
return false;
},
}
GlobalUI.Account = {
isOpen: false,
changing: false,
init: function () {
var self = GlobalUI.Account;
$('.sidebarAccountIcon').click(self.toggleBox);
$('.sidebarAccountBox').click(function(event){
event.stopPropagation();
});
$('body').click(self.close);
},
toggleBox: function (event) {
var self = GlobalUI.Account;
if (self.isOpen) self.close();
else self.open();
event.stopPropagation();
},
open: function () {
var self = GlobalUI.Account;
Filter.close();
$('.sidebarAccountIcon .tooltipsUnder').addClass('hide');
if (!self.isOpen && !self.changing) {
self.changing = true;
$('.sidebarAccountBox').fadeIn(200, function () {
self.changing = false;
self.isOpen = true;
$('.sidebarAccountBox #user_email').focus();
});
}
},
close: function () {
var self = GlobalUI.Account;
$('.sidebarAccountIcon .tooltipsUnder').removeClass('hide');
if (!self.changing) {
self.changing = true;
$('.sidebarAccountBox #user_email').blur();
$('.sidebarAccountBox').fadeOut(200, function () {
self.changing = false;
self.isOpen = false;
});
}
}
}
GlobalUI.Search = {
locked: false,
isOpen: false,
limitTopicsToMe: false,
limitMapsToMe: false,
timeOut: null,
changing: false,
optionsInitialized: false,
init: function () {
var self = GlobalUI.Search;
var loader = new CanvasLoader('searchLoading');
loader.setColor('#4fb5c0'); // default is '#000000'
loader.setDiameter(24); // default is 40
loader.setDensity(41); // default is 40
loader.setRange(0.9); // default is 1.3
loader.show(); // Hidden by default
// bind the hover events
$(".sidebarSearch").hover(function () {
self.open()
}, function () {
self.close(800, false)
});
$('.sidebarSearchIcon').click(function (e) {
$('.sidebarSearchField').focus();
});
$('.sidebarSearch').click(function (e) {
e.stopPropagation();
});
$('body').click(function (e) {
self.close(0, false);
});
// open if the search is closed and user hits ctrl+/
// close if they hit ESC
$('body').bind('keyup', function (e) {
switch (e.which) {
case 191:
if ((e.ctrlKey && !self.isOpen) || (e.ctrlKey && self.locked)) {
self.open(true); // true for focus
}
break;
case 27:
if (self.isOpen) {
self.close(0, true);
}
break;
default:
break; //console.log(e.which);
}
});
self.startTypeahead();
},
lock: function() {
var self = GlobalUI.Search;
self.locked = true;
},
unlock: function() {
var self = GlobalUI.Search;
self.locked = false;
},
open: function (focus) {
var self = GlobalUI.Search;
clearTimeout(self.timeOut);
if (!self.isOpen && !self.changing && !self.locked) {
self.changing = true;
$('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({
width: '400px'
}, 300, function () {
if (focus) $('.sidebarSearchField').focus();
$('.sidebarSearchField, .sidebarSearch .tt-hint').css({
padding: '7px 10px 3px 10px',
width: '380px'
});
self.changing = false;
self.isOpen = true;
});
}
},
close: function (closeAfter, bypass) {
// for now
return
var self = GlobalUI.Search;
self.timeOut = setTimeout(function () {
if (!self.locked && !self.changing && self.isOpen && (bypass || $('.sidebarSearchField.tt-input').val() == '')) {
self.changing = true;
$('.sidebarSearchField, .sidebarSearch .tt-hint').css({
padding: '7px 0 3px 0',
width: '400px'
});
$('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({
width: '0'
}, 300, function () {
$('.sidebarSearchField').typeahead('val', '');
$('.sidebarSearchField').blur();
self.changing = false;
self.isOpen = false;
});
}
}, closeAfter);
},
startTypeahead: function () {
var self = GlobalUI.Search;
var mapheader = Active.Mapper ? '<div class="searchMapsHeader searchHeader"><h3 class="search-heading">Maps</h3><input type="checkbox" class="limitToMe" id="limitMapsToMe"></input><label for="limitMapsToMe" class="limitToMeLabel">added by me</label><div class="minimizeResults minimizeMapResults"></div><div class="clearfloat"></div></div>' : '<div class="searchMapsHeader searchHeader"><h3 class="search-heading">Maps</h3><div class="minimizeResults minimizeMapResults"></div><div class="clearfloat"></div></div>';
var topicheader = Active.Mapper ? '<div class="searchTopicsHeader searchHeader"><h3 class="search-heading">Topics</h3><input type="checkbox" class="limitToMe" id="limitTopicsToMe"></input><label for="limitTopicsToMe" class="limitToMeLabel">added by me</label><div class="minimizeResults minimizeTopicResults"></div><div class="clearfloat"></div></div>' : '<div class="searchTopicsHeader searchHeader"><h3 class="search-heading">Topics</h3><div class="minimizeResults minimizeTopicResults"></div><div class="clearfloat"></div></div>';
var mapperheader = '<div class="searchMappersHeader searchHeader"><h3 class="search-heading">Mappers</h3><div class="minimizeResults minimizeMapperResults"></div><div class="clearfloat"></div></div>';
var topics = {
name: 'topics',
limit: 9999,
display: function(s) { return s.label; },
templates: {
notFound: function(s) {
return Hogan.compile(topicheader + $('#topicSearchTemplate').html()).render({
value: "No results",
label: "No results",
typeImageURL: Metamaps.Erb['icons/wildcard.png'],
rtype: "noresult"
});
},
header: topicheader,
suggestion: function(s) {
return Hogan.compile($('#topicSearchTemplate').html()).render(s);
},
},
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/topics',
prepare: function(query, settings) {
settings.url += '?term=' + query;
if (Active.Mapper && self.limitTopicsToMe) {
settings.url += "&user=" + Active.Mapper.id.toString();
}
return settings;
},
},
}),
};
var maps = {
name: 'maps',
limit: 9999,
display: function(s) { return s.label; },
templates: {
notFound: function(s) {
return Hogan.compile(mapheader + $('#mapSearchTemplate').html()).render({
value: "No results",
label: "No results",
rtype: "noresult"
});
},
header: mapheader,
suggestion: function(s) {
return Hogan.compile($('#mapSearchTemplate').html()).render(s);
},
},
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/maps',
prepare: function(query, settings) {
settings.url += '?term=' + query;
if (Active.Mapper && self.limitMapsToMe) {
settings.url += "&user=" + Active.Mapper.id.toString();
}
return settings;
},
},
}),
};
var mappers = {
name: 'mappers',
limit: 9999,
display: function(s) { return s.label; },
templates: {
notFound: function(s) {
return Hogan.compile(mapperheader + $('#mapperSearchTemplate').html()).render({
value: "No results",
label: "No results",
rtype: "noresult",
profile: Metamaps.Erb['user.png']
});
},
header: mapperheader,
suggestion: function(s) {
return Hogan.compile($('#mapperSearchTemplate').html()).render(s);
},
},
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/mappers?term=%QUERY',
wildcard: '%QUERY',
},
}),
};
// Take all that crazy setup data and put it together into one beautiful typeahead call!
$('.sidebarSearchField').typeahead(
{
highlight: true,
},
[topics, maps, mappers]
);
//Set max height of the search results box to prevent it from covering bottom left footer
$('.sidebarSearchField').bind('typeahead:render', function (event) {
self.initSearchOptions();
self.hideLoader();
var h = $(window).height();
$(".tt-dropdown-menu").css('max-height', h - 100);
if (self.limitTopicsToMe) {
$('#limitTopicsToMe').prop('checked', true);
}
if (self.limitMapsToMe) {
$('#limitMapsToMe').prop('checked', true);
}
});
$(window).resize(function () {
var h = $(window).height();
$(".tt-dropdown-menu").css('max-height', h - 100);
});
// tell the autocomplete to launch a new tab with the topic, map, or mapper you clicked on
$('.sidebarSearchField').bind('typeahead:select', self.handleResultClick);
// don't do it, if they clicked on a 'addToMap' button
$('.sidebarSearch button.addToMap').click(function (event) {
event.stopPropagation();
});
// make sure that when you click on 'limit to me' or 'toggle section' it works
$('.sidebarSearchField.tt-input').keyup(function(){
if ($('.sidebarSearchField.tt-input').val() === '') {
self.hideLoader();
} else {
self.showLoader();
}
});
},
handleResultClick: function (event, datum, dataset) {
var self = GlobalUI.Search;
self.hideLoader();
if (["topic", "map", "mapper"].indexOf(datum.rtype) !== -1) {
self.close(0, true);
var win;
if (datum.rtype == "topic") {
Router.topics(datum.id);
} else if (datum.rtype == "map") {
Router.maps(datum.id);
} else if (datum.rtype == "mapper") {
Router.explore("mapper", datum.id);
}
}
},
initSearchOptions: function () {
var self = GlobalUI.Search;
function toggleResultSet(set) {
var s = $('.tt-dataset-' + set + ' .tt-suggestion, .tt-dataset-' + set + ' .resultnoresult');
if (s.is(':visible')) {
s.hide();
$(this).removeClass('minimizeResults').addClass('maximizeResults');
} else {
s.show();
$(this).removeClass('maximizeResults').addClass('minimizeResults');
}
}
$('.limitToMe').unbind().bind("change", function (e) {
if ($(this).attr('id') == 'limitTopicsToMe') {
self.limitTopicsToMe = !self.limitTopicsToMe;
}
if ($(this).attr('id') == 'limitMapsToMe') {
self.limitMapsToMe = !self.limitMapsToMe;
}
// set the value of the search equal to itself to retrigger the
// autocomplete event
var searchQuery = $('.sidebarSearchField.tt-input').val();
$(".sidebarSearchField").typeahead('val', '')
.typeahead('val', searchQuery);
});
// when the user clicks minimize section, hide the results for that section
$('.minimizeMapperResults').unbind().click(function (e) {
toggleResultSet.call(this, 'mappers');
});
$('.minimizeTopicResults').unbind().click(function (e) {
toggleResultSet.call(this, 'topics');
});
$('.minimizeMapResults').unbind().click(function (e) {
toggleResultSet.call(this, 'maps');
});
},
hideLoader: function () {
$('#searchLoading').hide();
},
showLoader: function () {
$('#searchLoading').show();
}
}
export default GlobalUI

View file

@ -1,19 +1,23 @@
/* global Metamaps, $ */
import Active from './Active'
import GlobalUI from './GlobalUI'
import Map from './Map'
import Synapse from './Synapse'
import Topic from './Topic'
/*
* Metamaps.Import.js.erb
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.Backbone
* - Metamaps.Map
* - Metamaps.Mappings
* - Metamaps.Metacodes
* - Metamaps.Synapses
* - Metamaps.Topics
*/
Metamaps.Import = {
const Import = {
// note that user is not imported
topicWhitelist: [
'id', 'name', 'metacode', 'x', 'y', 'description', 'link', 'permission'
@ -24,19 +28,19 @@ Metamaps.Import = {
cidMappings: {}, // to be filled by import_id => cid mappings
handleTSV: function (text) {
var self = Metamaps.Import
var self = Import
results = self.parseTabbedString(text)
self.handle(results)
},
handleJSON: function (text) {
var self = Metamaps.Import
var self = Import
results = JSON.parse(text)
self.handle(results)
},
handle: function(results) {
var self = Metamaps.Import
var self = Import
var topics = results.topics
var synapses = results.synapses
@ -60,7 +64,7 @@ Metamaps.Import = {
},
parseTabbedString: function (text) {
var self = Metamaps.Import
var self = Import
// determine line ending and split lines
var delim = '\n'
@ -186,7 +190,7 @@ Metamaps.Import = {
},
importTopics: function (parsedTopics) {
var self = Metamaps.Import
var self = Import
// up to 25 topics: scale 100
// up to 81 topics: scale 200
@ -219,7 +223,7 @@ Metamaps.Import = {
},
importSynapses: function (parsedSynapses) {
var self = Metamaps.Import
var self = Import
parsedSynapses.forEach(function (synapse) {
// only createSynapseWithParameters once both topics are persisted
@ -255,16 +259,16 @@ Metamaps.Import = {
createTopicWithParameters: function (name, metacode_name, permission, desc,
link, xloc, yloc, import_id, opts) {
var self = Metamaps.Import
$(document).trigger(Metamaps.Map.events.editedByActiveMapper)
var self = Import
$(document).trigger(Map.events.editedByActiveMapper)
var metacode = Metamaps.Metacodes.where({name: metacode_name})[0] || null
if (metacode === null) {
metacode = Metamaps.Metacodes.where({ name: 'Wildcard' })[0]
console.warn("Couldn't find metacode " + metacode_name + ' so used Wildcard instead.')
}
var topic_permission = permission || Metamaps.Active.Map.get('permission')
var defer_to_map_id = permission === topic_permission ? Metamaps.Active.Map.get('id') : null
var topic_permission = permission || Active.Map.get('permission')
var defer_to_map_id = permission === topic_permission ? Active.Map.get('id') : null
var topic = new Metamaps.Backbone.Topic({
name: name,
metacode_id: metacode.id,
@ -272,7 +276,7 @@ Metamaps.Import = {
defer_to_map_id: defer_to_map_id,
desc: desc || "",
link: link || "",
calculated_permission: Metamaps.Active.Map.get('permission')
calculated_permission: Active.Map.get('permission')
})
Metamaps.Topics.add(topic)
@ -289,11 +293,11 @@ Metamaps.Import = {
Metamaps.Mappings.add(mapping)
// this function also includes the creation of the topic in the database
Metamaps.Topic.renderTopic(mapping, topic, true, true, {
Topic.renderTopic(mapping, topic, true, true, {
success: opts.success
})
Metamaps.GlobalUI.hideDiv('#instructions')
GlobalUI.hideDiv('#instructions')
},
createSynapseWithParameters: function (desc, category, permission,
@ -321,6 +325,8 @@ Metamaps.Import = {
})
Metamaps.Mappings.add(mapping)
Metamaps.Synapse.renderSynapse(mapping, synapse, node1, node2, true)
Synapse.renderSynapse(mapping, synapse, node1, node2, true)
}
}
export default Import

View file

@ -0,0 +1,123 @@
/* global $ */
import Active from './Active'
import Control from './Control'
import JIT from './JIT'
import Mobile from './Mobile'
import Realtime from './Realtime'
import Selected from './Selected'
import Topic from './Topic'
import Visualize from './Visualize'
const Listeners = {
init: function () {
var self = this
$(document).on('keydown', function (e) {
if (!(Active.Map || Active.Topic)) return
switch (e.which) {
case 13: // if enter key is pressed
JIT.enterKeyHandler()
e.preventDefault()
break
case 27: // if esc key is pressed
JIT.escKeyHandler()
break
case 65: // if a or A is pressed
if (e.ctrlKey) {
Control.deselectAllNodes()
Control.deselectAllEdges()
e.preventDefault()
Visualize.mGraph.graph.eachNode(function (n) {
Control.selectNode(n, e)
})
Visualize.mGraph.plot()
}
break
case 68: // if d or D is pressed
if (e.ctrlKey) {
e.preventDefault()
Control.deleteSelected()
}
break
case 69: // if e or E is pressed
if (e.ctrlKey && Active.Map) {
e.preventDefault()
JIT.zoomExtents(null, Visualize.mGraph.canvas)
break
}
if (e.altKey && Active.Topic) {
e.preventDefault()
if (Active.Topic) {
self.centerAndReveal(Selected.Nodes, {
center: true,
reveal: false
})
}
break
}
break
case 72: // if h or H is pressed
if (e.ctrlKey) {
e.preventDefault()
Control.hideSelectedNodes()
Control.hideSelectedEdges()
}
break
case 77: // if m or M is pressed
if (e.ctrlKey) {
e.preventDefault()
Control.removeSelectedNodes()
Control.removeSelectedEdges()
}
break
case 82: // if r or R is pressed
if (e.altKey && Active.Topic) {
e.preventDefault()
self.centerAndReveal(Selected.Nodes, {
center: false,
reveal: true
})
}
break
case 84: // if t or T is pressed
if (e.altKey && Active.Topic) {
e.preventDefault()
self.centerAndReveal(Selected.Nodes, {
center: true,
reveal: true
})
}
break
default:
// console.log(e.which)
break
}
})
$(window).resize(function () {
if (Visualize && Visualize.mGraph) Visualize.mGraph.canvas.resize($(window).width(), $(window).height())
if (Active.Map && Realtime.inConversation) Realtime.positionVideos()
Mobile.resizeTitle()
})
},
centerAndReveal: function(nodes, opts) {
if (nodes.length < 1) return
var node = nodes[nodes.length - 1]
if (opts.center && opts.reveal) {
Topic.centerOn(node.id, function() {
Topic.fetchRelatives(nodes)
})
} else if (opts.center) {
Topic.centerOn(node.id)
} else if (opts.reveal) {
Topic.fetchRelatives(nodes)
}
}
}
export default Listeners

View file

@ -0,0 +1,27 @@
const CheatSheet = {
init: function () {
// tab the cheatsheet
$('#cheatSheet').tabs()
$('#quickReference').tabs().addClass('ui-tabs-vertical ui-helper-clearfix')
$('#quickReference .ui-tabs-nav li').removeClass('ui-corner-top').addClass('ui-corner-left')
// id = the id of a vimeo video
var switchVideo = function (element, id) {
$('.tutorialItem').removeClass('active')
$(element).addClass('active')
$('#tutorialVideo').attr('src', '//player.vimeo.com/video/' + id)
}
$('#gettingStarted').click(function () {
// switchVideo(this,'88334167')
})
$('#upYourSkillz').click(function () {
// switchVideo(this,'100118167')
})
$('#advancedMapping').click(function () {
// switchVideo(this,'88334167')
})
}
}
export default CheatSheet

View file

@ -0,0 +1,373 @@
/* global Metamaps, $ */
import Active from '../Active'
import GlobalUI from '../GlobalUI'
import Router from '../Router'
import Util from '../Util'
/*
* Metamaps.Collaborators
* Metamaps.Erb
* Metamaps.Mappers
* Metamaps.Maps
* Metamaps.Synapses
* Metamaps.Topics
*/
const InfoBox = {
isOpen: false,
changing: false,
selectingPermission: false,
changePermissionText: "<div class='tooltips'>As the creator, you can change the permission of this map, and the permission of all the topics and synapses you have authority to change will change as well.</div>",
nameHTML: '<span class="best_in_place best_in_place_name" id="best_in_place_map_{{id}}_name" data-url="/maps/{{id}}" data-object="map" data-attribute="name" data-type="textarea" data-activator="#mapInfoName">{{name}}</span>',
descHTML: '<span class="best_in_place best_in_place_desc" id="best_in_place_map_{{id}}_desc" data-url="/maps/{{id}}" data-object="map" data-attribute="desc" data-nil="Click to add description..." data-type="textarea" data-activator="#mapInfoDesc">{{desc}}</span>',
init: function () {
var self = InfoBox
$('.mapInfoIcon').click(self.toggleBox)
$('.mapInfoBox').click(function (event) {
event.stopPropagation()
})
$('body').click(self.close)
self.attachEventListeners()
self.generateBoxHTML = Hogan.compile($('#mapInfoBoxTemplate').html())
var querystring = window.location.search.replace(/^\?/, '')
if (querystring == 'new') {
self.open()
$('.mapInfoBox').addClass('mapRequestTitle')
}
},
toggleBox: function (event) {
var self = InfoBox
if (self.isOpen) self.close()
else self.open()
event.stopPropagation()
},
open: function () {
var self = InfoBox
$('.mapInfoIcon div').addClass('hide')
if (!self.isOpen && !self.changing) {
self.changing = true
$('.mapInfoBox').fadeIn(200, function () {
self.changing = false
self.isOpen = true
})
}
},
close: function () {
var self = InfoBox
$('.mapInfoIcon div').removeClass('hide')
if (!self.changing) {
self.changing = true
$('.mapInfoBox').fadeOut(200, function () {
self.changing = false
self.isOpen = false
self.hidePermissionSelect()
$('.mapContributors .tip').hide()
})
}
},
load: function () {
var self = InfoBox
var map = Active.Map
var obj = map.pick('permission', 'topic_count', 'synapse_count')
var isCreator = map.authorizePermissionChange(Active.Mapper)
var canEdit = map.authorizeToEdit(Active.Mapper)
var relevantPeople = map.get('permission') === 'commons' ? Metamaps.Mappers : Metamaps.Collaborators
var shareable = map.get('permission') !== 'private'
obj['name'] = canEdit ? Hogan.compile(self.nameHTML).render({id: map.id, name: map.get('name')}) : map.get('name')
obj['desc'] = canEdit ? Hogan.compile(self.descHTML).render({id: map.id, desc: map.get('desc')}) : map.get('desc')
obj['map_creator_tip'] = isCreator ? self.changePermissionText : ''
obj['contributor_count'] = relevantPeople.length
obj['contributors_class'] = relevantPeople.length > 1 ? 'multiple' : ''
obj['contributors_class'] += relevantPeople.length === 2 ? ' mTwo' : ''
obj['contributor_image'] = relevantPeople.length > 0 ? relevantPeople.models[0].get('image') : Metamaps.Erb['user.png']
obj['contributor_list'] = self.createContributorList()
obj['user_name'] = isCreator ? 'You' : map.get('user_name')
obj['created_at'] = map.get('created_at_clean')
obj['updated_at'] = map.get('updated_at_clean')
var classes = isCreator ? 'yourMap' : ''
classes += canEdit ? ' canEdit' : ''
classes += shareable ? ' shareable' : ''
$('.mapInfoBox').removeClass('shareable yourMap canEdit')
.addClass(classes)
.html(self.generateBoxHTML.render(obj))
self.attachEventListeners()
},
attachEventListeners: function () {
var self = InfoBox
$('.mapInfoBox.canEdit .best_in_place').best_in_place()
// because anyone who can edit the map can change the map title
var bipName = $('.mapInfoBox .best_in_place_name')
bipName.unbind('best_in_place:activate').bind('best_in_place:activate', function () {
var $el = bipName.find('textarea')
var el = $el[0]
$el.attr('maxlength', '140')
$('.mapInfoName').append('<div class="nameCounter forMap"></div>')
var callback = function (data) {
$('.nameCounter.forMap').html(data.all + '/140')
}
Countable.live(el, callback)
})
bipName.unbind('best_in_place:deactivate').bind('best_in_place:deactivate', function () {
$('.nameCounter.forMap').remove()
})
$('.mapInfoName .best_in_place_name').unbind('ajax:success').bind('ajax:success', function () {
var name = $(this).html()
Active.Map.set('name', name)
Active.Map.trigger('saved')
// mobile menu
$('#header_content').html(name)
$('.mapInfoBox').removeClass('mapRequestTitle')
document.title = name + ' | Metamaps'
})
$('.mapInfoDesc .best_in_place_desc').unbind('ajax:success').bind('ajax:success', function () {
var desc = $(this).html()
Active.Map.set('desc', desc)
Active.Map.trigger('saved')
})
$('.yourMap .mapPermission').unbind().click(self.onPermissionClick)
// .yourMap in the unbind/bind is just a namespace for the events
// not a reference to the class .yourMap on the .mapInfoBox
$('.mapInfoBox.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect)
$('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap)
$('.mapContributors span, #mapContribs').unbind().click(function (event) {
$('.mapContributors .tip').toggle()
event.stopPropagation()
})
$('.mapContributors .tip').unbind().click(function (event) {
event.stopPropagation()
})
$('.mapContributors .tip li a').click(Router.intercept)
$('.mapInfoBox').unbind('.hideTip').bind('click.hideTip', function () {
$('.mapContributors .tip').hide()
})
self.addTypeahead()
},
addTypeahead: function () {
var self = InfoBox
if (!Active.Map) return
// for autocomplete
var collaborators = {
name: 'collaborators',
limit: 9999,
display: function(s) { return s.label; },
templates: {
notFound: function(s) {
return Hogan.compile($('#collaboratorSearchTemplate').html()).render({
value: "No results",
label: "No results",
rtype: "noresult",
profile: Metamaps.Erb['user.png'],
});
},
suggestion: function(s) {
return Hogan.compile($('#collaboratorSearchTemplate').html()).render(s);
},
},
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/mappers?term=%QUERY',
wildcard: '%QUERY',
},
})
}
// for adding map collaborators, who will have edit rights
if (Active.Mapper && Active.Mapper.id === Active.Map.get('user_id')) {
$('.collaboratorSearchField').typeahead(
{
highlight: false,
},
[collaborators]
)
$('.collaboratorSearchField').bind('typeahead:select', self.handleResultClick)
$('.mapContributors .removeCollaborator').click(function () {
self.removeCollaborator(parseInt($(this).data('id')))
})
}
},
removeCollaborator: function (collaboratorId) {
var self = InfoBox
Metamaps.Collaborators.remove(Metamaps.Collaborators.get(collaboratorId))
var mapperIds = Metamaps.Collaborators.models.map(function (mapper) { return mapper.id })
$.post('/maps/' + Active.Map.id + '/access', { access: mapperIds })
self.updateNumbers()
},
addCollaborator: function (newCollaboratorId) {
var self = InfoBox
if (Metamaps.Collaborators.get(newCollaboratorId)) {
GlobalUI.notifyUser('That user already has access')
return
}
function callback(mapper) {
Metamaps.Collaborators.add(mapper)
var mapperIds = Metamaps.Collaborators.models.map(function (mapper) { return mapper.id })
$.post('/maps/' + Active.Map.id + '/access', { access: mapperIds })
var name = Metamaps.Collaborators.get(newCollaboratorId).get('name')
GlobalUI.notifyUser(name + ' will be notified by email')
self.updateNumbers()
}
$.getJSON('/users/' + newCollaboratorId + '.json', callback)
},
handleResultClick: function (event, item) {
var self = InfoBox
self.addCollaborator(item.id)
$('.collaboratorSearchField').typeahead('val', '')
},
updateNameDescPerm: function (name, desc, perm) {
$('.mapInfoBox').removeClass('mapRequestTitle')
$('.mapInfoName .best_in_place_name').html(name)
$('.mapInfoDesc .best_in_place_desc').html(desc)
$('.mapInfoBox .mapPermission').removeClass('commons public private').addClass(perm)
},
createContributorList: function () {
var self = InfoBox
var relevantPeople = Active.Map.get('permission') === 'commons' ? Metamaps.Mappers : Metamaps.Collaborators
var activeMapperIsCreator = Active.Mapper && Active.Mapper.id === Active.Map.get('user_id')
var string = ''
string += '<ul>'
relevantPeople.each(function (m) {
var isCreator = Active.Map.get('user_id') === m.get('id')
string += '<li><a href="/explore/mapper/' + m.get('id') + '">' + '<img class="rtUserImage" width="25" height="25" src="' + m.get('image') + '" />' + m.get('name')
if (isCreator) string += ' (creator)'
string += '</a>'
if (activeMapperIsCreator && !isCreator) string += '<span class="removeCollaborator" data-id="' + m.get('id') + '"></span>'
string += '</li>'
})
string += '</ul>'
if (activeMapperIsCreator) {
string += '<div class="collabSearchField"><span class="addCollab"></span><input class="collaboratorSearchField" placeholder="Add a collaborator!"></input></div>'
}
return string
},
updateNumbers: function () {
if (!Active.Map) return
var self = InfoBox
var mapper = Active.Mapper
var relevantPeople = Active.Map.get('permission') === 'commons' ? Metamaps.Mappers : Metamaps.Collaborators
var contributors_class = ''
if (relevantPeople.length === 2) contributors_class = 'multiple mTwo'
else if (relevantPeople.length > 2) contributors_class = 'multiple'
var contributors_image = Metamaps.Erb['user.png']
if (relevantPeople.length > 0) {
// get the first contributor and use their image
contributors_image = relevantPeople.models[0].get('image')
}
$('.mapContributors img').attr('src', contributors_image).removeClass('multiple mTwo').addClass(contributors_class)
$('.mapContributors span').text(relevantPeople.length)
$('.mapContributors .tip').html(self.createContributorList())
self.addTypeahead()
$('.mapContributors .tip').unbind().click(function (event) {
event.stopPropagation()
})
$('.mapTopics').text(Metamaps.Topics.length)
$('.mapSynapses').text(Metamaps.Synapses.length)
$('.mapEditedAt').html('<span>Last edited: </span>' + Util.nowDateFormatted())
},
onPermissionClick: function (event) {
var self = InfoBox
if (!self.selectingPermission) {
self.selectingPermission = true
$(this).addClass('minimize') // this line flips the drop down arrow to a pull up arrow
if ($(this).hasClass('commons')) {
$(this).append('<ul class="permissionSelect"><li class="public"></li><li class="private"></li></ul>')
} else if ($(this).hasClass('public')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="private"></li></ul>')
} else if ($(this).hasClass('private')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="public"></li></ul>')
}
$('.mapPermission .permissionSelect li').click(self.selectPermission)
event.stopPropagation()
}
},
hidePermissionSelect: function () {
var self = InfoBox
self.selectingPermission = false
$('.mapPermission').removeClass('minimize') // this line flips the pull up arrow to a drop down arrow
$('.mapPermission .permissionSelect').remove()
},
selectPermission: function (event) {
var self = InfoBox
self.selectingPermission = false
var permission = $(this).attr('class')
Active.Map.save({
permission: permission
})
Active.Map.updateMapWrapper()
shareable = permission === 'private' ? '' : 'shareable'
$('.mapPermission').removeClass('commons public private minimize').addClass(permission)
$('.mapPermission .permissionSelect').remove()
$('.mapInfoBox').removeClass('shareable').addClass(shareable)
event.stopPropagation()
},
deleteActiveMap: function () {
var confirmString = 'Are you sure you want to delete this map? '
confirmString += 'This action is irreversible. It will not delete the topics and synapses on the map.'
var doIt = confirm(confirmString)
var map = Active.Map
var mapper = Active.Mapper
var authorized = map.authorizePermissionChange(mapper)
if (doIt && authorized) {
InfoBox.close()
Metamaps.Maps.Active.remove(map)
Metamaps.Maps.Featured.remove(map)
Metamaps.Maps.Mine.remove(map)
Metamaps.Maps.Shared.remove(map)
map.destroy()
Router.home()
GlobalUI.notifyUser('Map eliminated!')
}
else if (!authorized) {
alert("Hey now. We can't just go around willy nilly deleting other people's maps now can we? Run off and find something constructive to do, eh?")
}
}
}
export default InfoBox

View file

@ -0,0 +1,365 @@
/* global Metamaps, $ */
import Active from '../Active'
import AutoLayout from '../AutoLayout'
import Create from '../Create'
import Filter from '../Filter'
import GlobalUI from '../GlobalUI'
import JIT from '../JIT'
import Realtime from '../Realtime'
import Router from '../Router'
import Selected from '../Selected'
import SynapseCard from '../SynapseCard'
import TopicCard from '../TopicCard'
import Visualize from '../Visualize'
import CheatSheet from './CheatSheet'
import InfoBox from './InfoBox'
/*
* Metamaps.Map.js.erb
*
* Dependencies:
* - Metamaps.Backbone
* - Metamaps.Erb
* - Metamaps.Loading
* - Metamaps.Mappers
* - Metamaps.Mappings
* - Metamaps.Maps
* - Metamaps.Messages
* - Metamaps.Synapses
* - Metamaps.Topics
*/
const Map = {
events: {
editedByActiveMapper: 'Metamaps:Map:events:editedByActiveMapper'
},
init: function () {
var self = Map
// prevent right clicks on the main canvas, so as to not get in the way of our right clicks
$('#center-container').bind('contextmenu', function (e) {
return false
})
$('.starMap').click(function () {
if ($(this).is('.starred')) self.unstar()
else self.star()
})
$('.sidebarFork').click(function () {
self.fork()
})
GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html()
self.updateStar()
self.InfoBox.init()
CheatSheet.init()
$(document).on(Map.events.editedByActiveMapper, self.editedByActiveMapper)
},
launch: function (id) {
var bb = Metamaps.Backbone
var start = function (data) {
Active.Map = new bb.Map(data.map)
Metamaps.Mappers = new bb.MapperCollection(data.mappers)
Metamaps.Collaborators = new bb.MapperCollection(data.collaborators)
Metamaps.Topics = new bb.TopicCollection(data.topics)
Metamaps.Synapses = new bb.SynapseCollection(data.synapses)
Metamaps.Mappings = new bb.MappingCollection(data.mappings)
Metamaps.Messages = data.messages
Metamaps.Stars = data.stars
Metamaps.Backbone.attachCollectionEvents()
var map = Active.Map
var mapper = Active.Mapper
// add class to .wrapper for specifying whether you can edit the map
if (map.authorizeToEdit(mapper)) {
$('.wrapper').addClass('canEditMap')
}
// add class to .wrapper for specifying if the map can
// be collaborated on
if (map.get('permission') === 'commons') {
$('.wrapper').addClass('commonsMap')
}
Map.updateStar()
// set filter mapper H3 text
$('#filter_by_mapper h3').html('MAPPERS')
// build and render the visualization
Visualize.type = 'ForceDirected'
JIT.prepareVizData()
// update filters
Filter.reset()
// reset selected arrays
Selected.reset()
// set the proper mapinfobox content
Map.InfoBox.load()
// these three update the actual filter box with the right list items
Filter.checkMetacodes()
Filter.checkSynapses()
Filter.checkMappers()
Realtime.startActiveMap()
Metamaps.Loading.hide()
// for mobile
$('#header_content').html(map.get('name'))
}
$.ajax({
url: '/maps/' + id + '/contains.json',
success: start
})
},
end: function () {
if (Active.Map) {
$('.wrapper').removeClass('canEditMap commonsMap')
AutoLayout.resetSpiral()
$('.rightclickmenu').remove()
TopicCard.hideCard()
SynapseCard.hideCard()
Create.newTopic.hide(true) // true means force (and override pinned)
Create.newSynapse.hide()
Filter.close()
Map.InfoBox.close()
Realtime.endActiveMap()
}
},
updateStar: function () {
if (!Active.Mapper || !Metamaps.Stars) return
// update the star/unstar icon
if (Metamaps.Stars.find(function (s) { return s.user_id === Active.Mapper.id })) {
$('.starMap').addClass('starred')
$('.starMap .tooltipsAbove').html('Unstar')
} else {
$('.starMap').removeClass('starred')
$('.starMap .tooltipsAbove').html('Star')
}
},
star: function () {
var self = Map
if (!Active.Map) return
$.post('/maps/' + Active.Map.id + '/star')
Metamaps.Stars.push({ user_id: Active.Mapper.id, map_id: Active.Map.id })
Metamaps.Maps.Starred.add(Active.Map)
GlobalUI.notifyUser('Map is now starred')
self.updateStar()
},
unstar: function () {
var self = Map
if (!Active.Map) return
$.post('/maps/' + Active.Map.id + '/unstar')
Metamaps.Stars = Metamaps.Stars.filter(function (s) { return s.user_id != Active.Mapper.id })
Metamaps.Maps.Starred.remove(Active.Map)
self.updateStar()
},
fork: function () {
GlobalUI.openLightbox('forkmap')
var nodes_data = '',
synapses_data = ''
var nodes_array = []
var synapses_array = []
// collect the unfiltered topics
Visualize.mGraph.graph.eachNode(function (n) {
// if the opacity is less than 1 then it's filtered
if (n.getData('alpha') === 1) {
var id = n.getData('topic').id
nodes_array.push(id)
var x, y
if (n.pos.x && n.pos.y) {
x = n.pos.x
y = n.pos.y
} else {
var x = Math.cos(n.pos.theta) * n.pos.rho
var y = Math.sin(n.pos.theta) * n.pos.rho
}
nodes_data += id + '/' + x + '/' + y + ','
}
})
// collect the unfiltered synapses
Metamaps.Synapses.each(function (synapse) {
var desc = synapse.get('desc')
var descNotFiltered = Filter.visible.synapses.indexOf(desc) > -1
// make sure that both topics are being added, otherwise, it
// doesn't make sense to add the synapse
var topicsNotFiltered = nodes_array.indexOf(synapse.get('node1_id')) > -1
topicsNotFiltered = topicsNotFiltered && nodes_array.indexOf(synapse.get('node2_id')) > -1
if (descNotFiltered && topicsNotFiltered) {
synapses_array.push(synapse.id)
}
})
synapses_data = synapses_array.join()
nodes_data = nodes_data.slice(0, -1)
GlobalUI.CreateMap.topicsToMap = nodes_data
GlobalUI.CreateMap.synapsesToMap = synapses_data
},
leavePrivateMap: function () {
var map = Active.Map
Metamaps.Maps.Active.remove(map)
Metamaps.Maps.Featured.remove(map)
Router.home()
GlobalUI.notifyUser('Sorry! That map has been changed to Private.')
},
cantEditNow: function () {
Realtime.turnOff(true); // true is for 'silence'
GlobalUI.notifyUser('Map was changed to Public. Editing is disabled.')
Active.Map.trigger('changeByOther')
},
canEditNow: function () {
var confirmString = "You've been granted permission to edit this map. "
confirmString += 'Do you want to reload and enable realtime collaboration?'
var c = confirm(confirmString)
if (c) {
Router.maps(Active.Map.id)
}
},
editedByActiveMapper: function () {
if (Active.Mapper) {
Metamaps.Mappers.add(Active.Mapper)
}
},
exportImage: function () {
var canvas = {}
canvas.canvas = document.createElement('canvas')
canvas.canvas.width = 1880 // 960
canvas.canvas.height = 1260 // 630
canvas.scaleOffsetX = 1
canvas.scaleOffsetY = 1
canvas.translateOffsetY = 0
canvas.translateOffsetX = 0
canvas.denySelected = true
canvas.getSize = function () {
if (this.size) return this.size
var canvas = this.canvas
return this.size = {
width: canvas.width,
height: canvas.height
}
}
canvas.scale = function (x, y) {
var px = this.scaleOffsetX * x,
py = this.scaleOffsetY * y
var dx = this.translateOffsetX * (x - 1) / px,
dy = this.translateOffsetY * (y - 1) / py
this.scaleOffsetX = px
this.scaleOffsetY = py
this.getCtx().scale(x, y)
this.translate(dx, dy)
}
canvas.translate = function (x, y) {
var sx = this.scaleOffsetX,
sy = this.scaleOffsetY
this.translateOffsetX += x * sx
this.translateOffsetY += y * sy
this.getCtx().translate(x, y)
}
canvas.getCtx = function () {
return this.canvas.getContext('2d')
}
// center it
canvas.getCtx().translate(1880 / 2, 1260 / 2)
var mGraph = Visualize.mGraph
var id = mGraph.root
var root = mGraph.graph.getNode(id)
var T = !!root.visited
// pass true to avoid basing it on a selection
JIT.zoomExtents(null, canvas, true)
var c = canvas.canvas,
ctx = canvas.getCtx(),
scale = canvas.scaleOffsetX
// draw a grey background
ctx.fillStyle = '#d8d9da'
var xPoint = (-(c.width / scale) / 2) - (canvas.translateOffsetX / scale),
yPoint = (-(c.height / scale) / 2) - (canvas.translateOffsetY / scale)
ctx.fillRect(xPoint, yPoint, c.width / scale, c.height / scale)
// draw the graph
mGraph.graph.eachNode(function (node) {
var nodeAlpha = node.getData('alpha')
node.eachAdjacency(function (adj) {
var nodeTo = adj.nodeTo
if (!!nodeTo.visited === T && node.drawn && nodeTo.drawn) {
mGraph.fx.plotLine(adj, canvas)
}
})
if (node.drawn) {
mGraph.fx.plotNode(node, canvas)
}
if (!mGraph.labelsHidden) {
if (node.drawn && nodeAlpha >= 0.95) {
mGraph.labels.plotLabel(canvas, node)
} else {
mGraph.labels.hideLabel(node, false)
}
}
node.visited = !T
})
var imageData = {
encoded_image: canvas.canvas.toDataURL()
}
var map = Active.Map
var today = new Date()
var dd = today.getDate()
var mm = today.getMonth() + 1; // January is 0!
var yyyy = today.getFullYear()
if (dd < 10) {
dd = '0' + dd
}
if (mm < 10) {
mm = '0' + mm
}
today = mm + '/' + dd + '/' + yyyy
var mapName = map.get('name').split(' ').join([separator = '-'])
var downloadMessage = ''
downloadMessage += 'Captured map screenshot! '
downloadMessage += "<a href='" + imageData.encoded_image + "' "
downloadMessage += "download='metamap-" + map.id + '-' + mapName + '-' + today + ".png'>DOWNLOAD</a>"
GlobalUI.notifyUser(downloadMessage)
$.ajax({
type: 'POST',
dataType: 'json',
url: '/maps/' + Active.Map.id + '/upload_screenshot',
data: imageData,
success: function (data) {
console.log('successfully uploaded map screenshot')
},
error: function () {
console.log('failed to save map screenshot')
}
})
}
}
export { CheatSheet, InfoBox }
export default Map

View file

@ -0,0 +1,19 @@
/*
* Metamaps.Backbone
*/
const Mapper = {
// this function is to retrieve a mapper JSON object from the database
// @param id = the id of the mapper to retrieve
get: function (id, callback) {
return fetch(`/users/${id}.json`, {
}).then(response => {
if (!response.ok) throw response
return response.json()
}).then(payload => {
callback(new Metamaps.Backbone.Mapper(payload))
})
}
}
export default Mapper

View file

@ -1,16 +1,11 @@
/* global Metamaps, $ */
/* global $ */
/*
* Metamaps.Mobile.js
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.Map
*/
import Active from './Active'
import Map from './Map'
Metamaps.Mobile = {
const Mobile = {
init: function () {
var self = Metamaps.Mobile
var self = Mobile
$('#menu_icon').click(self.toggleMenu)
$('#mobile_menu li a').click(self.liClick)
@ -22,7 +17,7 @@ Metamaps.Mobile = {
$('#header_content').width($(document).width() - 70)
},
liClick: function () {
var self = Metamaps.Mobile
var self = Mobile
$('#header_content').html($(this).text())
self.toggleMenu()
},
@ -30,8 +25,10 @@ Metamaps.Mobile = {
$('#mobile_menu').toggle()
},
titleClick: function () {
if (Metamaps.Active.Map) {
Metamaps.Map.InfoBox.open()
if (Active.Map) {
Map.InfoBox.open()
}
}
}
export default Mobile

View file

@ -0,0 +1,16 @@
const 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
}
export default Mouse

View file

@ -1,22 +1,22 @@
/* global Metamaps, $ */
/* global $ */
/*
* Metamaps.Organize.js.erb
*
* Dependencies:
* - Metamaps.Visualize
*/
Metamaps.Organize = {
init: function () {},
import _ from 'lodash'
import $jit from '../patched/JIT'
import Visualize from './Visualize'
import JIT from './JIT'
const Organize = {
arrange: function (layout, centerNode) {
// first option for layout to implement is 'grid', will do an evenly spaced grid with its center at the 0,0 origin
if (layout == 'grid') {
var numNodes = _.size(Metamaps.Visualize.mGraph.graph.nodes); // this will always be an integer, the # of nodes on your graph visualization
var numNodes = _.size(Visualize.mGraph.graph.nodes); // this will always be an integer, the # of nodes on your graph visualization
var numColumns = Math.floor(Math.sqrt(numNodes)) // the number of columns to make an even grid
var GRIDSPACE = 400
var row = 0
var column = 0
Metamaps.Visualize.mGraph.graph.eachNode(function (n) {
Visualize.mGraph.graph.eachNode(function (n) {
if (column == numColumns) {
column = 0
row += 1
@ -27,14 +27,14 @@ Metamaps.Organize = {
n.setPos(newPos, 'end')
column += 1
})
Metamaps.Visualize.mGraph.animate(Metamaps.JIT.ForceDirected.animateSavedLayout)
Visualize.mGraph.animate(JIT.ForceDirected.animateSavedLayout)
} else if (layout == 'grid_full') {
// this will always be an integer, the # of nodes on your graph visualization
var numNodes = _.size(Metamaps.Visualize.mGraph.graph.nodes)
var numNodes = _.size(Visualize.mGraph.graph.nodes)
// var numColumns = Math.floor(Math.sqrt(numNodes)) // the number of columns to make an even grid
// var GRIDSPACE = 400
var height = Metamaps.Visualize.mGraph.canvas.getSize(0).height
var width = Metamaps.Visualize.mGraph.canvas.getSize(0).width
var height = Visualize.mGraph.canvas.getSize(0).height
var width = Visualize.mGraph.canvas.getSize(0).width
var totalArea = height * width
var cellArea = totalArea / numNodes
var ratio = height / width
@ -45,7 +45,7 @@ Metamaps.Organize = {
var totalCells = row * column
if (totalCells)
Metamaps.Visualize.mGraph.graph.eachNode(function (n) {
Visualize.mGraph.graph.eachNode(function (n) {
if (column == numColumns) {
column = 0
row += 1
@ -56,7 +56,7 @@ Metamaps.Organize = {
n.setPos(newPos, 'end')
column += 1
})
Metamaps.Visualize.mGraph.animate(Metamaps.JIT.ForceDirected.animateSavedLayout)
Visualize.mGraph.animate(JIT.ForceDirected.animateSavedLayout)
} else if (layout == 'radial') {
var centerX = centerNode.getPos().x
var centerY = centerNode.getPos().y
@ -88,16 +88,16 @@ Metamaps.Organize = {
})
}
radial(centerNode, 1, 0)
Metamaps.Visualize.mGraph.animate(Metamaps.JIT.ForceDirected.animateSavedLayout)
Visualize.mGraph.animate(JIT.ForceDirected.animateSavedLayout)
} else if (layout == 'center_viewport') {
var lowX = 0,
lowY = 0,
highX = 0,
highY = 0
var oldOriginX = Metamaps.Visualize.mGraph.canvas.translateOffsetX
var oldOriginY = Metamaps.Visualize.mGraph.canvas.translateOffsetY
var oldOriginX = Visualize.mGraph.canvas.translateOffsetX
var oldOriginY = Visualize.mGraph.canvas.translateOffsetY
Metamaps.Visualize.mGraph.graph.eachNode(function (n) {
Visualize.mGraph.graph.eachNode(function (n) {
if (n.id === 1) {
lowX = n.getPos().x
lowY = n.getPos().y
@ -114,4 +114,6 @@ Metamaps.Organize = {
var newOriginY = (lowY + highY) / 2
} else alert('please call function with a valid layout dammit!')
}
}; // end Metamaps.Organize
}
export default Organize

View file

@ -1,19 +1,16 @@
/* global Metamaps, $ */
/* global $ */
/*
* Metamaps.PasteInput.js.erb
*
* Dependencies:
* - Metamaps.Import
* - Metamaps.AutoLayout
*/
import AutoLayout from './AutoLayout'
import Import from './Import'
import TopicCard from './TopicCard'
import Util from './Util'
Metamaps.PasteInput = {
const PasteInput = {
// thanks to https://github.com/kevva/url-regex
URL_REGEX: new RegExp('^(?:(?:(?:[a-z]+:)?//)|www\.)(?:\S+(?::\S*)?@)?(?:localhost|(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])){3}|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#][^\s"]*)?$'),
init: function () {
var self = Metamaps.PasteInput
var self = PasteInput
// intercept dragged files
// see http://stackoverflow.com/questions/6756583
@ -24,7 +21,7 @@ Metamaps.PasteInput = {
window.addEventListener("drop", function(e) {
e = e || event;
e.preventDefault();
var coords = Metamaps.Util.pixelsToCoords({ x: e.clientX, y: e.clientY })
var coords = Util.pixelsToCoords({ x: e.clientX, y: e.clientY })
if (e.dataTransfer.files.length > 0) {
var fileReader = new FileReader()
var text = fileReader.readAsText(e.dataTransfer.files[0])
@ -58,7 +55,7 @@ Metamaps.PasteInput = {
},
handle: function(text, coords) {
var self = Metamaps.PasteInput
var self = PasteInput
if (text.match(self.URL_REGEX)) {
self.handleURL(text, coords)
@ -74,13 +71,13 @@ Metamaps.PasteInput = {
handleURL: function (text, coords) {
var title = 'Link'
if (!coords || !coords.x || !coords.y) {
coords = Metamaps.AutoLayout.getNextCoord()
coords = AutoLayout.getNextCoord()
}
var import_id = null // don't store a cidMapping
var permission = null // use default
Metamaps.Import.createTopicWithParameters(
Import.createTopicWithParameters(
title,
'Reference', // metacode - todo fix
permission,
@ -91,7 +88,7 @@ Metamaps.PasteInput = {
import_id,
{
success: function(topic) {
Metamaps.TopicCard.showCard(topic.get('node'), function() {
TopicCard.showCard(topic.get('node'), function() {
$('#showcard #titleActivator').click()
.find('textarea, input').focus()
})
@ -101,10 +98,12 @@ Metamaps.PasteInput = {
},
handleJSON: function (text) {
Metamaps.Import.handleJSON(text)
Import.handleJSON(text)
},
handleTSV: function (text) {
Metamaps.Import.handleTSV(text)
Import.handleTSV(text)
}
}
export default PasteInput

View file

@ -0,0 +1,7 @@
import Maps from '../components/Maps'
const ReactComponents = {
Maps
}
export default ReactComponents

View file

@ -0,0 +1,252 @@
/* global Metamaps, $ */
import Backbone from 'backbone'
//TODO is this line good or bad?
//Backbone.$ = window.$
import Active from './Active'
import GlobalUI from './GlobalUI'
import JIT from './JIT'
import Map from './Map'
import Topic from './Topic'
import Views from './Views'
import Visualize from './Visualize'
/*
* Metamaps.Router.js.erb
*
* Dependencies:
* - Metamaps.Loading
* - Metamaps.Maps
*/
const _Router = Backbone.Router.extend({
currentPage: '',
currentSection: '',
timeoutId: undefined,
routes: {
'': 'home', // #home
'explore/:section': 'explore', // #explore/active
'explore/:section/:id': 'explore', // #explore/mapper/1234
'maps/:id': 'maps' // #maps/7
},
home: function () {
let self = this
clearTimeout(this.timeoutId)
if (Active.Mapper) document.title = 'Explore Active Maps | Metamaps'
else document.title = 'Home | Metamaps'
this.currentSection = ''
this.currentPage = ''
$('.wrapper').removeClass('mapPage topicPage')
var classes = Active.Mapper ? 'homePage explorePage' : 'homePage'
$('.wrapper').addClass(classes)
var navigate = function () {
self.timeoutId = setTimeout(function () {
self.navigate('')
}, 300)
}
// all this only for the logged in home page
if (Active.Mapper) {
$('.homeButton a').attr('href', '/')
GlobalUI.hideDiv('#yield')
GlobalUI.showDiv('#explore')
Views.ExploreMaps.setCollection(Metamaps.Maps.Active)
if (Metamaps.Maps.Active.length === 0) {
Metamaps.Maps.Active.getMaps(navigate) // this will trigger an explore maps render
} else {
Views.ExploreMaps.render(navigate)
}
} else {
// logged out home page
GlobalUI.hideDiv('#explore')
GlobalUI.showDiv('#yield')
this.timeoutId = setTimeout(navigate, 500)
}
GlobalUI.hideDiv('#infovis')
GlobalUI.hideDiv('#instructions')
Map.end()
Topic.end()
Active.Map = null
Active.Topic = null
},
explore: function (section, id) {
var self = this
clearTimeout(this.timeoutId)
// just capitalize the variable section
// either 'featured', 'mapper', or 'active'
var capitalize = section.charAt(0).toUpperCase() + section.slice(1)
if (section === 'shared' || section === 'featured' || section === 'active' || section === 'starred') {
document.title = 'Explore ' + capitalize + ' Maps | Metamaps'
} else if (section === 'mapper') {
$.ajax({
url: '/users/' + id + '.json',
success: function (response) {
document.title = response.name + ' | Metamaps'
},
error: function () {}
})
} else if (section === 'mine') {
document.title = 'Explore My Maps | Metamaps'
}
if (Active.Mapper && section != 'mapper') $('.homeButton a').attr('href', '/explore/' + section)
$('.wrapper').removeClass('homePage mapPage topicPage')
$('.wrapper').addClass('explorePage')
this.currentSection = 'explore'
this.currentPage = section
// this will mean it's a mapper page being loaded
if (id) {
if (Metamaps.Maps.Mapper.mapperId !== id) {
// empty the collection if we are trying to load the maps
// collection of a different mapper than we had previously
Metamaps.Maps.Mapper.reset()
Metamaps.Maps.Mapper.page = 1
}
Metamaps.Maps.Mapper.mapperId = id
}
Views.ExploreMaps.setCollection(Metamaps.Maps[capitalize])
var navigate = function () {
var path = '/explore/' + self.currentPage
// alter url if for mapper profile page
if (self.currentPage === 'mapper') {
path += '/' + Metamaps.Maps.Mapper.mapperId
}
self.navigate(path)
}
var navigateTimeout = function () {
self.timeoutId = setTimeout(navigate, 300)
}
if (Metamaps.Maps[capitalize].length === 0) {
Metamaps.Loading.show()
setTimeout(function () {
Metamaps.Maps[capitalize].getMaps(navigate) // this will trigger an explore maps render
}, 300) // wait 300 milliseconds till the other animations are done to do the fetch
} else {
if (id) {
Views.ExploreMaps.fetchUserThenRender(navigateTimeout)
} else {
Views.ExploreMaps.render(navigateTimeout)
}
}
GlobalUI.showDiv('#explore')
GlobalUI.hideDiv('#yield')
GlobalUI.hideDiv('#infovis')
GlobalUI.hideDiv('#instructions')
Map.end()
Topic.end()
Active.Map = null
Active.Topic = null
},
maps: function (id) {
clearTimeout(this.timeoutId)
document.title = 'Map ' + id + ' | Metamaps'
this.currentSection = 'map'
this.currentPage = id
$('.wrapper').removeClass('homePage explorePage topicPage')
$('.wrapper').addClass('mapPage')
// another class will be added to wrapper if you
// can edit this map '.canEditMap'
GlobalUI.hideDiv('#yield')
GlobalUI.hideDiv('#explore')
// clear the visualization, if there was one, before showing its div again
if (Visualize.mGraph) {
Visualize.mGraph.graph.empty()
Visualize.mGraph.plot()
JIT.centerMap(Visualize.mGraph.canvas)
}
GlobalUI.showDiv('#infovis')
Topic.end()
Active.Topic = null
Metamaps.Loading.show()
Map.end()
Map.launch(id)
},
topics: function (id) {
clearTimeout(this.timeoutId)
document.title = 'Topic ' + id + ' | Metamaps'
this.currentSection = 'topic'
this.currentPage = id
$('.wrapper').removeClass('homePage explorePage mapPage')
$('.wrapper').addClass('topicPage')
GlobalUI.hideDiv('#yield')
GlobalUI.hideDiv('#explore')
// clear the visualization, if there was one, before showing its div again
if (Visualize.mGraph) {
Visualize.mGraph.graph.empty()
Visualize.mGraph.plot()
JIT.centerMap(Visualize.mGraph.canvas)
}
GlobalUI.showDiv('#infovis')
Map.end()
Active.Map = null
Topic.end()
Topic.launch(id)
}
})
const Router = new _Router()
Router.intercept = function (evt) {
var segments
var href = {
prop: $(this).prop('href'),
attr: $(this).attr('href')
}
var root = window.location.protocol + '//' + window.location.host + Backbone.history.options.root
if (href.prop && href.prop === root) href.attr = ''
if (href.prop && href.prop.slice(0, root.length) === root) {
evt.preventDefault()
segments = href.attr.split('/')
segments.splice(0, 1) // pop off the element created by the first /
if (href.attr === '') {
Router.home()
} else {
Router[segments[0]](segments[1], segments[2])
}
}
}
Router.init = function () {
Backbone.history.start({
silent: true,
pushState: true,
root: '/'
})
$(document).on('click', 'a[data-router="true"]', Router.intercept)
}
export default Router

View file

@ -0,0 +1,11 @@
const Selected = {
reset: function () {
var self = Metamaps.Selected
self.Nodes = []
self.Edges = []
},
Nodes: [],
Edges: []
}
export default Selected

View file

@ -0,0 +1,21 @@
const 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'
}
},
}
export default Settings

View file

@ -1,23 +1,25 @@
/* global Metamaps, $ */
import Active from './Active'
import Control from './Control'
import Create from './Create'
import JIT from './JIT'
import Map from './Map'
import Selected from './Selected'
import Settings from './Settings'
import Visualize from './Visualize'
/*
* Metamaps.Synapse.js.erb
*
* Dependencies:
* - Metamaps.Backbone
* - Metamaps.Control
* - Metamaps.Create
* - Metamaps.JIT
* - Metamaps.Map
* - Metamaps.Mappings
* - Metamaps.Selected
* - Metamaps.Settings
* - Metamaps.Synapses
* - Metamaps.Topics
* - Metamaps.Visualize
*/
Metamaps.Synapse = {
const Synapse = {
// this function is to retrieve a synapse JSON object from the database
// @param id = the id of the synapse to retrieve
get: function (id, callback) {
@ -52,18 +54,18 @@ Metamaps.Synapse = {
*
*/
renderSynapse: function (mapping, synapse, node1, node2, createNewInDB) {
var self = Metamaps.Synapse
var self = Synapse
var edgeOnViz
var newedge = synapse.createEdge(mapping)
Metamaps.Visualize.mGraph.graph.addAdjacence(node1, node2, newedge.data)
edgeOnViz = Metamaps.Visualize.mGraph.graph.getAdjacence(node1.id, node2.id)
Visualize.mGraph.graph.addAdjacence(node1, node2, newedge.data)
edgeOnViz = Visualize.mGraph.graph.getAdjacence(node1.id, node2.id)
synapse.set('edge', edgeOnViz)
synapse.updateEdge() // links the synapse and the mapping to the edge
Metamaps.Control.selectEdge(edgeOnViz)
Control.selectEdge(edgeOnViz)
var mappingSuccessCallback = function (mappingModel, response) {
var newSynapseData = {
@ -71,17 +73,17 @@ Metamaps.Synapse = {
mappableid: mappingModel.get('mappable_id')
}
$(document).trigger(Metamaps.JIT.events.newSynapse, [newSynapseData])
$(document).trigger(JIT.events.newSynapse, [newSynapseData])
}
var synapseSuccessCallback = function (synapseModel, response) {
if (Metamaps.Active.Map) {
if (Active.Map) {
mapping.save({ mappable_id: synapseModel.id }, {
success: mappingSuccessCallback
})
}
}
if (!Metamaps.Settings.sandbox && createNewInDB) {
if (!Settings.sandbox && createNewInDB) {
if (synapse.isNew()) {
synapse.save(null, {
success: synapseSuccessCallback,
@ -89,7 +91,7 @@ Metamaps.Synapse = {
console.log('error saving synapse to database')
}
})
} else if (!synapse.isNew() && Metamaps.Active.Map) {
} else if (!synapse.isNew() && Active.Map) {
mapping.save(null, {
success: mappingSuccessCallback
})
@ -97,7 +99,7 @@ Metamaps.Synapse = {
}
},
createSynapseLocally: function () {
var self = Metamaps.Synapse,
var self = Synapse,
topic1,
topic2,
node1,
@ -105,27 +107,27 @@ Metamaps.Synapse = {
synapse,
mapping
$(document).trigger(Metamaps.Map.events.editedByActiveMapper)
$(document).trigger(Map.events.editedByActiveMapper)
// for each node in this array we will create a synapse going to the position2 node.
var synapsesToCreate = []
topic2 = Metamaps.Topics.get(Metamaps.Create.newSynapse.topic2id)
topic2 = Metamaps.Topics.get(Create.newSynapse.topic2id)
node2 = topic2.get('node')
var len = Metamaps.Selected.Nodes.length
var len = Selected.Nodes.length
if (len == 0) {
topic1 = Metamaps.Topics.get(Metamaps.Create.newSynapse.topic1id)
topic1 = Metamaps.Topics.get(Create.newSynapse.topic1id)
synapsesToCreate[0] = topic1.get('node')
} else if (len > 0) {
synapsesToCreate = Metamaps.Selected.Nodes
synapsesToCreate = Selected.Nodes
}
for (var i = 0; i < synapsesToCreate.length; i++) {
node1 = synapsesToCreate[i]
topic1 = node1.getData('topic')
synapse = new Metamaps.Backbone.Synapse({
desc: Metamaps.Create.newSynapse.description,
desc: Create.newSynapse.description,
node1_id: topic1.isNew() ? topic1.cid : topic1.id,
node2_id: topic2.isNew() ? topic2.cid : topic2.id,
})
@ -141,10 +143,10 @@ Metamaps.Synapse = {
self.renderSynapse(mapping, synapse, node1, node2, true)
} // for each in synapsesToCreate
Metamaps.Create.newSynapse.hide()
Create.newSynapse.hide()
},
getSynapseFromAutocomplete: function (id) {
var self = Metamaps.Synapse,
var self = Synapse,
topic1,
topic2,
node1,
@ -158,12 +160,14 @@ Metamaps.Synapse = {
})
Metamaps.Mappings.add(mapping)
topic1 = Metamaps.Topics.get(Metamaps.Create.newSynapse.topic1id)
topic1 = Metamaps.Topics.get(Create.newSynapse.topic1id)
node1 = topic1.get('node')
topic2 = Metamaps.Topics.get(Metamaps.Create.newSynapse.topic2id)
topic2 = Metamaps.Topics.get(Create.newSynapse.topic2id)
node2 = topic2.get('node')
Metamaps.Create.newSynapse.hide()
Create.newSynapse.hide()
self.renderSynapse(mapping, synapse, node1, node2, true)
}
}; // end Metamaps.Synapse
}
export default Synapse

View file

@ -1,18 +1,13 @@
/* global Metamaps, $ */
/* global $ */
import Active from './Active'
import Control from './Control'
import Mapper from './Mapper'
import Visualize from './Visualize'
/*
* Metamaps.SynapseCard.js
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.Control
* - Metamaps.Mapper
* - Metamaps.Visualize
*/
Metamaps.SynapseCard = {
const SynapseCard = {
openSynapseCard: null,
showCard: function (edge, e) {
var self = Metamaps.SynapseCard
var self = SynapseCard
// reset so we don't interfere with other edges, but first, save its x and y
var myX = $('#edit_synapse').css('left')
@ -20,7 +15,7 @@ Metamaps.SynapseCard = {
$('#edit_synapse').remove()
// so label is missing while editing
Metamaps.Control.deselectEdge(edge)
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
@ -30,9 +25,9 @@ Metamaps.SynapseCard = {
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)) {
if (synapse.authorizeToEdit(Active.Mapper)) {
edit_div.className = 'permission canEdit'
edit_div.className += synapse.authorizePermissionChange(Metamaps.Active.Mapper) ? ' yourEdge' : ''
edit_div.className += synapse.authorizePermissionChange(Active.Mapper) ? ' yourEdge' : ''
} else {
edit_div.className = 'permission cannotEdit'
}
@ -58,11 +53,11 @@ Metamaps.SynapseCard = {
hideCard: function () {
$('#edit_synapse').remove()
Metamaps.SynapseCard.openSynapseCard = null
SynapseCard.openSynapseCard = null
},
populateShowCard: function (edge, synapse) {
var self = Metamaps.SynapseCard
var self = SynapseCard
self.add_synapse_count(edge)
self.add_desc_form(synapse)
@ -94,7 +89,7 @@ Metamaps.SynapseCard = {
// 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)) {
if (synapse.authorizeToEdit(Active.Mapper)) {
$('#edit_synapse_desc').html(data_nil)
} else {
$('#edit_synapse_desc').html('(no description)')
@ -109,8 +104,8 @@ Metamaps.SynapseCard = {
synapse.set('desc', desc)
}
synapse.trigger('saved')
Metamaps.Control.selectEdge(synapse.get('edge'))
Metamaps.Visualize.mGraph.plot()
Control.selectEdge(synapse.get('edge'))
Visualize.mGraph.plot()
})
},
add_drop_down: function (edge, synapse) {
@ -152,8 +147,8 @@ Metamaps.SynapseCard = {
e.stopPropagation()
var index = parseInt($(this).attr('data-synapse-index'))
edge.setData('displayIndex', index)
Metamaps.Visualize.mGraph.plot()
Metamaps.SynapseCard.showCard(edge, false)
Visualize.mGraph.plot()
SynapseCard.showCard(edge, false)
})
}
},
@ -167,7 +162,7 @@ Metamaps.SynapseCard = {
var setMapperImage = function (mapper) {
$('#edgeUser img').attr('src', mapper.get('image'))
}
Metamaps.Mapper.get(synapse.get('user_id'), setMapperImage)
Mapper.get(synapse.get('user_id'), setMapperImage)
},
add_perms_form: function (synapse) {
@ -210,7 +205,7 @@ Metamaps.SynapseCard = {
$('#edit_synapse .permissionSelect').remove()
}
if (synapse.authorizePermissionChange(Metamaps.Active.Mapper)) {
if (synapse.authorizePermissionChange(Active.Mapper)) {
$('#edit_synapse.yourEdge .mapPerm').click(openPermissionSelect)
$('#edit_synapse').click(hidePermissionSelect)
}
@ -257,7 +252,7 @@ Metamaps.SynapseCard = {
$('#edit_synapse_right').addClass('checked')
}
if (synapse.authorizeToEdit(Metamaps.Active.Mapper)) {
if (synapse.authorizeToEdit(Active.Mapper)) {
$('#edit_synapse_left, #edit_synapse_right').click(function () {
$(this).toggleClass('checked')
@ -281,8 +276,10 @@ Metamaps.SynapseCard = {
node1_id: dir[0],
node2_id: dir[1]
})
Metamaps.Visualize.mGraph.plot()
Visualize.mGraph.plot()
})
} // if
} // add_direction_form
}; // end Metamaps.SynapseCard
}
export default SynapseCard

View file

@ -1,33 +1,35 @@
/* global Metamaps, $ */
import $jit from '../patched/JIT'
import Active from './Active'
import AutoLayout from './AutoLayout'
import Create from './Create'
import Filter from './Filter'
import GlobalUI from './GlobalUI'
import JIT from './JIT'
import Map from './Map'
import Router from './Router'
import Selected from './Selected'
import Settings from './Settings'
import SynapseCard from './SynapseCard'
import TopicCard from './TopicCard'
import Util from './Util'
import Visualize from './Visualize'
/*
* Metamaps.Topic.js.erb
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.Backbone
* - Metamaps.Backbone
* - Metamaps.Create
* - Metamaps.Creators
* - Metamaps.Famous
* - Metamaps.Filter
* - Metamaps.GlobalUI
* - Metamaps.JIT
* - Metamaps.Mappings
* - Metamaps.Selected
* - Metamaps.Settings
* - Metamaps.SynapseCard
* - Metamaps.Synapses
* - Metamaps.TopicCard
* - Metamaps.Topics
* - Metamaps.Util
* - Metamaps.Visualize
* - Metamaps.tempInit
* - Metamaps.tempNode
* - Metamaps.tempNode2
*/
Metamaps.Topic = {
const Topic = {
// this function is to retrieve a topic JSON object from the database
// @param id = the id of the topic to retrieve
get: function (id, callback) {
@ -61,7 +63,7 @@ Metamaps.Topic = {
launch: function (id) {
var bb = Metamaps.Backbone
var start = function (data) {
Metamaps.Active.Topic = new bb.Topic(data.topic)
Active.Topic = new bb.Topic(data.topic)
Metamaps.Creators = new bb.MapperCollection(data.creators)
Metamaps.Topics = new bb.TopicCollection([data.topic].concat(data.relatives))
Metamaps.Synapses = new bb.SynapseCollection(data.synapses)
@ -71,22 +73,22 @@ Metamaps.Topic = {
$('#filter_by_mapper h3').html('CREATORS')
// build and render the visualization
Metamaps.Visualize.type = 'RGraph'
Metamaps.JIT.prepareVizData()
Visualize.type = 'RGraph'
JIT.prepareVizData()
// update filters
Metamaps.Filter.reset()
Filter.reset()
// reset selected arrays
Metamaps.Selected.reset()
Selected.reset()
// these three update the actual filter box with the right list items
Metamaps.Filter.checkMetacodes()
Metamaps.Filter.checkSynapses()
Metamaps.Filter.checkMappers()
Filter.checkMetacodes()
Filter.checkSynapses()
Filter.checkMappers()
// for mobile
$('#header_content').html(Metamaps.Active.Topic.get('name'))
$('#header_content').html(Active.Topic.get('name'))
}
$.ajax({
@ -95,25 +97,25 @@ Metamaps.Topic = {
})
},
end: function () {
if (Metamaps.Active.Topic) {
if (Active.Topic) {
$('.rightclickmenu').remove()
Metamaps.TopicCard.hideCard()
Metamaps.SynapseCard.hideCard()
Metamaps.Filter.close()
TopicCard.hideCard()
SynapseCard.hideCard()
Filter.close()
}
},
centerOn: function (nodeid, callback) {
// don't clash with fetchRelatives
if (!Metamaps.Visualize.mGraph.busy) {
Metamaps.Visualize.mGraph.onClick(nodeid, {
if (!Visualize.mGraph.busy) {
Visualize.mGraph.onClick(nodeid, {
hideLabels: false,
duration: 1000,
onComplete: function () {
if (callback) callback()
}
})
Metamaps.Router.navigate('/topics/' + nodeid)
Metamaps.Active.Topic = Metamaps.Topics.get(nodeid)
Router.navigate('/topics/' + nodeid)
Active.Topic = Metamaps.Topics.get(nodeid)
}
},
fetchRelatives: function (nodes, metacode_id) {
@ -131,7 +133,7 @@ Metamaps.Topic = {
var successCallback;
successCallback = function (data) {
if (Metamaps.Visualize.mGraph.busy) {
if (Visualize.mGraph.busy) {
// don't clash with centerOn
window.setTimeout(function() { successCallback(data) }, 100)
return
@ -144,8 +146,8 @@ Metamaps.Topic = {
topicColl.add(topic)
var synapseColl = new Metamaps.Backbone.SynapseCollection(data.synapses)
var graph = Metamaps.JIT.convertModelsToJIT(topicColl, synapseColl)[0]
Metamaps.Visualize.mGraph.op.sum(graph, {
var graph = JIT.convertModelsToJIT(topicColl, synapseColl)[0]
Visualize.mGraph.op.sum(graph, {
type: 'fade',
duration: 500,
hideLabels: false
@ -153,7 +155,7 @@ Metamaps.Topic = {
var i, l, t, s
Metamaps.Visualize.mGraph.graph.eachNode(function (n) {
Visualize.mGraph.graph.eachNode(function (n) {
t = Metamaps.Topics.get(n.id)
t.set({ node: n }, { silent: true })
t.updateNode()
@ -189,8 +191,8 @@ Metamaps.Topic = {
// opts is additional options in a hash
// TODO: move createNewInDB and permitCerateSYnapseAfter into opts
renderTopic: function (mapping, topic, createNewInDB, permitCreateSynapseAfter, opts) {
var self = Metamaps.Topic
renderTopic: function (mapping, topic, createNewInDB, permitCreateSynapseAfter, opts = {}) {
var self = Topic
var nodeOnViz, tempPos
@ -198,56 +200,56 @@ Metamaps.Topic = {
var midpoint = {}, pixelPos
if (!$.isEmptyObject(Metamaps.Visualize.mGraph.graph.nodes)) {
Metamaps.Visualize.mGraph.graph.addNode(newnode)
nodeOnViz = Metamaps.Visualize.mGraph.graph.getNode(newnode.id)
if (!$.isEmptyObject(Visualize.mGraph.graph.nodes)) {
Visualize.mGraph.graph.addNode(newnode)
nodeOnViz = Visualize.mGraph.graph.getNode(newnode.id)
topic.set('node', nodeOnViz, {silent: true})
topic.updateNode() // links the topic and the mapping to the node
nodeOnViz.setData('dim', 1, 'start')
nodeOnViz.setData('dim', 25, 'end')
if (Metamaps.Visualize.type === 'RGraph') {
if (Visualize.type === 'RGraph') {
tempPos = new $jit.Complex(mapping.get('xloc'), mapping.get('yloc'))
tempPos = tempPos.toPolar()
nodeOnViz.setPos(tempPos, 'current')
nodeOnViz.setPos(tempPos, 'start')
nodeOnViz.setPos(tempPos, 'end')
} else if (Metamaps.Visualize.type === 'ForceDirected') {
} else if (Visualize.type === 'ForceDirected') {
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'current')
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'start')
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'end')
}
if (Metamaps.Create.newTopic.addSynapse && permitCreateSynapseAfter) {
Metamaps.Create.newSynapse.topic1id = Metamaps.tempNode.getData('topic').id
if (Create.newTopic.addSynapse && permitCreateSynapseAfter) {
Create.newSynapse.topic1id = JIT.tempNode.getData('topic').id
// position the form
midpoint.x = Metamaps.tempNode.pos.getc().x + (nodeOnViz.pos.getc().x - Metamaps.tempNode.pos.getc().x) / 2
midpoint.y = Metamaps.tempNode.pos.getc().y + (nodeOnViz.pos.getc().y - Metamaps.tempNode.pos.getc().y) / 2
pixelPos = Metamaps.Util.coordsToPixels(midpoint)
midpoint.x = JIT.tempNode.pos.getc().x + (nodeOnViz.pos.getc().x - JIT.tempNode.pos.getc().x) / 2
midpoint.y = JIT.tempNode.pos.getc().y + (nodeOnViz.pos.getc().y - JIT.tempNode.pos.getc().y) / 2
pixelPos = Util.coordsToPixels(midpoint)
$('#new_synapse').css('left', pixelPos.x + 'px')
$('#new_synapse').css('top', pixelPos.y + 'px')
// show the form
Metamaps.Create.newSynapse.open()
Metamaps.Visualize.mGraph.fx.animate({
Create.newSynapse.open()
Visualize.mGraph.fx.animate({
modes: ['node-property:dim'],
duration: 500,
onComplete: function () {
Metamaps.tempNode = null
Metamaps.tempNode2 = null
Metamaps.tempInit = false
JIT.tempNode = null
JIT.tempNode2 = null
JIT.tempInit = false
}
})
} else {
Metamaps.Visualize.mGraph.fx.plotNode(nodeOnViz, Metamaps.Visualize.mGraph.canvas)
Metamaps.Visualize.mGraph.fx.animate({
Visualize.mGraph.fx.plotNode(nodeOnViz, Visualize.mGraph.canvas)
Visualize.mGraph.fx.animate({
modes: ['node-property:dim'],
duration: 500,
onComplete: function () {}
})
}
} else {
Metamaps.Visualize.mGraph.loadJSON(newnode)
nodeOnViz = Metamaps.Visualize.mGraph.graph.getNode(newnode.id)
Visualize.mGraph.loadJSON(newnode)
nodeOnViz = Visualize.mGraph.graph.getNode(newnode.id)
topic.set('node', nodeOnViz, {silent: true})
topic.updateNode() // links the topic and the mapping to the node
@ -256,8 +258,8 @@ Metamaps.Topic = {
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'current')
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'start')
nodeOnViz.setPos(new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')), 'end')
Metamaps.Visualize.mGraph.fx.plotNode(nodeOnViz, Metamaps.Visualize.mGraph.canvas)
Metamaps.Visualize.mGraph.fx.animate({
Visualize.mGraph.fx.plotNode(nodeOnViz, Visualize.mGraph.canvas)
Visualize.mGraph.fx.animate({
modes: ['node-property:dim'],
duration: 500,
onComplete: function () {}
@ -270,14 +272,14 @@ Metamaps.Topic = {
mappableid: mappingModel.get('mappable_id')
}
$(document).trigger(Metamaps.JIT.events.newTopic, [newTopicData])
$(document).trigger(JIT.events.newTopic, [newTopicData])
// call a success callback if provided
if (opts.success) {
opts.success(topicModel)
}
}
var topicSuccessCallback = function (topicModel, response) {
if (Metamaps.Active.Map) {
if (Active.Map) {
mapping.save({ mappable_id: topicModel.id }, {
success: function (model, response) {
mappingSuccessCallback(model, response, topicModel)
@ -288,12 +290,12 @@ Metamaps.Topic = {
})
}
if (Metamaps.Create.newTopic.addSynapse) {
Metamaps.Create.newSynapse.topic2id = topicModel.id
if (Create.newTopic.addSynapse) {
Create.newSynapse.topic2id = topicModel.id
}
}
if (!Metamaps.Settings.sandbox && createNewInDB) {
if (!Settings.sandbox && createNewInDB) {
if (topic.isNew()) {
topic.save(null, {
success: topicSuccessCallback,
@ -301,7 +303,7 @@ Metamaps.Topic = {
console.log('error saving topic to database')
}
})
} else if (!topic.isNew() && Metamaps.Active.Map) {
} else if (!topic.isNew() && Active.Map) {
mapping.save(null, {
success: mappingSuccessCallback
})
@ -309,58 +311,58 @@ Metamaps.Topic = {
}
},
createTopicLocally: function () {
var self = Metamaps.Topic
var self = Topic
if (Metamaps.Create.newTopic.name === '') {
Metamaps.GlobalUI.notifyUser('Please enter a topic title...')
if (Create.newTopic.name === '') {
GlobalUI.notifyUser('Please enter a topic title...')
return
}
// hide the 'double-click to add a topic' message
Metamaps.GlobalUI.hideDiv('#instructions')
GlobalUI.hideDiv('#instructions')
$(document).trigger(Metamaps.Map.events.editedByActiveMapper)
$(document).trigger(Map.events.editedByActiveMapper)
var metacode = Metamaps.Metacodes.get(Metamaps.Create.newTopic.metacode)
var metacode = Metamaps.Metacodes.get(Create.newTopic.metacode)
var topic = new Metamaps.Backbone.Topic({
name: Metamaps.Create.newTopic.name,
name: Create.newTopic.name,
metacode_id: metacode.id,
defer_to_map_id: Metamaps.Active.Map.id
defer_to_map_id: Active.Map.id
})
Metamaps.Topics.add(topic)
if (Metamaps.Create.newTopic.pinned) {
var nextCoords = Metamaps.AutoLayout.getNextCoord()
if (Create.newTopic.pinned) {
var nextCoords = AutoLayout.getNextCoord()
}
var mapping = new Metamaps.Backbone.Mapping({
xloc: nextCoords ? nextCoords.x : Metamaps.Create.newTopic.x,
yloc: nextCoords ? nextCoords.y : Metamaps.Create.newTopic.y,
xloc: nextCoords ? nextCoords.x : Create.newTopic.x,
yloc: nextCoords ? nextCoords.y : Create.newTopic.y,
mappable_id: topic.cid,
mappable_type: 'Topic',
})
Metamaps.Mappings.add(mapping)
// these can't happen until the value is retrieved, which happens in the line above
Metamaps.Create.newTopic.hide()
Create.newTopic.hide()
self.renderTopic(mapping, topic, true, true) // this function also includes the creation of the topic in the database
},
getTopicFromAutocomplete: function (id) {
var self = Metamaps.Topic
var self = Topic
$(document).trigger(Metamaps.Map.events.editedByActiveMapper)
$(document).trigger(Map.events.editedByActiveMapper)
Metamaps.Create.newTopic.hide()
Create.newTopic.hide()
var topic = self.get(id)
if (Metamaps.Create.newTopic.pinned) {
var nextCoords = Metamaps.AutoLayout.getNextCoord()
if (Create.newTopic.pinned) {
var nextCoords = AutoLayout.getNextCoord()
}
var mapping = new Metamaps.Backbone.Mapping({
xloc: nextCoords ? nextCoords.x : Metamaps.Create.newTopic.x,
yloc: nextCoords ? nextCoords.y : Metamaps.Create.newTopic.y,
xloc: nextCoords ? nextCoords.x : Create.newTopic.x,
yloc: nextCoords ? nextCoords.y : Create.newTopic.y,
mappable_type: 'Topic',
mappable_id: topic.id,
})
@ -369,13 +371,13 @@ Metamaps.Topic = {
self.renderTopic(mapping, topic, true, true)
},
getTopicFromSearch: function (event, id) {
var self = Metamaps.Topic
var self = Topic
$(document).trigger(Metamaps.Map.events.editedByActiveMapper)
$(document).trigger(Map.events.editedByActiveMapper)
var topic = self.get(id)
var nextCoords = Metamaps.AutoLayout.getNextCoord()
var nextCoords = AutoLayout.getNextCoord()
var mapping = new Metamaps.Backbone.Mapping({
xloc: nextCoords.x,
yloc: nextCoords.y,
@ -386,10 +388,12 @@ Metamaps.Topic = {
self.renderTopic(mapping, topic, true, true)
Metamaps.GlobalUI.notifyUser('Topic was added to your map!')
GlobalUI.notifyUser('Topic was added to your map!')
event.stopPropagation()
event.preventDefault()
return false
}
}; // end Metamaps.Topic
}
export default Topic

View file

@ -1,27 +1,28 @@
/* global Metamaps, $ */
import Active from './Active'
import GlobalUI from './GlobalUI'
import Mapper from './Mapper'
import Router from './Router'
import Util from './Util'
import Visualize from './Visualize'
/*
* Metamaps.TopicCard.js
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.GlobalUI
* - Metamaps.Mapper
* - Metamaps.Metacodes
* - Metamaps.Router
* - Metamaps.Util
* - Metamaps.Visualize
*/
Metamaps.TopicCard = {
const 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
var self = TopicCard
// initialize best_in_place editing
$('.authenticated div.permission.canEdit .best_in_place').best_in_place()
Metamaps.TopicCard.generateShowcardHTML = Hogan.compile($('#topicCardTemplate').html())
TopicCard.generateShowcardHTML = Hogan.compile($('#topicCardTemplate').html())
// initialize topic card draggability and resizability
$('.showcard').draggable({
@ -38,12 +39,12 @@ Metamaps.TopicCard = {
* @param {$jit.Graph.Node} node
*/
showCard: function (node, opts) {
var self = Metamaps.TopicCard
var self = TopicCard
if (!opts) opts = {}
var topic = node.getData('topic')
self.openTopicCard = topic
self.authorizedToEdit = topic.authorizeToEdit(Metamaps.Active.Mapper)
self.authorizedToEdit = topic.authorizeToEdit(Active.Mapper)
// populate the card that's about to show with the right topics data
self.populateShowCard(topic)
return $('.showcard').fadeIn('fast', function() {
@ -53,14 +54,14 @@ Metamaps.TopicCard = {
})
},
hideCard: function () {
var self = Metamaps.TopicCard
var self = TopicCard
$('.showcard').fadeOut('fast')
self.openTopicCard = null
self.authorizedToEdit = false
},
embedlyCardRendered: function (iframe) {
var self = Metamaps.TopicCard
var self = TopicCard
$('#embedlyLinkLoader').hide()
@ -77,7 +78,7 @@ Metamaps.TopicCard = {
}
},
removeLink: function () {
var self = Metamaps.TopicCard
var self = TopicCard
self.openTopicCard.save({
link: null
})
@ -87,7 +88,7 @@ Metamaps.TopicCard = {
$('.CardOnGraph').removeClass('hasAttachment')
},
bindShowCardListeners: function (topic) {
var self = Metamaps.TopicCard
var self = TopicCard
var showCard = document.getElementById('showcard')
var authorized = self.authorizedToEdit
@ -96,7 +97,7 @@ Metamaps.TopicCard = {
var setMapperImage = function (mapper) {
$('.contributorIcon').attr('src', mapper.get('image'))
}
Metamaps.Mapper.get(topic.get('user_id'), setMapperImage)
Mapper.get(topic.get('user_id'), setMapperImage)
// starting embed.ly
var resetFunc = function () {
@ -179,24 +180,16 @@ Metamaps.TopicCard = {
topic.save({
metacode_id: metacode.id
})
Metamaps.Visualize.mGraph.plot()
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
@ -205,9 +198,9 @@ Metamaps.TopicCard = {
// 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)
var windowWidth = $(window).width()
var showcardLeft = parseInt($('.showcard').css('left'))
var distanceFromEdge = windowWidth - (showcardLeft + TOPICCARD_WIDTH)
if (distanceFromEdge < METACODESELECT_WIDTH) {
$('.metacodeSelect').addClass('onRightEdge')
}
@ -216,11 +209,11 @@ Metamaps.TopicCard = {
// 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)
var windowHeight = $(window).height()
var showcardTop = parseInt($('.showcard').css('top'))
var topicTitleHeight = $('.showcard .title').height() + parseInt($('.showcard .title').css('padding-top')) + parseInt($('.showcard .title').css('padding-bottom'))
var heightOfSetList = $('.showcard .metacodeSelect').height()
var distanceFromBottom = windowHeight - (showcardTop + topicTitleHeight)
if (distanceFromBottom < MAX_METACODELIST_HEIGHT) {
$('.metacodeSelect').addClass('onBottomEdge')
}
@ -265,7 +258,7 @@ Metamaps.TopicCard = {
// bind best_in_place ajax callbacks
bipName.bind('ajax:success', function () {
var name = Metamaps.Util.decodeEntities($(this).html())
var name = Util.decodeEntities($(this).html())
topic.set('name', name)
topic.trigger('saved')
})
@ -313,7 +306,7 @@ Metamaps.TopicCard = {
}
// ability to change permission
var selectingPermission = false
if (topic.authorizePermissionChange(Metamaps.Active.Mapper)) {
if (topic.authorizePermissionChange(Active.Mapper)) {
$('.showcard .yourTopic .mapPerm').click(openPermissionSelect)
$('.showcard').click(hidePermissionSelect)
}
@ -331,7 +324,7 @@ Metamaps.TopicCard = {
$('.showcard .hoverTip').removeClass('hide')
})
$('.mapCount .tip li a').click(Metamaps.Router.intercept)
$('.mapCount .tip li a').click(Router.intercept)
var originalText = $('.showMore').html()
$('.mapCount .tip .showMore').unbind().toggle(
@ -349,13 +342,13 @@ Metamaps.TopicCard = {
})
},
handleInvalidLink: function () {
var self = Metamaps.TopicCard
var self = TopicCard
self.removeLink()
Metamaps.GlobalUI.notifyUser('Invalid link')
GlobalUI.notifyUser('Invalid link')
},
populateShowCard: function (topic) {
var self = Metamaps.TopicCard
var self = TopicCard
var showCard = document.getElementById('showcard')
@ -364,11 +357,11 @@ Metamaps.TopicCard = {
var topicForTemplate = self.buildObject(topic)
var html = self.generateShowcardHTML.render(topicForTemplate)
if (topic.authorizeToEdit(Metamaps.Active.Mapper)) {
if (topic.authorizeToEdit(Active.Mapper)) {
var perm = document.createElement('div')
var string = 'permission canEdit'
if (topic.authorizePermissionChange(Metamaps.Active.Mapper)) string += ' yourTopic'
if (topic.authorizePermissionChange(Active.Mapper)) string += ' yourTopic'
perm.className = string
perm.innerHTML = html
showCard.appendChild(perm)
@ -379,16 +372,16 @@ Metamaps.TopicCard = {
showCard.appendChild(perm)
}
Metamaps.TopicCard.bindShowCardListeners(topic)
TopicCard.bindShowCardListeners(topic)
},
generateShowcardHTML: null, // will be initialized into a Hogan template within init function
// generateShowcardHTML
buildObject: function (topic) {
var self = Metamaps.TopicCard
var self = TopicCard
var nodeValues = {}
var authorized = topic.authorizeToEdit(Metamaps.Active.Mapper)
var authorized = topic.authorizeToEdit(Active.Mapper)
if (!authorized) {
} else {
@ -421,18 +414,18 @@ Metamaps.TopicCard = {
var inmapsLinks = topic.get('inmapsLinks') || []
nodeValues.inmaps = ''
if (inmapsAr.length < 6) {
for (i = 0; i < inmapsAr.length; i++) {
for (let 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++) {
for (let 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++) {
for (let i = 5; i < inmapsAr.length; i++) {
var url = '/maps/' + inmapsLinks[i]
nodeValues.inmaps += '<li class="hideExtra extraText"><a href="' + url + '">' + inmapsAr[i] + '</a></li>'
}
@ -455,4 +448,6 @@ Metamaps.TopicCard = {
nodeValues.desc = (topic.get('desc') == '' && authorized) ? desc_nil : topic.get('desc')
return nodeValues
}
}; // end Metamaps.TopicCard
}
export default TopicCard

View file

@ -1,13 +1,6 @@
/* global Metamaps */
import Visualize from './Visualize'
/*
* Metamaps.Util.js
*
* Dependencies:
* - Metamaps.Visualize
*/
Metamaps.Util = {
const Util = {
// helper function to determine how many lines are needed
// Line Splitter Function
// copyright Stephen Chapman, 19th April 2006
@ -45,8 +38,8 @@ Metamaps.Util = {
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,
if (Visualize.mGraph) {
var canvas = Visualize.mGraph.canvas,
s = canvas.getSize(),
p = canvas.getPos(),
ox = canvas.translateOffsetX,
@ -67,8 +60,8 @@ Metamaps.Util = {
},
pixelsToCoords: function (pixels) {
var coords
if (Metamaps.Visualize.mGraph) {
var canvas = Metamaps.Visualize.mGraph.canvas,
if (Visualize.mGraph) {
var canvas = Visualize.mGraph.canvas,
s = canvas.getSize(),
p = canvas.getPos(),
ox = canvas.translateOffsetX,
@ -91,7 +84,7 @@ Metamaps.Util = {
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)
return Util.colorLuminance('#' + r + g + b, -0.4)
},
// darkens a hex value by 'lum' percentage
colorLuminance: function (hex, lum) {
@ -127,4 +120,6 @@ Metamaps.Util = {
checkURLisYoutubeVideo: function (url) {
return (url.match(/^https?:\/\/(?:www\.)?youtube.com\/watch\?(?=[^?]*v=\w+)(?:[^\s?]+)?$/) != null)
}
}; // end Metamaps.Util
}
export default Util

View file

@ -0,0 +1,343 @@
/* global $ */
import Backbone from 'backbone'
import Autolinker from 'autolinker'
// TODO is this line good or bad
// Backbone.$ = window.$
const linker = new Autolinker({ newWindow: true, truncate: 50, email: false, phone: false, twitter: false });
var Private = {
messageHTML: "<div class='chat-message'>" +
"<div class='chat-message-user'><img src='{{ user_image }}' title='{{user_name }}'/></div>" +
"<div class='chat-message-text'>{{ message }}</div>" +
"<div class='chat-message-time'>{{ timestamp }}</div>" +
"<div class='clearfloat'></div>" +
"</div>",
participantHTML: "<div class='participant participant-{{ id }} {{ selfClass }}'>" +
"<div class='chat-participant-image'><img src='{{ image }}' style='border: 2px solid {{ color }};' /></div>" +
"<div class='chat-participant-name'>{{ username }} {{ selfName }}</div>" +
"<button type='button' class='button chat-participant-invite-call' onclick='Metamaps.Realtime.inviteACall({{ id}});'></button>" +
"<button type='button' class='button chat-participant-invite-join' onclick='Metamaps.Realtime.inviteToJoin({{ id}});'></button>" +
"<span class='chat-participant-participating'><div class='green-dot'></div></span>" +
"<div class='clearfloat'></div>" +
"</div>",
templates: function() {
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g
};
this.messageTemplate = _.template(Private.messageHTML);
this.participantTemplate = _.template(Private.participantHTML);
},
createElements: function() {
this.$unread = $('<div class="chat-unread"></div>');
this.$button = $('<div class="chat-button"><div class="tooltips">Chat</div></div>');
this.$messageInput = $('<textarea placeholder="Send a message..." class="chat-input"></textarea>');
this.$juntoHeader = $('<div class="junto-header">PARTICIPANTS</div>');
this.$videoToggle = $('<div class="video-toggle"></div>');
this.$cursorToggle = $('<div class="cursor-toggle"></div>');
this.$participants = $('<div class="participants"></div>');
this.$conversationInProgress = $('<div class="conversation-live">LIVE <span class="call-action leave" onclick="Metamaps.Realtime.leaveCall();">LEAVE</span><span class="call-action join" onclick="Metamaps.Realtime.joinCall();">JOIN</span></div>');
this.$chatHeader = $('<div class="chat-header">CHAT</div>');
this.$soundToggle = $('<div class="sound-toggle"></div>');
this.$messages = $('<div class="chat-messages"></div>');
this.$container = $('<div class="chat-box"></div>');
},
attachElements: function() {
this.$button.append(this.$unread);
this.$juntoHeader.append(this.$videoToggle);
this.$juntoHeader.append(this.$cursorToggle);
this.$chatHeader.append(this.$soundToggle);
this.$participants.append(this.$conversationInProgress);
this.$container.append(this.$juntoHeader);
this.$container.append(this.$participants);
this.$container.append(this.$chatHeader);
this.$container.append(this.$button);
this.$container.append(this.$messages);
this.$container.append(this.$messageInput);
},
addEventListeners: function() {
var self = this;
this.participants.on('add', function (participant) {
Private.addParticipant.call(self, participant);
});
this.participants.on('remove', function (participant) {
Private.removeParticipant.call(self, participant);
});
this.$button.on('click', function () {
Handlers.buttonClick.call(self);
});
this.$videoToggle.on('click', function () {
Handlers.videoToggleClick.call(self);
});
this.$cursorToggle.on('click', function () {
Handlers.cursorToggleClick.call(self);
});
this.$soundToggle.on('click', function () {
Handlers.soundToggleClick.call(self);
});
this.$messageInput.on('keyup', function (event) {
Handlers.keyUp.call(self, event);
});
this.$messageInput.on('focus', function () {
Handlers.inputFocus.call(self);
});
this.$messageInput.on('blur', function () {
Handlers.inputBlur.call(self);
});
},
initializeSounds: function() {
this.sound = new Howl({
urls: [Metamaps.Erb['sounds/MM_sounds.mp3'], Metamaps.Erb['sounds/MM_sounds.ogg']],
sprite: {
joinmap: [0, 561],
leavemap: [1000, 592],
receivechat: [2000, 318],
sendchat: [3000, 296],
sessioninvite: [4000, 5393, true]
}
});
},
incrementUnread: function() {
this.unreadMessages++;
this.$unread.html(this.unreadMessages);
this.$unread.show();
},
addMessage: function(message, isInitial, wasMe) {
if (!this.isOpen && !isInitial) Private.incrementUnread.call(this);
function addZero(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
var m = _.clone(message.attributes);
var today = new Date();
m.timestamp = new Date(m.created_at);
var date = (m.timestamp.getMonth() + 1) + '/' + m.timestamp.getDate();
date += " " + addZero(m.timestamp.getHours()) + ":" + addZero(m.timestamp.getMinutes());
m.timestamp = date;
m.image = m.user_image || 'http://www.hotpepper.ca/wp-content/uploads/2014/11/default_profile_1_200x200.png'; // TODO: remove
m.message = linker.link(m.message);
var $html = $(this.messageTemplate(m));
this.$messages.append($html);
if (!isInitial) this.scrollMessages(200);
if (!wasMe && !isInitial && this.alertSound) this.sound.play('receivechat');
},
initialMessages: function() {
var messages = this.messages.models;
for (var i = 0; i < messages.length; i++) {
Private.addMessage.call(this, messages[i], true);
}
},
handleInputMessage: function() {
var message = {
message: this.$messageInput.val(),
};
this.$messageInput.val('');
$(document).trigger(ChatView.events.message + '-' + this.room, [message]);
},
addParticipant: function(participant) {
var p = _.clone(participant.attributes);
if (p.self) {
p.selfClass = 'is-self';
p.selfName = '(me)';
} else {
p.selfClass = '';
p.selfName = '';
}
var html = this.participantTemplate(p);
this.$participants.append(html);
},
removeParticipant: function(participant) {
this.$container.find('.participant-' + participant.get('id')).remove();
}
};
var Handlers = {
buttonClick: function() {
if (this.isOpen) this.close();
else if (!this.isOpen) this.open();
},
videoToggleClick: function() {
this.$videoToggle.toggleClass('active');
this.videosShowing = !this.videosShowing;
$(document).trigger(this.videosShowing ? ChatView.events.videosOn : ChatView.events.videosOff);
},
cursorToggleClick: function() {
this.$cursorToggle.toggleClass('active');
this.cursorsShowing = !this.cursorsShowing;
$(document).trigger(this.cursorsShowing ? ChatView.events.cursorsOn : ChatView.events.cursorsOff);
},
soundToggleClick: function() {
this.alertSound = !this.alertSound;
this.$soundToggle.toggleClass('active');
},
keyUp: function(event) {
switch(event.which) {
case 13: // enter
Private.handleInputMessage.call(this);
break;
}
},
inputFocus: function() {
$(document).trigger(ChatView.events.inputFocus);
},
inputBlur: function() {
$(document).trigger(ChatView.events.inputBlur);
}
};
const ChatView = function(messages, mapper, room) {
var self = this;
this.room = room;
this.mapper = mapper;
this.messages = messages; // backbone collection
this.isOpen = false;
this.alertSound = true; // whether to play sounds on arrival of new messages or not
this.cursorsShowing = true;
this.videosShowing = true;
this.unreadMessages = 0;
this.participants = new Backbone.Collection();
Private.templates.call(this);
Private.createElements.call(this);
Private.attachElements.call(this);
Private.addEventListeners.call(this);
Private.initialMessages.call(this);
Private.initializeSounds.call(this);
this.$container.css({
right: '-300px'
});
};
ChatView.prototype.conversationInProgress = function (participating) {
this.$conversationInProgress.show();
this.$participants.addClass('is-live');
if (participating) this.$participants.addClass('is-participating');
this.$button.addClass('active');
// hide invite to call buttons
}
ChatView.prototype.conversationEnded = function () {
this.$conversationInProgress.hide();
this.$participants.removeClass('is-live');
this.$participants.removeClass('is-participating');
this.$button.removeClass('active');
this.$participants.find('.participant').removeClass('active');
this.$participants.find('.participant').removeClass('pending');
}
ChatView.prototype.leaveConversation = function () {
this.$participants.removeClass('is-participating');
}
ChatView.prototype.mapperJoinedCall = function (id) {
this.$participants.find('.participant-' + id).addClass('active');
}
ChatView.prototype.mapperLeftCall = function (id) {
this.$participants.find('.participant-' + id).removeClass('active');
}
ChatView.prototype.invitationPending = function (id) {
this.$participants.find('.participant-' + id).addClass('pending');
}
ChatView.prototype.invitationAnswered = function (id) {
this.$participants.find('.participant-' + id).removeClass('pending');
}
ChatView.prototype.addParticipant = function (participant) {
this.participants.add(participant);
}
ChatView.prototype.removeParticipant = function (username) {
var p = this.participants.find(function (p) { return p.get('username') === username; });
if (p) {
this.participants.remove(p);
}
}
ChatView.prototype.removeParticipants = function () {
this.participants.remove(this.participants.models);
}
ChatView.prototype.open = function () {
this.$container.css({
right: '0'
});
this.$messageInput.focus();
this.isOpen = true;
this.unreadMessages = 0;
this.$unread.hide();
this.scrollMessages(0);
$(document).trigger(ChatView.events.openTray);
}
ChatView.prototype.addMessage = function(message, isInitial, wasMe) {
this.messages.add(message);
Private.addMessage.call(this, message, isInitial, wasMe);
}
ChatView.prototype.scrollMessages = function(duration) {
duration = duration || 0;
this.$messages.animate({
scrollTop: this.$messages[0].scrollHeight
}, duration);
}
ChatView.prototype.clearMessages = function () {
this.unreadMessages = 0;
this.$unread.hide();
this.$messages.empty();
}
ChatView.prototype.close = function () {
this.$container.css({
right: '-300px'
});
this.$messageInput.blur();
this.isOpen = false;
$(document).trigger(ChatView.events.closeTray);
}
ChatView.prototype.remove = function () {
this.$button.off();
this.$container.remove();
}
/**
* @class
* @static
*/
ChatView.events = {
message: 'ChatView:message',
openTray: 'ChatView:openTray',
closeTray: 'ChatView:closeTray',
inputFocus: 'ChatView:inputFocus',
inputBlur: 'ChatView:inputBlur',
cursorsOff: 'ChatView:cursorsOff',
cursorsOn: 'ChatView:cursorsOn',
videosOff: 'ChatView:videosOff',
videosOn: 'ChatView:videosOn'
};
export default ChatView

View file

@ -0,0 +1,88 @@
/* global Metamaps, $ */
import React from 'react'
import ReactDOM from 'react-dom' // TODO ensure this isn't a double import
import Active from '../Active'
import ReactComponents from '../ReactComponents'
/*
* - Metamaps.Loading
*/
const ExploreMaps = {
setCollection: function (collection) {
var self = ExploreMaps
if (self.collection) {
self.collection.off('add', self.render)
self.collection.off('successOnFetch', self.handleSuccess)
self.collection.off('errorOnFetch', self.handleError)
}
self.collection = collection
self.collection.on('add', self.render)
self.collection.on('successOnFetch', self.handleSuccess)
self.collection.on('errorOnFetch', self.handleError)
},
render: function (mapperObj, cb) {
var self = ExploreMaps
if (typeof mapperObj === 'function') {
cb = mapperObj
mapperObj = null
}
var exploreObj = {
currentUser: Active.Mapper,
section: self.collection.id,
displayStyle: 'grid',
maps: self.collection,
moreToLoad: self.collection.page != 'loadedAll',
user: mapperObj,
loadMore: self.loadMore
}
ReactDOM.render(
React.createElement(ReactComponents.Maps, exploreObj),
document.getElementById('explore')
)
if (cb) cb()
Metamaps.Loading.hide()
},
loadMore: function () {
var self = ExploreMaps
if (self.collection.page != "loadedAll") {
self.collection.getMaps()
}
else self.render()
},
handleSuccess: function (cb) {
var self = ExploreMaps
if (self.collection && self.collection.id === 'mapper') {
self.fetchUserThenRender(cb)
} else {
self.render(cb)
}
},
handleError: function () {
console.log('error loading maps!') // TODO
},
fetchUserThenRender: function (cb) {
var self = ExploreMaps
// first load the mapper object and then call the render function
$.ajax({
url: '/users/' + self.collection.mapperId + '/details.json',
success: function (response) {
self.render(response, cb)
},
error: function () {
self.render(cb)
}
})
}
}
export default ExploreMaps

View file

@ -0,0 +1,204 @@
/* global Metamaps, $ */
import Backbone from 'backbone'
// TODO is this line good or bad
// Backbone.$ = window.$
import Active from '../Active'
import Realtime from '../Realtime'
import ChatView from './ChatView'
import VideoView from './VideoView'
/*
* Dependencies:
* Metamaps.Backbone
*/
const Room = function(opts) {
var self = this
this.isActiveRoom = false
this.socket = opts.socket
this.webrtc = opts.webrtc
//this.roomRef = opts.firebase
this.room = opts.room
this.config = opts.config
this.peopleCount = 0
this.$myVideo = opts.$video
this.myVideo = opts.myVideoView
this.messages = new Backbone.Collection()
this.currentMapper = new Backbone.Model({ name: opts.username, image: opts.image })
this.chat = new ChatView(this.messages, this.currentMapper, this.room)
this.videos = {}
this.init()
}
Room.prototype.join = function(cb) {
this.isActiveRoom = true
this.webrtc.joinRoom(this.room, cb)
this.chat.conversationInProgress(true) // true indicates participation
}
Room.prototype.conversationInProgress = function() {
this.chat.conversationInProgress(false) // false indicates not participating
}
Room.prototype.conversationEnding = function() {
this.chat.conversationEnded()
}
Room.prototype.leaveVideoOnly = function() {
this.chat.leaveConversation() // the conversation will carry on without you
for (var id in this.videos) {
this.removeVideo(id)
}
this.isActiveRoom = false
this.webrtc.leaveRoom()
}
Room.prototype.leave = function() {
for (var id in this.videos) {
this.removeVideo(id)
}
this.isActiveRoom = false
this.webrtc.leaveRoom()
this.chat.conversationEnded()
this.chat.removeParticipants()
this.chat.clearMessages()
this.messages.reset()
}
Room.prototype.setPeopleCount = function(count) {
this.peopleCount = count
}
Room.prototype.init = function () {
var self = this
$(document).on(VideoView.events.audioControlClick, function (event, videoView) {
if (!videoView.audioStatus) self.webrtc.mute()
else if (videoView.audioStatus) self.webrtc.unmute()
})
$(document).on(VideoView.events.videoControlClick, function (event, videoView) {
if (!videoView.videoStatus) self.webrtc.pauseVideo()
else if (videoView.videoStatus) self.webrtc.resumeVideo()
})
this.webrtc.webrtc.off('peerStreamAdded')
this.webrtc.webrtc.off('peerStreamRemoved')
this.webrtc.on('peerStreamAdded', function (peer) {
var mapper = Realtime.mappersOnMap[peer.nick]
peer.avatar = mapper.image
peer.username = mapper.name
if (self.isActiveRoom) {
self.addVideo(peer)
}
})
this.webrtc.on('peerStreamRemoved', function (peer) {
if (self.isActiveRoom) {
self.removeVideo(peer)
}
})
this.webrtc.on('mute', function (data) {
var v = self.videos[data.id]
if (!v) return
if (data.name === 'audio') {
v.audioStatus = false
}
else if (data.name === 'video') {
v.videoStatus = false
v.$avatar.show()
}
if (!v.audioStatus && !v.videoStatus) v.$container.hide()
})
this.webrtc.on('unmute', function (data) {
var v = self.videos[data.id]
if (!v) return
if (data.name === 'audio') {
v.audioStatus = true
}
else if (data.name === 'video') {
v.videoStatus = true
v.$avatar.hide()
}
v.$container.show()
})
var sendChatMessage = function (event, data) {
self.sendChatMessage(data)
}
$(document).on(ChatView.events.message + '-' + this.room, sendChatMessage)
}
Room.prototype.videoAdded = function (callback) {
this._videoAdded = callback
}
Room.prototype.addVideo = function (peer) {
var
id = this.webrtc.getDomId(peer),
video = attachMediaStream(peer.stream)
var
v = new VideoView(video, null, id, false, { DOUBLE_CLICK_TOLERANCE: 200, avatar: peer.avatar, username: peer.username })
this.videos[peer.id] = v
if (this._videoAdded) this._videoAdded(v, peer.nick)
}
Room.prototype.removeVideo = function (peer) {
var id = typeof peer == 'string' ? peer : peer.id
if (this.videos[id]) {
this.videos[id].remove()
delete this.videos[id]
}
}
Room.prototype.sendChatMessage = function (data) {
var self = this
//this.roomRef.child('messages').push(data)
if (self.chat.alertSound) self.chat.sound.play('sendchat')
var m = new Metamaps.Backbone.Message({
message: data.message,
resource_id: Active.Map.id,
resource_type: "Map"
})
m.save(null, {
success: function (model, response) {
self.addMessages(new Metamaps.Backbone.MessageCollection(model), false, true)
$(document).trigger(Room.events.newMessage, [model])
},
error: function (model, response) {
console.log('error!', response)
}
})
}
// they should be instantiated as backbone models before they get
// passed to this function
Room.prototype.addMessages = function (messages, isInitial, wasMe) {
var self = this
messages.models.forEach(function (message) {
self.chat.addMessage(message, isInitial, wasMe)
})
}
/**
* @class
* @static
*/
Room.events = {
newMessage: "Room:newMessage"
}
export default Room

View file

@ -0,0 +1,202 @@
/* global $ */
var Private = {
addControls: function() {
var self = this;
this.$audioControl = $('<div class="video-audio"></div>');
this.$videoControl = $('<div class="video-video"></div>');
this.$audioControl.on('click', function () {
Handlers.audioControlClick.call(self);
});
this.$videoControl.on('click', function () {
Handlers.videoControlClick.call(self);
});
this.$container.append(this.$audioControl);
this.$container.append(this.$videoControl);
},
cancelClick: function() {
this.mouseIsDown = false;
if (this.hasMoved) {
}
$(document).trigger(VideoView.events.dragEnd);
}
};
var Handlers = {
mousedown: function(event) {
this.mouseIsDown = true;
this.hasMoved = false;
this.mouseMoveStart = {
x: event.pageX,
y: event.pageY
};
this.posStart = {
x: parseInt(this.$container.css('left'), '10'),
y: parseInt(this.$container.css('top'), '10')
}
$(document).trigger(VideoView.events.mousedown);
},
mouseup: function(event) {
$(document).trigger(VideoView.events.mouseup, [this]);
var storedTime = this.lastClick;
var now = Date.now();
this.lastClick = now;
if (now - storedTime < this.config.DOUBLE_CLICK_TOLERANCE) {
$(document).trigger(VideoView.events.doubleClick, [this]);
}
},
mousemove: function(event) {
var
diffX,
diffY,
newX,
newY;
if (this.$parent && this.mouseIsDown) {
this.manuallyPositioned = true;
this.hasMoved = true;
diffX = event.pageX - this.mouseMoveStart.x;
diffY = this.mouseMoveStart.y - event.pageY;
newX = this.posStart.x + diffX;
newY = this.posStart.y - diffY;
this.$container.css({
top: newY,
left: newX
});
}
},
audioControlClick: function() {
if (this.audioStatus) {
this.audioOff();
} else {
this.audioOn();
}
$(document).trigger(VideoView.events.audioControlClick, [this]);
},
videoControlClick: function() {
if (this.videoStatus) {
this.videoOff();
} else {
this.videoOn();
}
$(document).trigger(VideoView.events.videoControlClick, [this]);
},
};
var VideoView = function(video, $parent, id, isMyself, config) {
var self = this;
this.$parent = $parent; // mapView
this.video = video;
this.id = id;
this.config = config;
this.mouseIsDown = false;
this.mouseDownOffset = { x: 0, y: 0 };
this.lastClick = null;
this.hasMoved = false;
this.audioStatus = true;
this.videoStatus = true;
this.$container = $('<div></div>');
this.$container.addClass('collaborator-video' + (isMyself ? ' my-video' : ''));
this.$container.attr('id', 'container_' + id);
var $vidContainer = $('<div></div>');
$vidContainer.addClass('video-cutoff');
$vidContainer.append(this.video);
this.avatar = config.avatar;
this.$avatar = $('<img draggable="false" class="collaborator-video-avatar" src="' + config.avatar + '" width="150" height="150" />');
$vidContainer.append(this.$avatar);
this.$container.append($vidContainer);
this.$container.on('mousedown', function (event) {
Handlers.mousedown.call(self, event);
});
if (isMyself) {
Private.addControls.call(this);
}
// suppress contextmenu
this.video.oncontextmenu = function () { return false; };
if (this.$parent) this.setParent(this.$parent);
};
VideoView.prototype.setParent = function($parent) {
var self = this;
this.$parent = $parent;
this.$parent.off('.video' + this.id);
this.$parent.on('mouseup.video' + this.id, function (event) {
Handlers.mouseup.call(self, event);
Private.cancelClick.call(self);
});
this.$parent.on('mousemove.video' + this.id, function (event) {
Handlers.mousemove.call(self, event);
});
}
VideoView.prototype.setAvatar = function (src) {
this.$avatar.attr('src', src);
this.avatar = src;
}
VideoView.prototype.remove = function () {
this.$container.off();
if (this.$parent) this.$parent.off('.video' + this.id);
this.$container.remove();
}
VideoView.prototype.videoOff = function () {
this.$videoControl.addClass('active');
this.$avatar.show();
this.videoStatus = false;
}
VideoView.prototype.videoOn = function () {
this.$videoControl.removeClass('active');
this.$avatar.hide();
this.videoStatus = true;
}
VideoView.prototype.audioOff = function () {
this.$audioControl.addClass('active');
this.audioStatus = false;
}
VideoView.prototype.audioOn = function () {
this.$audioControl.removeClass('active');
this.audioStatus = true;
}
/**
* @class
* @static
*/
VideoView.events = {
mousedown: "VideoView:mousedown",
mouseup: "VideoView:mouseup",
doubleClick: "VideoView:doubleClick",
dragEnd: "VideoView:dragEnd",
audioControlClick: "VideoView:audioControlClick",
videoControlClick: "VideoView:videoControlClick",
};
export default VideoView

View file

@ -0,0 +1,7 @@
import ExploreMaps from './ExploreMaps'
import ChatView from './ChatView'
import VideoView from './VideoView'
import Room from './Room'
const Views = { ExploreMaps, ChatView, VideoView, Room }
export default Views

View file

@ -1,27 +1,32 @@
/* global Metamaps, $ */
import _ from 'lodash'
import $jit from '../patched/JIT'
import Active from './Active'
import JIT from './JIT'
import Router from './Router'
import TopicCard from './TopicCard'
/*
* Metamaps.Visualize
*
* Dependencies:
* - Metamaps.Active
* - Metamaps.JIT
* - Metamaps.Loading
* - Metamaps.Metacodes
* - Metamaps.Router
* - Metamaps.Synapses
* - Metamaps.TopicCard
* - Metamaps.Topics
* - Metamaps.Touch
* - Metamaps.Visualize
*/
Metamaps.Visualize = {
const 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
touchDragNode: null,
init: function () {
var self = Metamaps.Visualize
var self = Visualize
// disable awkward dragging of the canvas element that would sometimes happen
$('#infovis-canvas').on('dragstart', function (event) {
event.preventDefault()
@ -35,19 +40,19 @@ Metamaps.Visualize = {
// prevent touch events on the canvas from default behaviour
$('#infovis-canvas').bind('touchmove', function (event) {
// Metamaps.JIT.touchPanZoomHandler(event)
// 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()
if (!self.mGraph.events.touchMoved && !Visualize.touchDragNode) TopicCard.hideCurrentCard()
self.mGraph.events.touched = self.mGraph.events.touchMoved = false
Metamaps.Touch.touchDragNode = false
Visualize.touchDragNode = false
})
},
computePositions: function () {
var self = Metamaps.Visualize,
var self = Visualize,
mapping
if (self.type == 'RGraph') {
@ -111,31 +116,31 @@ Metamaps.Visualize = {
*
*/
render: function () {
var self = Metamaps.Visualize, RGraphSettings, FDSettings
var self = Visualize, RGraphSettings, FDSettings
if (self.type == 'RGraph' && (!self.mGraph || self.mGraph instanceof $jit.ForceDirected)) {
// clear the previous canvas from #infovis
$('#infovis').empty()
RGraphSettings = $.extend(true, {}, Metamaps.JIT.ForceDirected.graphSettings)
RGraphSettings = $.extend(true, {}, JIT.ForceDirected.graphSettings)
$jit.RGraph.Plot.NodeTypes.implement(Metamaps.JIT.ForceDirected.nodeSettings)
$jit.RGraph.Plot.EdgeTypes.implement(Metamaps.JIT.ForceDirected.edgeSettings)
$jit.RGraph.Plot.NodeTypes.implement(JIT.ForceDirected.nodeSettings)
$jit.RGraph.Plot.EdgeTypes.implement(JIT.ForceDirected.edgeSettings)
RGraphSettings.width = $(document).width()
RGraphSettings.height = $(document).height()
RGraphSettings.background = Metamaps.JIT.RGraph.background
RGraphSettings.levelDistance = Metamaps.JIT.RGraph.levelDistance
RGraphSettings.background = JIT.RGraph.background
RGraphSettings.levelDistance = JIT.RGraph.levelDistance
self.mGraph = new $jit.RGraph(RGraphSettings)
} else if (self.type == 'ForceDirected' && (!self.mGraph || self.mGraph instanceof $jit.RGraph)) {
// clear the previous canvas from #infovis
$('#infovis').empty()
FDSettings = $.extend(true, {}, Metamaps.JIT.ForceDirected.graphSettings)
FDSettings = $.extend(true, {}, JIT.ForceDirected.graphSettings)
$jit.ForceDirected.Plot.NodeTypes.implement(Metamaps.JIT.ForceDirected.nodeSettings)
$jit.ForceDirected.Plot.EdgeTypes.implement(Metamaps.JIT.ForceDirected.edgeSettings)
$jit.ForceDirected.Plot.NodeTypes.implement(JIT.ForceDirected.nodeSettings)
$jit.ForceDirected.Plot.EdgeTypes.implement(JIT.ForceDirected.edgeSettings)
FDSettings.width = $('body').width()
FDSettings.height = $('body').height()
@ -146,14 +151,14 @@ Metamaps.Visualize = {
$('#infovis').empty()
// init ForceDirected3D
self.mGraph = new $jit.ForceDirected3D(Metamaps.JIT.ForceDirected3D.graphSettings)
self.mGraph = new $jit.ForceDirected3D(JIT.ForceDirected3D.graphSettings)
self.cameraPosition = self.mGraph.canvas.canvases[0].camera.position
} else {
self.mGraph.graph.empty()
}
if (self.type == 'ForceDirected' && Metamaps.Active.Mapper) $.post('/maps/' + Metamaps.Active.Map.id + '/events/user_presence')
if (self.type == 'ForceDirected' && Active.Mapper) $.post('/maps/' + Active.Map.id + '/events/user_presence')
function runAnimation () {
Metamaps.Loading.hide()
@ -161,22 +166,22 @@ Metamaps.Visualize = {
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
if (Active.Topic) {
var node = _.find(JIT.vizData, function (node) {
return node.id === Active.Topic.id
})
rootIndex = _.indexOf(Metamaps.JIT.vizData, node)
rootIndex = _.indexOf(JIT.vizData, node)
}
self.mGraph.loadJSON(Metamaps.JIT.vizData, rootIndex)
self.mGraph.loadJSON(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)
self.mGraph.fx.animate(JIT.RGraph.animate)
} else if (self.type == 'ForceDirected') {
self.mGraph.animate(Metamaps.JIT.ForceDirected.animateSavedLayout)
self.mGraph.animate(JIT.ForceDirected.animateSavedLayout)
} else if (self.type == 'ForceDirected3D') {
self.mGraph.animate(Metamaps.JIT.ForceDirected.animateFDLayout)
self.mGraph.animate(JIT.ForceDirected.animateFDLayout)
}
}
}
@ -203,17 +208,19 @@ Metamaps.Visualize = {
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
clearTimeout(Router.timeoutId)
Router.timeoutId = setTimeout(function () {
var m = Active.Map
var t = Active.Topic
if (m && window.location.pathname !== '/maps/' + m.id) {
Metamaps.Router.navigate('/maps/' + m.id)
Router.navigate('/maps/' + m.id)
}
else if (t && window.location.pathname !== '/topics/' + t.id) {
Metamaps.Router.navigate('/topics/' + t.id)
Router.navigate('/topics/' + t.id)
}
}, 800)
}
}; // end Metamaps.Visualize
}
export default Visualize

View file

@ -0,0 +1,107 @@
/* global $ */
import Account from './Account'
import Active from './Active'
import Admin from './Admin'
import AutoLayout from './AutoLayout'
import Backbone from './Backbone'
import Control from './Control'
import Create from './Create'
import Debug from './Debug'
import Filter from './Filter'
import GlobalUI from './GlobalUI'
import Import from './Import'
import JIT from './JIT'
import Listeners from './Listeners'
import Map, { CheatSheet, InfoBox } from './Map'
import Mapper from './Mapper'
import Mobile from './Mobile'
import Mouse from './Mouse'
import Organize from './Organize'
import PasteInput from './PasteInput'
import Realtime from './Realtime'
import Router from './Router'
import Selected from './Selected'
import Settings from './Settings'
import Synapse from './Synapse'
import SynapseCard from './SynapseCard'
import Topic from './Topic'
import TopicCard from './TopicCard'
import Util from './Util'
import Views from './Views'
import Visualize from './Visualize'
import ReactComponents from './ReactComponents'
Metamaps.Account = Account
Metamaps.Active = Active
Metamaps.Admin = Admin
Metamaps.AutoLayout = AutoLayout
Metamaps.Backbone = Backbone
Metamaps.Control = Control
Metamaps.Create = Create
Metamaps.Debug = Debug
Metamaps.Filter = Filter
Metamaps.GlobalUI = GlobalUI
Metamaps.Import = Import
Metamaps.JIT = JIT
Metamaps.Listeners = Listeners
Metamaps.Map = Map
Metamaps.Map.CheatSheet = CheatSheet
Metamaps.Map.InfoBox = InfoBox
Metamaps.Maps = {}
Metamaps.Mapper = Mapper
Metamaps.Mobile = Mobile
Metamaps.Mouse = Mouse
Metamaps.Organize = Organize
Metamaps.PasteInput = PasteInput
Metamaps.Realtime = Realtime
Metamaps.ReactComponents = ReactComponents
Metamaps.Router = Router
Metamaps.Selected = Selected
Metamaps.Settings = Settings
Metamaps.Synapse = Synapse
Metamaps.SynapseCard = SynapseCard
Metamaps.Topic = Topic
Metamaps.TopicCard = TopicCard
Metamaps.Util = Util
Metamaps.Views = Views
Metamaps.Visualize = Visualize
document.addEventListener("DOMContentLoaded", function() {
// initialize all the modules
for (const prop in Metamaps) {
// this runs the init function within each sub-object on the Metamaps one
if (Metamaps.hasOwnProperty(prop) &&
Metamaps[prop] != null &&
Metamaps[prop].hasOwnProperty('init') &&
typeof (Metamaps[prop].init) == 'function'
) {
Metamaps[prop].init()
}
}
// load whichever page you are on
if (Metamaps.currentSection === "explore") {
const capitalize = Metamaps.currentPage.charAt(0).toUpperCase() + Metamaps.currentPage.slice(1)
Metamaps.Views.ExploreMaps.setCollection( Metamaps.Maps[capitalize] )
if (Metamaps.currentPage === "mapper") {
Views.ExploreMaps.fetchUserThenRender()
}
else {
Views.ExploreMaps.render()
}
GlobalUI.showDiv('#explore')
}
else if (Metamaps.currentSection === "" && Active.Mapper) {
Views.ExploreMaps.setCollection(Metamaps.Maps.Active)
Views.ExploreMaps.render()
GlobalUI.showDiv('#explore')
}
else if (Active.Map || Active.Topic) {
Metamaps.Loading.show()
JIT.prepareVizData()
GlobalUI.showDiv('#infovis')
}
});
export default Metamaps

View file

@ -1,18 +1,8 @@
import React from 'react'
// create global references to some utility libraries
import ReactDOM from 'react-dom'
import Backbone from 'backbone'
import _ from 'underscore'
import Maps from './components/Maps.js'
// this is optional really, if we import components directly React will be
// in the bundle, so we won't need a global reference
window.React = React
window.ReactDOM = ReactDOM
Backbone.$ = window.$
window.Backbone = Backbone
window._ = _
window.Metamaps = window.Metamaps || {}
window.Metamaps.ReactComponents = {
Maps
}
import Metamaps from './Metamaps'
window.Metamaps = Metamaps

View file

@ -20,7 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
(function () {
/*
File: Core.js
@ -34,7 +33,11 @@ THE SOFTWARE.
This variable is the *only* global variable defined in the Toolkit.
There are also other interesting properties attached to this variable described below.
*/
window.$jit = function(w) {
// START METAMAPS CODE
const $jit = function(w) {
// ORIGINAL:
// window.$jit = function(w) {
// END METAMAPS CODE
w = w || window;
for(var k in $jit) {
if($jit[k].$extend) {
@ -3122,9 +3125,15 @@ var Canvas;
};
},
translateToCenter: function(ps) {
var size = this.getSize(),
width = ps? (size.width - ps.width - this.translateOffsetX*2) : size.width;
height = ps? (size.height - ps.height - this.translateOffsetY*2) : size.height;
// START METAMAPS CODE
var size = this.getSize();
var width = ps ? (size.width - ps.width - this.translateOffsetX*2) : size.width;
var height = ps ? (size.height - ps.height - this.translateOffsetY*2) : size.height;
// ORIGINAL CODE
// var size = this.getSize(),
// width = ps? (size.width - ps.width - this.translateOffsetX*2) : size.width;
// height = ps? (size.height - ps.height - this.translateOffsetY*2) : size.height;
// END METAMAPS CODE
var ctx = this.getCtx();
ps && ctx.scale(1/this.scaleOffsetX, 1/this.scaleOffsetY);
ctx.translate(width/2, height/2);
@ -3232,7 +3241,7 @@ var Canvas;
ctx = base.getCtx(),
scale = base.scaleOffsetX;
//var pattern = new Image();
//pattern.src = "<%= asset_path('cubes.png') %>";
//pattern.src = Metamaps.Erb['cubes.png']
//var ptrn = ctx.createPattern(pattern, 'repeat');
//ctx.fillStyle = ptrn;
ctx.fillStyle = Metamaps.Settings.colors.background;
@ -5634,7 +5643,11 @@ Graph.Op = {
break;
case 'fade:seq': case 'fade': case 'fade:con':
that = this;
// START METAMAPS CODE
var that = this;
// ORIGINAL CODE:
// that = this;
// END METAMAPS CODE
graph = viz.construct(json);
//set alpha to 0 for nodes to add.
@ -5770,7 +5783,11 @@ Graph.Op = {
break;
case 'fade:seq': case 'fade': case 'fade:con':
that = this;
// START METAMAPS CODE
var that = this;
// ORIGINAL CODE:
// that = this;
// END METAMAPS CODE
graph = viz.construct(json);
//preprocessing for nodes to delete.
//get node property modes to interpolate
@ -11312,7 +11329,4 @@ $jit.ForceDirected3D.$extend = true;
})($jit.ForceDirected3D);
})();
export default $jit

View file

@ -1,13 +1,14 @@
/* global describe, it */
const chai = require('chai')
const expect = chai.expect
Metamaps = {}
require('../../app/assets/javascripts/src/Metamaps.Import')
import chai from 'chai'
import Import from '../src/Metamaps/Import'
const { expect } = chai
describe('Metamaps.Import.js', function () {
it('has a topic whitelist', function () {
expect(Metamaps.Import.topicWhitelist).to.deep.equal(
expect(Import.topicWhitelist).to.deep.equal(
['id', 'name', 'metacode', 'x', 'y', 'description', 'link', 'permission']
)
})

View file

@ -5,7 +5,7 @@
"scripts": {
"build": "webpack",
"build:watch": "webpack --watch",
"test": "mocha frontend/test || (echo 'Run `npm install` to setup testing' && false)"
"test": "mocha --compilers js:babel-core/register frontend/test || (echo 'Run `npm install` to setup testing' && false)"
},
"repository": {
"type": "git",
@ -18,6 +18,7 @@
},
"homepage": "https://github.com/metamaps/metamaps#readme",
"dependencies": {
"autolinker": "^0.17.1",
"babel-cli": "^6.11.4",
"babel-loader": "^6.2.4",
"babel-plugin-transform-class-properties": "^6.11.5",
@ -27,9 +28,12 @@
"chai": "^3.5.0",
"jquery": "1.12.1",
"mocha": "^3.0.2",
"mocha-jsdom": "^1.1.0",
"node-uuid": "1.2.0",
"react": "^15.3.0",
"react-dom": "^15.3.0",
"requirejs": "^2.1.1",
"socket.io": "0.9.12",
"underscore": "^1.4.4",
"webpack": "^1.13.1"
}

View file

@ -1,10 +0,0 @@
{
"name": "RoR-real-time",
"description": "providing real-time sychronization for ruby on rails",
"version": "0.0.1",
"private": true,
"dependencies": {
"socket.io": "0.9.12",
"node-uuid": "1.2.0"
}
}