// TODO document this user agent function var labelType, useGradients, nativeTextSupport, animate; (function () { var ua = navigator.userAgent, iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), typeOfCanvas = typeof HTMLCanvasElement, nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), textSupport = nativeCanvasSupport && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); //I'm setting this based on the fact that ExCanvas provides text support for IE //and that as of today iPhone/iPad current text support is lame labelType = (!nativeCanvasSupport || (textSupport && !iStuff)) ? 'Native' : 'HTML'; nativeTextSupport = labelType == 'Native'; useGradients = nativeCanvasSupport; animate = !(iStuff || !nativeCanvasSupport); })(); // TODO eliminate these 5 top-level variables Metamaps.panningInt = null; Metamaps.tempNode = null; Metamaps.tempInit = false; Metamaps.tempNode2 = null; Metamaps.VERSION = '<%= METAMAPS_VERSION %>' Metamaps.Settings = { embed: false, // indicates that the app is on a page that is optimized for embedding in iFrames on other web pages sandbox: false, // puts the app into a mode (when true) where it only creates data locally, and isn't writing it to the database colors: { background: '#344A58', synapses: { normal: '#888888', hover: '#888888', selected: '#FFFFFF' }, topics: { selected: '#FFFFFF' }, labels: { background: '#18202E', text: '#DDD' } }, }; Metamaps.Touch = { touchPos: null, // this stores the x and y values of a current touch event touchDragNode: null // this stores a reference to a JIT node that is being dragged }; Metamaps.Mouse = { didPan: false, didBoxZoom: false, changeInX: 0, changeInY: 0, edgeHoveringOver: false, boxStartCoordinates: false, boxEndCoordinates: false, synapseStartCoordinates: [], synapseEndCoordinates: null, lastNodeClick: 0, lastCanvasClick: 0, DOUBLE_CLICK_TOLERANCE: 300 }; Metamaps.Selected = { reset: function () { var self = Metamaps.Selected; self.Nodes = []; self.Edges = []; }, Nodes: [], Edges: [] }; /* * * BACKBONE * */ Metamaps.Backbone.init = function () { var self = Metamaps.Backbone; self.Metacode = Backbone.Model.extend({ initialize: function () { var image = new Image(); image.crossOrigin = "Anonymous"; image.src = this.get('icon'); this.set('image',image); }, prepareLiForFilter: function () { var li = ''; li += '
  • ';       li += '';       li += '

    ' + this.get('name').toLowerCase() + '

  • '; return li; } }); self.MetacodeCollection = Backbone.Collection.extend({ model: this.Metacode, url: '/metacodes', comparator: function (a, b) { a = a.get('name').toLowerCase(); b = b.get('name').toLowerCase(); return a > b ? 1 : a < b ? -1 : 0; } }); self.Topic = Backbone.Model.extend({ urlRoot: '/topics', blacklist: ['node', 'created_at', 'updated_at', 'user_name', 'user_image', 'map_count', 'synapse_count'], toJSON: function (options) { return _.omit(this.attributes, this.blacklist); }, save: function (key, val, options) { var attrs; // Handle both `"key", value` and `{key: value}` -style arguments. if (key == null || typeof key === 'object') { attrs = key; options = val; } else { (attrs = {})[key] = val; } var newOptions = options || {}; var s = newOptions.success; var permBefore = this.get('permission'); newOptions.success = function (model, response, opt) { if (s) s(model, response, opt); model.trigger('saved'); if (permBefore === 'private' && model.get('permission') !== 'private') { model.trigger('noLongerPrivate'); } else if (permBefore !== 'private' && model.get('permission') === 'private') { model.trigger('nowPrivate'); } }; return Backbone.Model.prototype.save.call(this, attrs, newOptions); }, initialize: function () { if (this.isNew()) { this.set({ "user_id": Metamaps.Active.Mapper.id, "desc": '', "link": '', "permission": Metamaps.Active.Map ? Metamaps.Active.Map.get('permission') : 'commons' }); } this.on('changeByOther', this.updateCardView); this.on('change', this.updateNodeView); this.on('saved', this.savedEvent); this.on('nowPrivate', function(){ var removeTopicData = { mappableid: this.id }; $(document).trigger(Metamaps.JIT.events.removeTopic, [removeTopicData]); }); this.on('noLongerPrivate', function(){ var newTopicData = { mappingid: this.getMapping().id, mappableid: this.id }; $(document).trigger(Metamaps.JIT.events.newTopic, [newTopicData]); }); this.on('change:metacode_id', Metamaps.Filter.checkMetacodes, this); }, authorizeToEdit: function (mapper) { if (mapper && (this.get('permission') === "commons" || this.get('user_id') === mapper.get('id'))) return true; else return false; }, authorizePermissionChange: function (mapper) { if (mapper && this.get('user_id') === mapper.get('id')) return true; else return false; }, getDate: function () { }, getMetacode: function () { return Metamaps.Metacodes.get(this.get('metacode_id')); }, getMapping: function () { if (!Metamaps.Active.Map) return false; return Metamaps.Mappings.findWhere({ map_id: Metamaps.Active.Map.id, mappable_type: "Topic", mappable_id: this.isNew() ? this.cid : this.id }); }, createNode: function () { var mapping; var node = { adjacencies: [], id: this.isNew() ? this.cid : this.id, name: this.get('name') }; if (Metamaps.Active.Map) { mapping = this.getMapping(); node.data = { $mapping: null, $mappingID: mapping.id }; } return node; }, updateNode: function () { var mapping; var node = this.get('node'); node.setData('topic', this); if (Metamaps.Active.Map) { mapping = this.getMapping(); node.setData('mapping', mapping); } return node; }, savedEvent: function() { Metamaps.Realtime.sendTopicChange(this); }, updateViews: function() { var onPageWithTopicCard = Metamaps.Active.Map || Metamaps.Active.Topic; var node = this.get('node'); // update topic card, if this topic is the one open there if (onPageWithTopicCard && this == Metamaps.TopicCard.openTopicCard) { Metamaps.TopicCard.showCard(node); } // update the node on the map if (onPageWithTopicCard && node) { node.name = this.get('name'); Metamaps.Visualize.mGraph.plot(); } }, updateCardView: function() { var onPageWithTopicCard = Metamaps.Active.Map || Metamaps.Active.Topic; var node = this.get('node'); // update topic card, if this topic is the one open there if (onPageWithTopicCard && this == Metamaps.TopicCard.openTopicCard) { Metamaps.TopicCard.showCard(node); } }, updateNodeView: function() { var onPageWithTopicCard = Metamaps.Active.Map || Metamaps.Active.Topic; var node = this.get('node'); // update the node on the map if (onPageWithTopicCard && node) { node.name = this.get('name'); Metamaps.Visualize.mGraph.plot(); } } }); self.TopicCollection = Backbone.Collection.extend({ model: self.Topic, url: '/topics' }); self.Synapse = Backbone.Model.extend({ urlRoot: '/synapses', blacklist: ['edge', 'created_at', 'updated_at'], toJSON: function (options) { return _.omit(this.attributes, this.blacklist); }, save: function (key, val, options) { var attrs; // Handle both `"key", value` and `{key: value}` -style arguments. if (key == null || typeof key === 'object') { attrs = key; options = val; } else { (attrs = {})[key] = val; } var newOptions = options || {}; var s = newOptions.success; var permBefore = this.get('permission'); newOptions.success = function (model, response, opt) { if (s) s(model, response, opt); model.trigger('saved'); if (permBefore === 'private' && model.get('permission') !== 'private') { model.trigger('noLongerPrivate'); } else if (permBefore !== 'private' && model.get('permission') === 'private') { model.trigger('nowPrivate'); } }; return Backbone.Model.prototype.save.call(this, attrs, newOptions); }, initialize: function () { if (this.isNew()) { this.set({ "user_id": Metamaps.Active.Mapper.id, "permission": Metamaps.Active.Map ? Metamaps.Active.Map.get('permission') : 'commons', "category": "from-to" }); } this.on('changeByOther', this.updateCardView); this.on('change', this.updateEdgeView); this.on('saved', this.savedEvent); this.on('noLongerPrivate', function(){ var newSynapseData = { mappingid: this.getMapping().id, mappableid: this.id }; $(document).trigger(Metamaps.JIT.events.newSynapse, [newSynapseData]); }); this.on('nowPrivate', function(){ $(document).trigger(Metamaps.JIT.events.removeSynapse, [{ mappableid: this.id }]); }); this.on('change:desc', Metamaps.Filter.checkSynapses, this); }, prepareLiForFilter: function () { var li = ''; li += '
  • ';       li += '
  • '; return li; }, authorizeToEdit: function (mapper) { if (mapper && (this.get('permission') === "commons" || this.get('user_id') === mapper.get('id'))) return true; else return false; }, authorizePermissionChange: function (mapper) { if (mapper && this.get('user_id') === mapper.get('id')) return true; else return false; }, getTopic1: function () { return Metamaps.Topics.get(this.get('node1_id')); }, getTopic2: function () { return Metamaps.Topics.get(this.get('node2_id')); }, getDirection: function () { var t1 = this.getTopic1(), t2 = this.getTopic2(); return t1 && t2 ? [ t1.get('node').id, t2.get('node').id ] : false; }, getMapping: function () { if (!Metamaps.Active.Map) return false; return Metamaps.Mappings.findWhere({ map_id: Metamaps.Active.Map.id, mappable_type: "Synapse", mappable_id: this.isNew() ? this.cid : this.id }); }, createEdge: function (providedMapping) { var mapping, mappingID; var synapseID = this.isNew() ? this.cid : this.id; var edge = { nodeFrom: this.get('node1_id'), nodeTo: this.get('node2_id'), data: { $synapses: [], $synapseIDs: [synapseID], } }; if (Metamaps.Active.Map) { mapping = providedMapping || this.getMapping(); mappingID = mapping.isNew() ? mapping.cid : mapping.id; edge.data.$mappings = []; edge.data.$mappingIDs = [mappingID]; } return edge; }, updateEdge: function () { var mapping; var edge = this.get('edge'); edge.getData('synapses').push(this); if (Metamaps.Active.Map) { mapping = this.getMapping(); edge.getData('mappings').push(mapping); } return edge; }, savedEvent: function() { Metamaps.Realtime.sendSynapseChange(this); }, updateViews: function() { this.updateCardView(); this.updateEdgeView(); }, updateCardView: function() { var onPageWithSynapseCard = Metamaps.Active.Map || Metamaps.Active.Topic; var edge = this.get('edge'); // update synapse card, if this synapse is the one open there if (onPageWithSynapseCard && edge == Metamaps.SynapseCard.openSynapseCard) { Metamaps.SynapseCard.showCard(edge); } }, updateEdgeView: function() { var onPageWithSynapseCard = Metamaps.Active.Map || Metamaps.Active.Topic; var edge = this.get('edge'); // update the edge on the map if (onPageWithSynapseCard && edge) { Metamaps.Visualize.mGraph.plot(); } } }); self.SynapseCollection = Backbone.Collection.extend({ model: self.Synapse, url: '/synapses' }); self.Mapping = Backbone.Model.extend({ urlRoot: '/mappings', blacklist: ['created_at', 'updated_at'], toJSON: function (options) { return _.omit(this.attributes, this.blacklist); }, initialize: function () { if (this.isNew()) { this.set({ "user_id": Metamaps.Active.Mapper.id, "map_id": Metamaps.Active.Map ? Metamaps.Active.Map.id : null }); } }, getMap: function () { return Metamaps.Map.get(this.get('map_id')); }, getTopic: function () { if (this.get('mappable_type') === 'Topic') return Metamaps.Topic.get(this.get('mappable_id')); else return false; }, getSynapse: function () { if (this.get('mappable_type') === 'Synapse') return Metamaps.Synapse.get(this.get('mappable_id')); else return false; } }); self.MappingCollection = Backbone.Collection.extend({ model: self.Mapping, url: '/mappings' }); Metamaps.Metacodes = Metamaps.Metacodes ? new self.MetacodeCollection(Metamaps.Metacodes) : new self.MetacodeCollection(); Metamaps.Topics = Metamaps.Topics ? new self.TopicCollection(Metamaps.Topics) : new self.TopicCollection(); Metamaps.Synapses = Metamaps.Synapses ? new self.SynapseCollection(Metamaps.Synapses) : new self.SynapseCollection(); Metamaps.Mappers = Metamaps.Mappers ? new self.MapperCollection(Metamaps.Mappers) : new self.MapperCollection(); // this is for topic view Metamaps.Creators = Metamaps.Creators ? new self.MapperCollection(Metamaps.Creators) : new self.MapperCollection(); if (Metamaps.Active.Map) { Metamaps.Mappings = Metamaps.Mappings ? new self.MappingCollection(Metamaps.Mappings) : new self.MappingCollection(); Metamaps.Active.Map = new self.Map(Metamaps.Active.Map); } if (Metamaps.Active.Topic) Metamaps.Active.Topic = new self.Topic(Metamaps.Active.Topic); //attach collection event listeners self.attachCollectionEvents = function () { Metamaps.Topics.on("add remove", function(topic){ Metamaps.Map.InfoBox.updateNumbers(); Metamaps.Filter.checkMetacodes(); Metamaps.Filter.checkMappers(); }); Metamaps.Synapses.on("add remove", function(synapse){ Metamaps.Map.InfoBox.updateNumbers(); Metamaps.Filter.checkSynapses(); Metamaps.Filter.checkMappers(); }); if (Metamaps.Active.Map) { Metamaps.Mappings.on("add remove", function(mapping){ Metamaps.Map.InfoBox.updateNumbers(); Metamaps.Filter.checkSynapses(); Metamaps.Filter.checkMetacodes(); Metamaps.Filter.checkMappers(); }); } } self.attachCollectionEvents(); }; // end Metamaps.Backbone.init /* * * CREATE * */ Metamaps.Create = { isSwitchingSet: false, // indicates whether the metacode set switch lightbox is open selectedMetacodeSet: null, selectedMetacodeSetIndex: null, selectedMetacodeNames: [], newSelectedMetacodeNames: [], selectedMetacodes: [], newSelectedMetacodes: [], init: function () { var self = Metamaps.Create; self.newTopic.init(); self.newSynapse.init(); ////// ////// //// SWITCHING METACODE SETS $('#metacodeSwitchTabs').tabs({ selected: self.selectedMetacodeSetIndex }).addClass("ui-tabs-vertical ui-helper-clearfix"); $("#metacodeSwitchTabs .ui-tabs-nav li").removeClass("ui-corner-top").addClass("ui-corner-left"); $('.customMetacodeList li').click(self.toggleMetacodeSelected); // within the custom metacode set tab }, toggleMetacodeSelected: function () { var self = Metamaps.Create; if ($(this).attr('class') != 'toggledOff') { $(this).addClass('toggledOff'); var value_to_remove = $(this).attr('id'); var name_to_remove = $(this).attr('data-name'); self.newSelectedMetacodes.splice(self.newSelectedMetacodes.indexOf(value_to_remove), 1); self.newSelectedMetacodeNames.splice(self.newSelectedMetacodeNames.indexOf(name_to_remove), 1); } else if ($(this).attr('class') == 'toggledOff') { $(this).removeClass('toggledOff'); self.newSelectedMetacodes.push($(this).attr('id')); self.newSelectedMetacodeNames.push($(this).attr('data-name')); } }, updateMetacodeSet: function (set, index, custom) { if (custom && Metamaps.Create.newSelectedMetacodes.length == 0) { alert('Please select at least one metacode to use!'); return false; } var codesToSwitchToIds; var metacodeModels = new Metamaps.Backbone.MetacodeCollection(); Metamaps.Create.selectedMetacodeSetIndex = index; Metamaps.Create.selectedMetacodeSet = "metacodeset-" + set; if (!custom) { codesToSwitchToIds = $('#metacodeSwitchTabs' + set).attr('data-metacodes').split(','); $('.customMetacodeList li').addClass('toggledOff'); Metamaps.Create.selectedMetacodes = []; Metamaps.Create.selectedMetacodeNames = []; Metamaps.Create.newSelectedMetacodes = []; Metamaps.Create.newSelectedMetacodeNames = []; } else if (custom) { // uses .slice to avoid setting the two arrays to the same actual array Metamaps.Create.selectedMetacodes = Metamaps.Create.newSelectedMetacodes.slice(0); Metamaps.Create.selectedMetacodeNames = Metamaps.Create.newSelectedMetacodeNames.slice(0); codesToSwitchToIds = Metamaps.Create.selectedMetacodes.slice(0); } // sort by name for (var i = 0; i < codesToSwitchToIds.length; i++) { metacodeModels.add( Metamaps.Metacodes.get(codesToSwitchToIds[i]) ); }; metacodeModels.sort(); $('#metacodeImg, #metacodeImgTitle').empty(); $('#metacodeImg').removeData('cloudcarousel'); var newMetacodes = ""; metacodeModels.each(function(metacode){ newMetacodes += '' + metacode.get('name') + ''; }); $('#metacodeImg').empty().append(newMetacodes).CloudCarousel({ titleBox: $('#metacodeImgTitle'), yRadius: 40, xRadius: 190, xPos: 170, yPos: 40, speed: 0.3, mouseWheel: true, bringToFront: true }); Metamaps.GlobalUI.closeLightbox(); $('#topic_name').focus(); var mdata = { "metacodes": { "value": custom ? Metamaps.Create.selectedMetacodes.toString() : Metamaps.Create.selectedMetacodeSet } }; $.ajax({ type: "POST", dataType: 'json', url: "/user/updatemetacodes", data: mdata, success: function (data) { console.log('selected metacodes saved'); }, error: function () { console.log('failed to save selected metacodes'); } }); }, cancelMetacodeSetSwitch: function () { var self = Metamaps.Create; self.isSwitchingSet = false; if (self.selectedMetacodeSet != "metacodeset-custom") { $('.customMetacodeList li').addClass('toggledOff'); self.selectedMetacodes = []; self.selectedMetacodeNames = []; self.newSelectedMetacodes = []; self.newSelectedMetacodeNames = []; } else { // custom set is selected // reset it to the current actual selection $('.customMetacodeList li').addClass('toggledOff'); for (var i = 0; i < self.selectedMetacodes.length; i++) { $('#' + self.selectedMetacodes[i]).removeClass('toggledOff'); }; // uses .slice to avoid setting the two arrays to the same actual array self.newSelectedMetacodeNames = self.selectedMetacodeNames.slice(0); self.newSelectedMetacodes = self.selectedMetacodes.slice(0); } $('#metacodeSwitchTabs').tabs("option", "active", self.selectedMetacodeSetIndex); $('#topic_name').focus(); }, newTopic: { init: function () { $('#topic_name').keyup(function () { Metamaps.Create.newTopic.name = $(this).val(); }); var topicBloodhound = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), queryTokenizer: Bloodhound.tokenizers.whitespace, remote: { url: '/topics/autocomplete_topic?term=%QUERY', wildcard: '%QUERY', }, }); // initialize the autocomplete results for the metacode spinner $('#topic_name').typeahead( { highlight: true, minLength: 2, }, [{ name: 'topic_autocomplete', limit: 8, display: function (s) { return s.label; }, templates: { suggestion: function(s) { return Hogan.compile($('#topicAutocompleteTemplate').html()).render(s); }, }, source: topicBloodhound, }] ); // tell the autocomplete to submit the form with the topic you clicked on if you pick from the autocomplete $('#topic_name').bind('typeahead:select', function (event, datum, dataset) { Metamaps.Topic.getTopicFromAutocomplete(datum.id); }); // initialize metacode spinner and then hide it $("#metacodeImg").CloudCarousel({ titleBox: $('#metacodeImgTitle'), yRadius: 40, xRadius: 190, xPos: 170, yPos: 40, speed: 0.3, mouseWheel: true, bringToFront: true }); $('.new_topic').hide(); }, name: null, newId: 1, beingCreated: false, metacode: null, x: null, y: null, addSynapse: false, open: function () { $('#new_topic').fadeIn('fast', function () { $('#topic_name').focus(); }); Metamaps.Create.newTopic.beingCreated = true; Metamaps.Create.newTopic.name = ""; }, hide: function () { $('#new_topic').fadeOut('fast'); $("#topic_name").typeahead('val', ''); Metamaps.Create.newTopic.beingCreated = false; } }, newSynapse: { init: function () { var self = Metamaps.Create.newSynapse; var synapseBloodhound = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), queryTokenizer: Bloodhound.tokenizers.whitespace, remote: { url: '/search/synapses?term=%QUERY', wildcard: '%QUERY', }, }); var existingSynapseBloodhound = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), queryTokenizer: Bloodhound.tokenizers.whitespace, remote: { url: '/search/synapses?topic1id=%TOPIC1&topic2id=%TOPIC2', prepare: function(query, settings) { var self = Metamaps.Create.newSynapse; if (Metamaps.Selected.Nodes.length < 2) { settings.url = settings.url.replace("%TOPIC1", self.topic1id).replace("%TOPIC2", self.topic2id); return settings; } else { return null; } }, }, }); // initialize the autocomplete results for synapse creation $('#synapse_desc').typeahead( { highlight: true, minLength: 2, }, [{ name: 'synapse_autocomplete', display: function(s) { return s.label; }, templates: { suggestion: function(s) { return Hogan.compile("
    {{label}}
    ").render(s); }, }, source: synapseBloodhound, }, { name: 'existing_synapses', limit: 50, display: function(s) { return s.label; }, templates: { suggestion: function(s) { return Hogan.compile($('#synapseAutocompleteTemplate').html()).render(s); }, header: "

    Existing synapses

    " }, source: existingSynapseBloodhound, }] ); $('#synapse_desc').keyup(function (e) { var ESC = 27, BACKSPACE = 8, DELETE = 46; if (e.keyCode === BACKSPACE && $(this).val() === "" || e.keyCode === DELETE && $(this).val() === "" || e.keyCode === ESC) { Metamaps.Create.newSynapse.hide(); }//if Metamaps.Create.newSynapse.description = $(this).val(); }); $('#synapse_desc').focusout(function() { if (Metamaps.Create.newSynapse.beingCreated) { Metamaps.Synapse.createSynapseLocally(); } }); $('#synapse_desc').bind('typeahead:select', function (event, datum, dataset) { if (datum.id) { // if they clicked on an existing synapse get it Metamaps.Synapse.getSynapseFromAutocomplete(datum.id); } else { Metamaps.Create.newSynapse.description = datum.value; Metamaps.Synapse.createSynapseLocally(); } }); }, beingCreated: false, description: null, topic1id: null, topic2id: null, newSynapseId: null, open: function () { $('#new_synapse').fadeIn(100, function () { $('#synapse_desc').focus(); }); Metamaps.Create.newSynapse.beingCreated = true; }, hide: function () { $('#new_synapse').fadeOut('fast'); $("#synapse_desc").typeahead('val', ''); Metamaps.Create.newSynapse.beingCreated = false; Metamaps.Create.newTopic.addSynapse = false; Metamaps.Create.newSynapse.topic1id = 0; Metamaps.Create.newSynapse.topic2id = 0; Metamaps.Mouse.synapseStartCoordinates = []; Metamaps.Visualize.mGraph.plot(); }, } }; // end Metamaps.Create ////////////////// TOPIC AND SYNAPSE CARDS ////////////////////////// /* * * TOPICCARD * */ Metamaps.TopicCard = { openTopicCard: null, //stores the topic that's currently open authorizedToEdit: false, // stores boolean for edit permission for open topic card init: function () { var self = Metamaps.TopicCard; // initialize best_in_place editing $('.authenticated div.permission.canEdit .best_in_place').best_in_place(); Metamaps.TopicCard.generateShowcardHTML = Hogan.compile($('#topicCardTemplate').html()); // initialize topic card draggability and resizability $('.showcard').draggable({ handle: ".metacodeImage" }); embedly('on', 'card.rendered', self.embedlyCardRendered); }, /** * Will open the Topic Card for the node that it's passed * @param {$jit.Graph.Node} node */ showCard: function (node) { var self = Metamaps.TopicCard; var topic = node.getData('topic'); self.openTopicCard = topic; self.authorizedToEdit = topic.authorizeToEdit(Metamaps.Active.Mapper); //populate the card that's about to show with the right topics data self.populateShowCard(topic); $('.showcard').fadeIn('fast'); }, hideCard: function () { var self = Metamaps.TopicCard; $('.showcard').fadeOut('fast'); self.openTopicCard = null; self.authorizedToEdit = false; }, embedlyCardRendered: function (iframe) { var self = Metamaps.TopicCard; $('#embedlyLinkLoader').hide(); // means that the embedly call returned 404 not found if ($('#embedlyLink')[0]) { $('#embedlyLink').css('display', 'block').fadeIn('fast'); $('.embeds').addClass('nonEmbedlyLink'); } $('.CardOnGraph').addClass('hasAttachment'); if (self.authorizedToEdit) { $('.embeds').append('
    '); $('#linkremove').click(self.removeLink); } }, removeLink: function () { var self = Metamaps.TopicCard; self.openTopicCard.save({ link: null }); $('.embeds').empty().removeClass('nonEmbedlyLink'); $('#addLinkInput input').val(""); $('.attachments').removeClass('hidden'); $('.CardOnGraph').removeClass('hasAttachment'); }, bindShowCardListeners: function (topic) { var self = Metamaps.TopicCard; var showCard = document.getElementById('showcard'); var authorized = self.authorizedToEdit; // get mapper image var setMapperImage = function (mapper) { $('.contributorIcon').attr('src', mapper.get('image')); }; Metamaps.Mapper.get(topic.get('user_id'), setMapperImage); // starting embed.ly var resetFunc = function () { $('#addLinkInput input').val(""); $('#addLinkInput input').focus(); }; var inputEmbedFunc = function (event) { var element = this; setTimeout(function () { var text = $(element).val(); if (event.type=="paste" || (event.type=="keyup" && event.which==13)){ // TODO evaluate converting this to '//' no matter what (infer protocol) if (text.slice(0, 7) !== 'http://' && text.slice(0, 8) !== 'https://' && text.slice(0, 2) !== '//') { text='//'+text; } topic.save({ link: text }); var embedlyEl = $('', { id: 'embedlyLink', 'data-card-description': '0', href: text }).html(text); $('.attachments').addClass('hidden'); $('.embeds').append(embedlyEl); $('.embeds').append('
    '); var loader = new CanvasLoader('embedlyLinkLoader'); loader.setColor('#4fb5c0'); // default is '#000000' loader.setDiameter(28); // default is 40 loader.setDensity(41); // default is 40 loader.setRange(0.9); // default is 1.3 loader.show(); // Hidden by default var e = embedly('card', document.getElementById('embedlyLink')); if (!e) { self.handleInvalidLink(); } } }, 100); }; $('#addLinkReset').click(resetFunc); $('#addLinkInput input').bind("paste keyup",inputEmbedFunc); // initialize the link card, if there is a link if (topic.get('link') && topic.get('link') !== '') { var loader = new CanvasLoader('embedlyLinkLoader'); loader.setColor('#4fb5c0'); // default is '#000000' loader.setDiameter(28); // default is 40 loader.setDensity(41); // default is 40 loader.setRange(0.9); // default is 1.3 loader.show(); // Hidden by default var e = embedly('card', document.getElementById('embedlyLink')); if (!e) { self.handleInvalidLink(); } } var selectingMetacode = false; // attach the listener that shows the metacode title when you hover over the image $('.showcard .metacodeImage').mouseenter(function () { $('.showcard .icon').css('z-index', '4'); $('.showcard .metacodeTitle').show(); }); $('.showcard .linkItem.icon').mouseleave(function () { if (!selectingMetacode) { $('.showcard .metacodeTitle').hide(); $('.showcard .icon').css('z-index', '1'); } }); var metacodeLiClick = function () { selectingMetacode = false; var metacodeId = parseInt($(this).attr('data-id')); var metacode = Metamaps.Metacodes.get(metacodeId); $('.CardOnGraph').find('.metacodeTitle').html(metacode.get('name')) .append('
    ') .attr('class', 'metacodeTitle mbg' + metacode.id); $('.CardOnGraph').find('.metacodeImage').css('background-image', 'url(' + metacode.get('icon') + ')'); topic.save({ metacode_id: metacode.id }); Metamaps.Visualize.mGraph.plot(); $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge'); $('.metacodeTitle').hide(); $('.showcard .icon').css('z-index', '1'); }; var openMetacodeSelect = function (event) { var windowWidth; var showcardLeft; var TOPICCARD_WIDTH = 300; var METACODESELECT_WIDTH = 404; var distanceFromEdge; var MAX_METACODELIST_HEIGHT = 270; var windowHeight; var showcardTop; var topicTitleHeight; var distanceFromBottom; if (!selectingMetacode) { selectingMetacode = true; // this is to make sure the metacode // select is accessible onscreen, when opened // while topic card is close to the right // edge of the screen windowWidth = $(window).width(); showcardLeft = parseInt($('.showcard').css('left')); distanceFromEdge = windowWidth - (showcardLeft + TOPICCARD_WIDTH); if (distanceFromEdge < METACODESELECT_WIDTH) { $('.metacodeSelect').addClass('onRightEdge'); } // this is to make sure the metacode // select is accessible onscreen, when opened // while topic card is close to the bottom // edge of the screen windowHeight = $(window).height(); showcardTop = parseInt($('.showcard').css('top')); topicTitleHeight = $('.showcard .title').height() + parseInt($('.showcard .title').css('padding-top')) + parseInt($('.showcard .title').css('padding-bottom')); heightOfSetList = $('.showcard .metacodeSelect').height(); distanceFromBottom = windowHeight - (showcardTop + topicTitleHeight); if (distanceFromBottom < MAX_METACODELIST_HEIGHT) { $('.metacodeSelect').addClass('onBottomEdge'); } $('.metacodeSelect').show(); event.stopPropagation(); } }; var hideMetacodeSelect = function () { selectingMetacode = false; $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge'); $('.metacodeTitle').hide(); $('.showcard .icon').css('z-index', '1'); }; if (authorized) { $('.showcard .metacodeTitle').click(openMetacodeSelect); $('.showcard').click(hideMetacodeSelect); $('.metacodeSelect > ul > li').click(function (event){ event.stopPropagation(); }); $('.metacodeSelect li li').click(metacodeLiClick); var bipName = $(showCard).find('.best_in_place_name'); bipName.bind("best_in_place:activate", function () { var $el = bipName.find('textarea'); var el = $el[0]; $el.attr('maxlength', '140'); $('.showcard .title').append('
    '); var callback = function (data) { $('.nameCounter.forTopic').html(data.all + '/140'); }; Countable.live(el, callback); }); bipName.bind("best_in_place:deactivate", function () { $('.nameCounter.forTopic').remove(); }); //bind best_in_place ajax callbacks bipName.bind("ajax:success", function () { var name = Metamaps.Util.decodeEntities($(this).html()); topic.set("name", name); topic.trigger('saved'); }); $(showCard).find('.best_in_place_desc').bind("ajax:success", function () { this.innerHTML = this.innerHTML.replace(/\r/g, ''); var desc = $(this).html() === $(this).data('nil') ? "" : $(this).html(); topic.set("desc", desc); topic.trigger('saved'); }); } var permissionLiClick = function (event) { selectingPermission = false; var permission = $(this).attr('class'); topic.save({ permission: permission }); $('.showcard .mapPerm').removeClass('co pu pr minimize').addClass(permission.substring(0, 2)); $('.showcard .permissionSelect').remove(); event.stopPropagation(); }; var openPermissionSelect = function (event) { if (!selectingPermission) { selectingPermission = true; $(this).addClass('minimize'); // this line flips the drop down arrow to a pull up arrow if ($(this).hasClass('co')) { $(this).append(''); } else if ($(this).hasClass('pu')) { $(this).append(''); } else if ($(this).hasClass('pr')) { $(this).append(''); } $('.showcard .permissionSelect li').click(permissionLiClick); event.stopPropagation(); } }; var hidePermissionSelect = function () { selectingPermission = false; $('.showcard .yourTopic .mapPerm').removeClass('minimize'); // this line flips the pull up arrow to a drop down arrow $('.showcard .permissionSelect').remove(); }; // ability to change permission var selectingPermission = false; if (topic.authorizePermissionChange(Metamaps.Active.Mapper)) { $('.showcard .yourTopic .mapPerm').click(openPermissionSelect); $('.showcard').click(hidePermissionSelect); } $('.links .mapCount').unbind().click(function(event){ $('.mapCount .tip').toggle(); $('.showcard .hoverTip').toggleClass('hide'); event.stopPropagation(); }); $('.mapCount .tip').unbind().click(function(event){ event.stopPropagation(); }); $('.showcard').unbind('.hideTip').bind('click.hideTip', function(){ $('.mapCount .tip').hide(); $('.showcard .hoverTip').removeClass('hide'); }); $('.mapCount .tip li a').click(Metamaps.Router.intercept); var originalText = $('.showMore').html(); $('.mapCount .tip .showMore').unbind().toggle( function(event){ $('.extraText').toggleClass("hideExtra"); $('.showMore').html('Show less...'); }, function(event){ $('.extraText').toggleClass("hideExtra"); $('.showMore').html(originalText); }); $('.mapCount .tip showMore').unbind().click(function(event){ event.stopPropagation(); }); }, handleInvalidLink: function() { var self = Metamaps.TopicCard; self.removeLink(); Metamaps.GlobalUI.notifyUser("Invalid link"); }, populateShowCard: function (topic) { var self = Metamaps.TopicCard; var showCard = document.getElementById('showcard'); $(showCard).find('.permission').remove(); var topicForTemplate = self.buildObject(topic); var html = self.generateShowcardHTML.render(topicForTemplate); if (topic.authorizeToEdit(Metamaps.Active.Mapper)) { var perm = document.createElement('div'); var string = 'permission canEdit'; if (topic.authorizePermissionChange(Metamaps.Active.Mapper)) string += ' yourTopic'; perm.className = string; perm.innerHTML = html; showCard.appendChild(perm); } else { var perm = document.createElement('div'); perm.className = 'permission cannotEdit'; perm.innerHTML = html; showCard.appendChild(perm); } Metamaps.TopicCard.bindShowCardListeners(topic); }, generateShowcardHTML: null, // will be initialized into a Hogan template within init function //generateShowcardHTML buildObject: function (topic) { var self=Metamaps.TopicCard; var nodeValues = {}; var authorized = topic.authorizeToEdit(Metamaps.Active.Mapper); if (!authorized) { } else { } var desc_nil = "Click to add description..."; nodeValues.attachmentsHidden = ''; if (topic.get('link') && topic.get('link')!== '') { nodeValues.embeds = '
    '; nodeValues.embeds += topic.get('link'); nodeValues.embeds += '
    '; nodeValues.attachmentsHidden = 'hidden'; nodeValues.hasAttachment = "hasAttachment"; } else { nodeValues.embeds = ''; nodeValues.hasAttachment = ''; } if (authorized) { nodeValues.attachments = ''; } else { nodeValues.attachmentsHidden = 'hidden'; nodeValues.attachments = ''; } var inmapsAr = topic.get("inmaps"); var inmapsLinks = topic.get("inmapsLinks"); nodeValues.inmaps =''; if (inmapsAr.length < 6) { for (i = 0; i < inmapsAr.length; i++) { var url = "/maps/" + inmapsLinks[i]; nodeValues.inmaps += '
  • ' + inmapsAr[i]+ '
  • '; } } else { for (i = 0; i < 5; i++){ var url = "/maps/" + inmapsLinks[i]; nodeValues.inmaps += '
  • ' + inmapsAr[i] + '
  • '; } extra = inmapsAr.length - 5; nodeValues.inmaps += '
  • See ' + extra + ' more...
  • ' for (i = 5; i < inmapsAr.length; i++){ var url = "/maps/" + inmapsLinks[i]; nodeValues.inmaps += '
  • ' + inmapsAr[i]+ '
  • '; } } nodeValues.permission = topic.get("permission"); nodeValues.mk_permission = topic.get("permission").substring(0, 2); nodeValues.map_count = topic.get("map_count").toString(); nodeValues.synapse_count = topic.get("synapse_count").toString(); nodeValues.id = topic.isNew() ? topic.cid : topic.id; nodeValues.metacode = topic.getMetacode().get("name"); nodeValues.metacode_class = 'mbg' + topic.get('metacode_id'); nodeValues.imgsrc = topic.getMetacode().get("icon"); nodeValues.name = topic.get("name"); nodeValues.userid = topic.get("user_id"); nodeValues.username = topic.get("user_name"); nodeValues.date = topic.getDate(); // the code for this is stored in /views/main/_metacodeOptions.html.erb nodeValues.metacode_select = $('#metacodeOptions').html(); nodeValues.desc_nil = desc_nil; nodeValues.desc = (topic.get("desc") == "" && authorized) ? desc_nil : topic.get("desc"); return nodeValues; } }; // end Metamaps.TopicCard /* * * SYNAPSECARD * */ Metamaps.SynapseCard = { openSynapseCard: null, showCard: function (edge, e) { var self = Metamaps.SynapseCard; //reset so we don't interfere with other edges, but first, save its x and y var myX = $('#edit_synapse').css('left'); var myY = $('#edit_synapse').css('top'); $('#edit_synapse').remove(); //so label is missing while editing Metamaps.Control.deselectEdge(edge); var index = edge.getData("displayIndex") ? edge.getData("displayIndex") : 0; var synapse = edge.getData('synapses')[index]; // for now, just get the first synapse //create the wrapper around the form elements, including permissions //classes to make best_in_place happy var edit_div = document.createElement('div'); edit_div.innerHTML = '
    '; edit_div.setAttribute('id', 'edit_synapse'); if (synapse.authorizeToEdit(Metamaps.Active.Mapper)) { edit_div.className = 'permission canEdit'; edit_div.className += synapse.authorizePermissionChange(Metamaps.Active.Mapper) ? ' yourEdge' : ''; } else { edit_div.className = 'permission cannotEdit'; } $('#wrapper').append(edit_div); self.populateShowCard(edge, synapse); //drop it in the right spot, activate it $('#edit_synapse').css('position', 'absolute'); if (e) { $('#edit_synapse').css('left', e.clientX); $('#edit_synapse').css('top', e.clientY); } else { $('#edit_synapse').css('left', myX); $('#edit_synapse').css('top', myY); } //$('#edit_synapse_name').click(); //required in case name is empty //$('#edit_synapse_name input').focus(); $('#edit_synapse').show(); self.openSynapseCard = edge; }, hideCard: function () { $('#edit_synapse').remove(); Metamaps.SynapseCard.openSynapseCard = null; }, populateShowCard: function (edge, synapse) { var self = Metamaps.SynapseCard; self.add_synapse_count(edge); self.add_desc_form(synapse); self.add_drop_down(edge, synapse); self.add_user_info(synapse); self.add_perms_form(synapse); self.add_direction_form(synapse); }, add_synapse_count: function (edge) { var count = edge.getData("synapses").length; $('#editSynUpperBar').append('
    ' + count + '
    ') }, add_desc_form: function (synapse) { var data_nil = 'Click to add description.'; // TODO make it so that this would work even in sandbox mode, // currently with Best_in_place it won't //desc editing form $('#editSynUpperBar').append('
    '); $('#edit_synapse_desc').attr('class', 'best_in_place best_in_place_desc'); $('#edit_synapse_desc').attr('data-object', 'synapse'); $('#edit_synapse_desc').attr('data-attribute', 'desc'); $('#edit_synapse_desc').attr('data-type', 'textarea'); $('#edit_synapse_desc').attr('data-nil', data_nil); $('#edit_synapse_desc').attr('data-url', '/synapses/' + synapse.id); $('#edit_synapse_desc').html(synapse.get("desc")); //if edge data is blank or just whitespace, populate it with data_nil if ($('#edit_synapse_desc').html().trim() == '') { if (synapse.authorizeToEdit(Metamaps.Active.Mapper)) { $('#edit_synapse_desc').html(data_nil); } else { $('#edit_synapse_desc').html("(no description)"); } } $('#edit_synapse_desc').bind("ajax:success", function () { var desc = $(this).html(); if (desc == data_nil) { synapse.set("desc", ''); } else { synapse.set("desc", desc); } synapse.trigger('saved'); Metamaps.Control.selectEdge(synapse.get('edge')); Metamaps.Visualize.mGraph.plot(); }); }, add_drop_down: function (edge, synapse) { var list, i, synapses, l, desc; synapses = edge.getData("synapses"); l = synapses.length; if (l > 1) { // append the element that you click to show dropdown select $('#editSynUpperBar').append(''); $('#dropdownSynapses').click(function(e){ e.preventDefault(); e.stopPropagation(); // stop it from immediately closing it again $('#switchSynapseList').toggle(); }); // hide the dropdown again if you click anywhere else on the synapse card $('#edit_synapse').click(function(){ $('#switchSynapseList').hide(); }); // generate the list of other synapses list = '' // add the list of the other synapses $('#editSynLowerBar').append(list); // attach click listeners to list items that // will cause it to switch the displayed synapse // when you click it $('#switchSynapseList li').click(function(e){ e.stopPropagation(); var index = parseInt($(this).attr('data-synapse-index')); edge.setData('displayIndex', index); Metamaps.Visualize.mGraph.plot(); Metamaps.SynapseCard.showCard(edge, false); }); } }, add_user_info: function (synapse) { var u = '
    '; u += ' ' u += '
    ' + synapse.get("user_name") + '
    '; $('#editSynLowerBar').append(u); // get mapper image var setMapperImage = function (mapper) { $('#edgeUser img').attr('src', mapper.get('image')); }; Metamaps.Mapper.get(synapse.get('user_id'), setMapperImage); }, add_perms_form: function (synapse) { //permissions - if owner, also allow permission editing $('#editSynLowerBar').append('
    '); // ability to change permission var selectingPermission = false; var permissionLiClick = function (event) { selectingPermission = false; var permission = $(this).attr('class'); synapse.save({ permission: permission }); $('#edit_synapse .mapPerm').removeClass('co pu pr minimize').addClass(permission.substring(0, 2)); $('#edit_synapse .permissionSelect').remove(); event.stopPropagation(); }; var openPermissionSelect = function (event) { if (!selectingPermission) { selectingPermission = true; $(this).addClass('minimize'); // this line flips the drop down arrow to a pull up arrow if ($(this).hasClass('co')) { $(this).append(''); } else if ($(this).hasClass('pu')) { $(this).append(''); } else if ($(this).hasClass('pr')) { $(this).append(''); } $('#edit_synapse .permissionSelect li').click(permissionLiClick); event.stopPropagation(); } }; var hidePermissionSelect = function () { selectingPermission = false; $('#edit_synapse.yourEdge .mapPerm').removeClass('minimize'); // this line flips the pull up arrow to a drop down arrow $('#edit_synapse .permissionSelect').remove(); }; if (synapse.authorizePermissionChange(Metamaps.Active.Mapper)) { $('#edit_synapse.yourEdge .mapPerm').click(openPermissionSelect); $('#edit_synapse').click(hidePermissionSelect); } }, //add_perms_form add_direction_form: function (synapse) { //directionality checkboxes $('#editSynLowerBar').append('
    '); $('#editSynLowerBar').append('
    '); var edge = synapse.get('edge'); //determine which node is to the left and the right //if directly in a line, top is left if (edge.nodeFrom.pos.x < edge.nodeTo.pos.x || edge.nodeFrom.pos.x == edge.nodeTo.pos.x && edge.nodeFrom.pos.y < edge.nodeTo.pos.y) { var left = edge.nodeTo.getData("topic"); var right = edge.nodeFrom.getData("topic"); } else { var left = edge.nodeFrom.getData("topic"); var right = edge.nodeTo.getData("topic"); } /* * One node is actually on the left onscreen. Call it left, & the other right. * If category is from-to, and that node is first, check the 'right' checkbox. * Else check the 'left' checkbox since the arrow is incoming. */ var directionCat = synapse.get('category'); //both, none, from-to if (directionCat == 'from-to') { var from_to = [synapse.get("node1_id"), synapse.get("node2_id")]; if (from_to[0] == left.id) { //check left checkbox $('#edit_synapse_left').addClass('checked'); } else { //check right checkbox $('#edit_synapse_right').addClass('checked'); } } else if (directionCat == 'both') { //check both checkboxes $('#edit_synapse_left').addClass('checked'); $('#edit_synapse_right').addClass('checked'); } if (synapse.authorizeToEdit(Metamaps.Active.Mapper)) { $('#edit_synapse_left, #edit_synapse_right').click(function () { $(this).toggleClass('checked'); var leftChecked = $('#edit_synapse_left').is('.checked'); var rightChecked = $('#edit_synapse_right').is('.checked'); var dir = synapse.getDirection(); var dirCat = 'none'; if (leftChecked && rightChecked) { dirCat = 'both'; } else if (!leftChecked && rightChecked) { dirCat = 'from-to'; dir = [right.id, left.id]; } else if (leftChecked && !rightChecked) { dirCat = 'from-to'; dir = [left.id, right.id]; } synapse.save({ category: dirCat, node1_id: dir[0], node2_id: dir[1] }); Metamaps.Visualize.mGraph.plot(); }); } // if } //add_direction_form }; // end Metamaps.SynapseCard ////////////////////// END TOPIC AND SYNAPSE CARDS ////////////////////////////////// /* * * VISUALIZE * */ Metamaps.Visualize = { mGraph: null, // a reference to the graph object. cameraPosition: null, // stores the camera position when using a 3D visualization type: "ForceDirected", // the type of graph we're building, could be "RGraph", "ForceDirected", or "ForceDirected3D" loadLater: false, // indicates whether there is JSON that should be loaded right in the offset, or whether to wait till the first topic is created init: function () { var self = Metamaps.Visualize; // disable awkward dragging of the canvas element that would sometimes happen $('#infovis-canvas').on('dragstart', function (event) { event.preventDefault(); }); // prevent touch events on the canvas from default behaviour $("#infovis-canvas").bind('touchstart', function (event) { event.preventDefault(); self.mGraph.events.touched = true; }); // prevent touch events on the canvas from default behaviour $("#infovis-canvas").bind('touchmove', function (event) { //Metamaps.JIT.touchPanZoomHandler(event); }); // prevent touch events on the canvas from default behaviour $("#infovis-canvas").bind('touchend touchcancel', function (event) { lastDist = 0; if (!self.mGraph.events.touchMoved && !Metamaps.Touch.touchDragNode) Metamaps.TopicCard.hideCurrentCard(); self.mGraph.events.touched = self.mGraph.events.touchMoved = false; Metamaps.Touch.touchDragNode = false; }); }, computePositions: function () { var self = Metamaps.Visualize, mapping; if (self.type == "RGraph") { var i, l, startPos, endPos, topic, synapse; self.mGraph.graph.eachNode(function (n) { topic = Metamaps.Topics.get(n.id); topic.set({ node: n }, { silent: true }); topic.updateNode(); n.eachAdjacency(function (edge) { if(!edge.getData('init')) { edge.setData('init', true); l = edge.getData('synapseIDs').length; for (i = 0; i < l; i++) { synapse = Metamaps.Synapses.get(edge.getData('synapseIDs')[i]); synapse.set({ edge: edge }, { silent: true }); synapse.updateEdge(); } } }); var pos = n.getPos(); pos.setc(-200, -200); }); self.mGraph.compute('end'); } else if (self.type == "ForceDirected") { var i, l, startPos, endPos, topic, synapse; self.mGraph.graph.eachNode(function (n) { topic = Metamaps.Topics.get(n.id); topic.set({ node: n }, { silent: true }); topic.updateNode(); mapping = topic.getMapping(); n.eachAdjacency(function (edge) { if(!edge.getData('init')) { edge.setData('init', true); l = edge.getData('synapseIDs').length; for (i = 0; i < l; i++) { synapse = Metamaps.Synapses.get(edge.getData('synapseIDs')[i]); synapse.set({ edge: edge }, { silent: true }); synapse.updateEdge(); } } }); startPos = new $jit.Complex(0, 0); endPos = new $jit.Complex(mapping.get('xloc'), mapping.get('yloc')); n.setPos(startPos, 'start'); n.setPos(endPos, 'end'); }); } else if (self.type == "ForceDirected3D") { self.mGraph.compute(); } }, /** * render does the heavy lifting of creating the engine that renders the graph with the properties we desire * */ render: function () { var self = Metamaps.Visualize, RGraphSettings, FDSettings; if (self.type == "RGraph" && (!self.mGraph || self.mGraph instanceof $jit.ForceDirected)) { RGraphSettings = $.extend(true, {}, Metamaps.JIT.ForceDirected.graphSettings); $jit.RGraph.Plot.NodeTypes.implement(Metamaps.JIT.ForceDirected.nodeSettings); $jit.RGraph.Plot.EdgeTypes.implement(Metamaps.JIT.ForceDirected.edgeSettings); RGraphSettings.width = $(document).width(); RGraphSettings.height = $(document).height(); RGraphSettings.background = Metamaps.JIT.RGraph.background; RGraphSettings.levelDistance = Metamaps.JIT.RGraph.levelDistance; self.mGraph = new $jit.RGraph(RGraphSettings); } else if (self.type == "ForceDirected" && (!self.mGraph || self.mGraph instanceof $jit.RGraph)) { FDSettings = $.extend(true, {}, Metamaps.JIT.ForceDirected.graphSettings); $jit.ForceDirected.Plot.NodeTypes.implement(Metamaps.JIT.ForceDirected.nodeSettings); $jit.ForceDirected.Plot.EdgeTypes.implement(Metamaps.JIT.ForceDirected.edgeSettings); FDSettings.width = $('body').width(); FDSettings.height = $('body').height(); self.mGraph = new $jit.ForceDirected(FDSettings); } else if (self.type == "ForceDirected3D" && !self.mGraph) { // init ForceDirected3D self.mGraph = new $jit.ForceDirected3D(Metamaps.JIT.ForceDirected3D.graphSettings); self.cameraPosition = self.mGraph.canvas.canvases[0].camera.position; } else { self.mGraph.graph.empty(); } function runAnimation() { Metamaps.Loading.hide(); // load JSON data, if it's not empty if (!self.loadLater) { //load JSON data. var rootIndex = 0; if (Metamaps.Active.Topic) { var node = _.find(Metamaps.JIT.vizData, function(node){ return node.id === Metamaps.Active.Topic.id; }); rootIndex = _.indexOf(Metamaps.JIT.vizData, node); } self.mGraph.loadJSON(Metamaps.JIT.vizData, rootIndex); //compute positions and plot. self.computePositions(); self.mGraph.busy = true; if (self.type == "RGraph") { self.mGraph.fx.animate(Metamaps.JIT.RGraph.animate); } else if (self.type == "ForceDirected") { self.mGraph.animate(Metamaps.JIT.ForceDirected.animateSavedLayout); } else if (self.type == "ForceDirected3D") { self.mGraph.animate(Metamaps.JIT.ForceDirected.animateFDLayout); } } } // hold until all the needed metacode images are loaded // hold for a maximum of 80 passes, or 4 seconds of waiting time var tries = 0; function hold() { var unique = _.uniq(Metamaps.Topics.models, function (metacode) { return metacode.get('metacode_id'); }), requiredMetacodes = _.map(unique, function (metacode) { return metacode.get('metacode_id'); }), loadedCount = 0; _.each(requiredMetacodes, function (metacode_id) { var metacode = Metamaps.Metacodes.get(metacode_id), img = metacode ? metacode.get('image') : false; if (img && (img.complete || (typeof img.naturalWidth !== "undefined" && img.naturalWidth !== 0))) { loadedCount += 1; } }); if (loadedCount === requiredMetacodes.length || tries > 80) runAnimation(); else setTimeout(function(){ tries++; hold() }, 50); } hold(); // update the url now that the map is ready clearTimeout(Metamaps.Router.timeoutId); Metamaps.Router.timeoutId = setTimeout(function(){ var m = Metamaps.Active.Map; var t = Metamaps.Active.Topic; if (m && window.location.pathname !== "/maps/" + m.id) { Metamaps.Router.navigate("/maps/" + m.id); } else if (t && window.location.pathname !== "/topics/" + t.id) { Metamaps.Router.navigate("/topics/" + t.id); } }, 800); } }; // end Metamaps.Visualize /* * * UTIL * */ Metamaps.Util = { // helper function to determine how many lines are needed // Line Splitter Function // copyright Stephen Chapman, 19th April 2006 // you may copy this code but please keep the copyright notice as well splitLine: function (st, n) { var b = ''; var s = st ? st : ''; while (s.length > n) { var c = s.substring(0, n); var d = c.lastIndexOf(' '); var e = c.lastIndexOf('\n'); if (e != -1) d = e; if (d == -1) d = n; b += c.substring(0, d) + '\n'; s = s.substring(d + 1); } return b + s; }, nowDateFormatted: function () { var date = new Date(Date.now()); var month = (date.getMonth() + 1) < 10 ? '0' + (date.getMonth() + 1) : (date.getMonth() + 1); var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); var year = date.getFullYear(); return month + '/' + day + '/' + year; }, decodeEntities: function (desc) { var str, temp = document.createElement('p'); temp.innerHTML = desc; //browser handles the topics str = temp.textContent || temp.innerText; temp = null; //delete the element; return str; }, //decodeEntities getDistance: function (p1, p2) { return Math.sqrt(Math.pow((p2.x - p1.x), 2) + Math.pow((p2.y - p1.y), 2)); }, coordsToPixels: function (coords) { if (Metamaps.Visualize.mGraph) { var canvas = Metamaps.Visualize.mGraph.canvas, s = canvas.getSize(), p = canvas.getPos(), ox = canvas.translateOffsetX, oy = canvas.translateOffsetY, sx = canvas.scaleOffsetX, sy = canvas.scaleOffsetY; var pixels = { x: (coords.x / (1/sx)) + p.x + s.width/2 + ox, y: (coords.y / (1/sy)) + p.y + s.height/2 + oy }; return pixels; } else { return { x: 0, y: 0 }; } }, pixelsToCoords: function (pixels) { var coords; if (Metamaps.Visualize.mGraph) { var canvas = Metamaps.Visualize.mGraph.canvas, s = canvas.getSize(), p = canvas.getPos(), ox = canvas.translateOffsetX, oy = canvas.translateOffsetY, sx = canvas.scaleOffsetX, sy = canvas.scaleOffsetY; coords = { x: (pixels.x - p.x - s.width/2 - ox) * (1/sx), y: (pixels.y - p.y - s.height/2 - oy) * (1/sy), }; } else { coords = { x: 0, y: 0 }; } return coords; }, getPastelColor: function () { var r = (Math.round(Math.random()* 127) + 127).toString(16); var g = (Math.round(Math.random()* 127) + 127).toString(16); var b = (Math.round(Math.random()* 127) + 127).toString(16); return Metamaps.Util.colorLuminance('#' + r + g + b, -0.4); }, // darkens a hex value by 'lum' percentage colorLuminance: function (hex, lum) { // validate hex string hex = String(hex).replace(/[^0-9a-f]/gi, ''); if (hex.length < 6) { hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; } lum = lum || 0; // convert to decimal and change luminosity var rgb = "#", c, i; for (i = 0; i < 3; i++) { c = parseInt(hex.substr(i*2,2), 16); c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); rgb += ("00"+c).substr(c.length); } return rgb; }, generateOptionsList: function (data) { var newlist = ""; for (var i = 0; i < data.length; i++) { newlist = newlist + ''; } return newlist; }, checkURLisImage: function (url) { // when the page reloads the following regular expression will be screwed up // please replace it with this one before you save: /*backslashhere*.(jpeg|jpg|gif|png)$/ return (url.match(/\.(jpeg|jpg|gif|png)$/) != null); }, checkURLisYoutubeVideo: function (url) { return (url.match(/^https?:\/\/(?:www\.)?youtube.com\/watch\?(?=[^?]*v=\w+)(?:[^\s?]+)?$/) != null); } }; // end Metamaps.Util /* * * REALTIME * */ Metamaps.Realtime = { videoId: 'video-wrapper', socket: null, webrtc: null, readyToCall: false, mappersOnMap: {}, disconnected: false, chatOpen: false, status: true, // stores whether realtime is True/On or False/Off, broadcastingStatus: false, inConversation: false, localVideo: null, init: function () { var self = Metamaps.Realtime; self.addJuntoListeners(); self.socket = new SocketIoConnection({ url: '<%= ENV['REALTIME_SERVER'] %>' }); self.socket.on('connect', function () { console.log('connected'); if (!self.disconnected) { self.startActiveMap(); } else self.disconnected = false; }); self.socket.on('disconnect', function () { self.disconnected = true; }); if (Metamaps.Active.Mapper) { self.webrtc = new SimpleWebRTC({ connection: self.socket, localVideoEl: self.videoId, remoteVideosEl: '', detectSpeakingEvents: true, autoAdjustMic: false, //true, autoRequestMedia: false, localVideo: { autoplay: true, mirror: true, muted: true }, media: { video: true, audio: true }, nick: Metamaps.Active.Mapper.id }); var $video = $('').attr('id', self.videoId); self.localVideo = { $video: $video, view: new Metamaps.Views.videoView($video[0], $('body'), 'me', true, { DOUBLE_CLICK_TOLERANCE: 200, avatar: Metamaps.Active.Mapper ? Metamaps.Active.Mapper.get('image') : '' }) }; self.room = new Metamaps.Views.room({ webrtc: self.webrtc, socket: self.socket, username: Metamaps.Active.Mapper ? Metamaps.Active.Mapper.get('name') : '', image: Metamaps.Active.Mapper ? Metamaps.Active.Mapper.get('image') : '', room: 'global', $video: self.localVideo.$video, myVideoView: self.localVideo.view, config: { DOUBLE_CLICK_TOLERANCE: 200 } }); self.room.videoAdded(self.handleVideoAdded); self.room.chat.$container.hide(); $('body').prepend(self.room.chat.$container); } // if Metamaps.Active.Mapper }, addJuntoListeners: function () { var self = Metamaps.Realtime; $(document).on(Metamaps.Views.chatView.events.openTray, function () { $('.main').addClass('compressed'); self.chatOpen = true; self.positionPeerIcons(); }); $(document).on(Metamaps.Views.chatView.events.closeTray, function () { $('.main').removeClass('compressed'); self.chatOpen = false; self.positionPeerIcons(); }); $(document).on(Metamaps.Views.chatView.events.videosOn, function () { $('#wrapper').removeClass('hideVideos'); }); $(document).on(Metamaps.Views.chatView.events.videosOff, function () { $('#wrapper').addClass('hideVideos'); }); $(document).on(Metamaps.Views.chatView.events.cursorsOn, function () { $('#wrapper').removeClass('hideCursors'); }); $(document).on(Metamaps.Views.chatView.events.cursorsOff, function () { $('#wrapper').addClass('hideCursors'); }); }, handleVideoAdded: function (v, id) { var self = Metamaps.Realtime; self.positionVideos(); v.setParent($('#wrapper')); v.$container.find('.video-cutoff').css({ border: '4px solid ' + self.mappersOnMap[id].color }); $('#wrapper').append(v.$container); }, positionVideos: function () { var self = Metamaps.Realtime; var videoIds = Object.keys(self.room.videos); var numOfVideos = videoIds.length; var numOfVideosToPosition = _.filter(videoIds, function (id) { return !self.room.videos[id].manuallyPositioned; }).length; var screenHeight = $(document).height(); var screenWidth = $(document).width(); var topExtraPadding = 20; var topPadding = 30; var leftPadding = 30; var videoHeight = 150; var videoWidth = 180; var column = 0; var row = 0; var yFormula = function () { var y = topExtraPadding + (topPadding + videoHeight)*row + topPadding; if (y + videoHeight > screenHeight) { row = 0; column += 1; y = yFormula(); } row++; return y; }; var xFormula = function () { var x = (leftPadding + videoWidth)*column + leftPadding; return x; }; // do self first var myVideo = Metamaps.Realtime.localVideo.view; if (!myVideo.manuallyPositioned) { myVideo.$container.css({ top: yFormula() + 'px', left: xFormula() + 'px' }); } videoIds.forEach(function (id) { var video = self.room.videos[id]; if (!video.manuallyPositioned) { video.$container.css({ top: yFormula() + 'px', left: xFormula() + 'px' }); } }); }, startActiveMap: function () { var self = Metamaps.Realtime; if (Metamaps.Active.Map && Metamaps.Active.Mapper) { var commonsMap = Metamaps.Active.Map.get('permission') === 'commons'; var publicMap = Metamaps.Active.Map.get('permission') === 'public'; if (commonsMap) { self.turnOn(); self.setupSocket(); } else if (publicMap) { self.attachMapListener(); } self.room.addMessages(new Metamaps.Backbone.MessageCollection(Metamaps.Messages), true); } }, endActiveMap: function () { var self = Metamaps.Realtime; $(document).off('mousemove'); self.socket.removeAllListeners(); if (self.inConversation) self.leaveCall(); self.socket.emit('endMapperNotify'); $(".collabCompass").remove(); self.status = false; self.room.leave(); self.room.chat.$container.hide(); self.room.chat.close(); }, reenableRealtime: function() { var confirmString = "The layout of your map has fallen out of sync with the saved copy. "; confirmString += "To save your changes without overwriting the map, hit 'Cancel' and "; confirmString += "then use 'Save to new map'. "; confirmString += "Do you want to discard your changes and enable realtime?"; var c = confirm(confirmString); if (c) { Metamaps.Router.maps(Metamaps.Active.Map.id); } }, turnOn: function (notify) { var self = Metamaps.Realtime; if (notify) self.sendRealtimeOn(); //$(".rtMapperSelf").removeClass('littleRtOff').addClass('littleRtOn'); //$('.rtOn').addClass('active'); //$('.rtOff').removeClass('active'); self.status = true; //$(".sidebarCollaborateIcon").addClass("blue"); $(".collabCompass").show(); self.room.chat.$container.show(); self.room.room = 'map-' + Metamaps.Active.Map.id; self.checkForACallToJoin(); self.activeMapper = { id: Metamaps.Active.Mapper.id, name: Metamaps.Active.Mapper.get('name'), username: Metamaps.Active.Mapper.get('name'), image: Metamaps.Active.Mapper.get('image'), color: Metamaps.Util.getPastelColor(), self: true }; self.localVideo.view.$container.find('.video-cutoff').css({ border: '4px solid ' + self.activeMapper.color }); self.room.chat.addParticipant(self.activeMapper); }, checkForACallToJoin: function () { var self = Metamaps.Realtime; self.socket.emit('checkForCall', { room: self.room.room, mapid: Metamaps.Active.Map.id }); }, promptToJoin: function () { var self = Metamaps.Realtime; var notifyText = 'There\'s a conversation happening, want to join?'; notifyText += ' '; notifyText += ' '; Metamaps.GlobalUI.notifyUser(notifyText, true); self.room.conversationInProgress(); }, conversationHasBegun: function () { var self = Metamaps.Realtime; if (self.inConversation) return; var notifyText = 'There\'s a conversation starting, want to join?'; notifyText += ' '; notifyText += ' '; Metamaps.GlobalUI.notifyUser(notifyText, true); self.room.conversationInProgress(); }, countOthersInConversation: function () { var self = Metamaps.Realtime; var count = 0; for (var key in self.mappersOnMap) { if (self.mappersOnMap[key].inConversation) count++; } return count; }, mapperJoinedCall: function (id) { var self = Metamaps.Realtime; var mapper = self.mappersOnMap[id]; if (mapper) { if (self.inConversation) { var username = mapper.name; var notifyText = username + ' joined the call'; Metamaps.GlobalUI.notifyUser(notifyText); } mapper.inConversation = true; self.room.chat.mapperJoinedCall(id); } }, mapperLeftCall: function (id) { var self = Metamaps.Realtime; var mapper = self.mappersOnMap[id]; if (mapper) { if (self.inConversation) { var username = mapper.name; var notifyText = username + ' left the call'; Metamaps.GlobalUI.notifyUser(notifyText); } mapper.inConversation = false; self.room.chat.mapperLeftCall(id); if ((self.inConversation && self.countOthersInConversation() === 0) || (!self.inConversation && self.countOthersInConversation() === 1)) { self.callEnded(); } } }, callEnded: function () { var self = Metamaps.Realtime; self.room.conversationEnding(); self.room.leaveVideoOnly(); self.inConversation = false; self.localVideo.view.$container.hide().css({ top: '72px', left: '30px' }); self.localVideo.view.audioOn(); self.localVideo.view.videoOn(); self.webrtc.webrtc.localStreams.forEach(function (stream) { stream.getTracks().forEach(function (track) { track.stop(); }); }); self.webrtc.webrtc.localStreams = []; }, invitedToCall: function (inviter) { var self = Metamaps.Realtime; self.room.chat.sound.stop('sessioninvite'); self.room.chat.sound.play('sessioninvite'); var username = self.mappersOnMap[inviter].name; var notifyText = "' style='display: inline-block; margin-top: -12px; vertical-align: top;' />"; notifyText += username + ' is inviting you to a conversation. Join live?'; notifyText += ' '; notifyText += ' '; Metamaps.GlobalUI.notifyUser(notifyText, true); }, invitedToJoin: function (inviter) { var self = Metamaps.Realtime; self.room.chat.sound.stop('sessioninvite'); self.room.chat.sound.play('sessioninvite'); var username = self.mappersOnMap[inviter].name; var notifyText = username + ' is inviting you to the conversation. Join?'; notifyText += ' '; notifyText += ' '; Metamaps.GlobalUI.notifyUser(notifyText, true); }, acceptCall: function (userid) { var self = Metamaps.Realtime; self.room.chat.sound.stop('sessioninvite'); self.socket.emit('callAccepted', { mapid: Metamaps.Active.Map.id, invited: Metamaps.Active.Mapper.id, inviter: userid }); $.post('/maps/' + Metamaps.Active.Map.id + '/events/conversation'); self.joinCall(); Metamaps.GlobalUI.clearNotify(); }, denyCall: function (userid) { var self = Metamaps.Realtime; self.room.chat.sound.stop('sessioninvite'); self.socket.emit('callDenied', { mapid: Metamaps.Active.Map.id, invited: Metamaps.Active.Mapper.id, inviter: userid }); Metamaps.GlobalUI.clearNotify(); }, denyInvite: function (userid) { var self = Metamaps.Realtime; self.room.chat.sound.stop('sessioninvite'); self.socket.emit('inviteDenied', { mapid: Metamaps.Active.Map.id, invited: Metamaps.Active.Mapper.id, inviter: userid }); Metamaps.GlobalUI.clearNotify(); }, inviteACall: function (userid) { var self = Metamaps.Realtime; self.socket.emit('inviteACall', { mapid: Metamaps.Active.Map.id, inviter: Metamaps.Active.Mapper.id, invited: userid }); self.room.chat.invitationPending(userid); Metamaps.GlobalUI.clearNotify(); }, inviteToJoin: function (userid) { var self = Metamaps.Realtime; self.socket.emit('inviteToJoin', { mapid: Metamaps.Active.Map.id, inviter: Metamaps.Active.Mapper.id, invited: userid }); self.room.chat.invitationPending(userid); }, callAccepted: function (userid) { var self = Metamaps.Realtime; var username = self.mappersOnMap[userid].name; Metamaps.GlobalUI.notifyUser('Conversation starting...'); self.joinCall(); self.room.chat.invitationAnswered(userid); }, callDenied: function (userid) { var self = Metamaps.Realtime; var username = self.mappersOnMap[userid].name; Metamaps.GlobalUI.notifyUser(username + ' didn\'t accept your invitation'); self.room.chat.invitationAnswered(userid); }, inviteDenied: function (userid) { var self = Metamaps.Realtime; var username = self.mappersOnMap[userid].name; Metamaps.GlobalUI.notifyUser(username + ' didn\'t accept your invitation'); self.room.chat.invitationAnswered(userid); }, joinCall: function () { var self = Metamaps.Realtime; self.webrtc.off('readyToCall'); self.webrtc.once('readyToCall', function () { self.videoInitialized = true; self.readyToCall = true; self.localVideo.view.manuallyPositioned = false; self.positionVideos(); self.localVideo.view.$container.show(); if (self.localVideo && self.status) { $('#wrapper').append(self.localVideo.view.$container); } self.room.join(); }); self.inConversation = true; self.socket.emit('mapperJoinedCall', { mapid: Metamaps.Active.Map.id, id: Metamaps.Active.Mapper.id }); self.webrtc.startLocalVideo(); Metamaps.GlobalUI.clearNotify(); self.room.chat.mapperJoinedCall(Metamaps.Active.Mapper.id); }, leaveCall: function () { var self = Metamaps.Realtime; self.socket.emit('mapperLeftCall', { mapid: Metamaps.Active.Map.id, id: Metamaps.Active.Mapper.id }); self.room.chat.mapperLeftCall(Metamaps.Active.Mapper.id); self.room.leaveVideoOnly(); self.inConversation = false; self.localVideo.view.$container.hide(); // if there's only two people in the room, and we're leaving // we should shut down the call locally if (self.countOthersInConversation() === 1) { self.callEnded(); } }, turnOff: function (silent) { var self = Metamaps.Realtime; if (self.status) { if (!silent) self.sendRealtimeOff(); //$(".rtMapperSelf").removeClass('littleRtOn').addClass('littleRtOff'); //$('.rtOn').removeClass('active'); //$('.rtOff').addClass('active'); self.status = false; //$(".sidebarCollaborateIcon").removeClass("blue"); $(".collabCompass").hide(); $('#' + self.videoId).remove(); } }, setupSocket: function () { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; var myId = Metamaps.Active.Mapper.id; socket.emit('newMapperNotify', { userid: myId, username: Metamaps.Active.Mapper.get("name"), userimage: Metamaps.Active.Mapper.get("image"), mapid: Metamaps.Active.Map.id }); socket.on(myId + '-' + Metamaps.Active.Map.id + '-invitedToCall', self.invitedToCall); // new call socket.on(myId + '-' + Metamaps.Active.Map.id + '-invitedToJoin', self.invitedToJoin); // call already in progress socket.on(myId + '-' + Metamaps.Active.Map.id + '-callAccepted', self.callAccepted); socket.on(myId + '-' + Metamaps.Active.Map.id + '-callDenied', self.callDenied); socket.on(myId + '-' + Metamaps.Active.Map.id + '-inviteDenied', self.inviteDenied); // receive word that there's a conversation in progress socket.on('maps-' + Metamaps.Active.Map.id + '-callInProgress', self.promptToJoin); socket.on('maps-' + Metamaps.Active.Map.id + '-callStarting', self.conversationHasBegun); socket.on('maps-' + Metamaps.Active.Map.id + '-mapperJoinedCall', self.mapperJoinedCall); socket.on('maps-' + Metamaps.Active.Map.id + '-mapperLeftCall', self.mapperLeftCall); // if you're the 'new guy' update your list with who's already online socket.on(myId + '-' + Metamaps.Active.Map.id + '-UpdateMapperList', self.updateMapperList); // receive word that there's a new mapper on the map socket.on('maps-' + Metamaps.Active.Map.id + '-newmapper', self.newPeerOnMap); // receive word that a mapper left the map socket.on('maps-' + Metamaps.Active.Map.id + '-lostmapper', self.lostPeerOnMap); // receive word that there's a mapper turned on realtime socket.on('maps-' + Metamaps.Active.Map.id + '-newrealtime', self.newCollaborator); // receive word that there's a mapper turned on realtime socket.on('maps-' + Metamaps.Active.Map.id + '-lostrealtime', self.lostCollaborator); // socket.on('maps-' + Metamaps.Active.Map.id + '-topicDrag', self.topicDrag); // socket.on('maps-' + Metamaps.Active.Map.id + '-newTopic', self.newTopic); // socket.on('maps-' + Metamaps.Active.Map.id + '-newMessage', self.newMessage); // socket.on('maps-' + Metamaps.Active.Map.id + '-removeTopic', self.removeTopic); // socket.on('maps-' + Metamaps.Active.Map.id + '-newSynapse', self.newSynapse); // socket.on('maps-' + Metamaps.Active.Map.id + '-removeSynapse', self.removeSynapse); // update mapper compass position socket.on('maps-' + Metamaps.Active.Map.id + '-updatePeerCoords', self.updatePeerCoords); // deletions socket.on('deleteTopicFromServer', self.removeTopic); socket.on('deleteSynapseFromServer', self.removeSynapse); socket.on('topicChangeFromServer', self.topicChange); socket.on('synapseChangeFromServer', self.synapseChange); self.attachMapListener(); // local event listeners that trigger events var sendCoords = function (event) { var pixels = { x: event.pageX, y: event.pageY }; var coords = Metamaps.Util.pixelsToCoords(pixels); self.sendCoords(coords); }; $(document).mousemove(sendCoords); var zoom = function (event, e) { if (e) { var pixels = { x: e.pageX, y: e.pageY }; var coords = Metamaps.Util.pixelsToCoords(pixels); self.sendCoords(coords); } self.positionPeerIcons(); }; $(document).on(Metamaps.JIT.events.zoom, zoom); $(document).on(Metamaps.JIT.events.pan, self.positionPeerIcons); var sendTopicDrag = function (event, positions) { self.sendTopicDrag(positions); }; $(document).on(Metamaps.JIT.events.topicDrag, sendTopicDrag); var sendNewTopic = function (event, data) { self.sendNewTopic(data); }; $(document).on(Metamaps.JIT.events.newTopic, sendNewTopic); var sendDeleteTopic = function (event, data) { self.sendDeleteTopic(data); }; $(document).on(Metamaps.JIT.events.deleteTopic, sendDeleteTopic); var sendRemoveTopic = function (event, data) { self.sendRemoveTopic(data); }; $(document).on(Metamaps.JIT.events.removeTopic, sendRemoveTopic); var sendNewSynapse = function (event, data) { self.sendNewSynapse(data); }; $(document).on(Metamaps.JIT.events.newSynapse, sendNewSynapse); var sendDeleteSynapse = function (event, data) { self.sendDeleteSynapse(data); }; $(document).on(Metamaps.JIT.events.deleteSynapse, sendDeleteSynapse); var sendRemoveSynapse = function (event, data) { self.sendRemoveSynapse(data); }; $(document).on(Metamaps.JIT.events.removeSynapse, sendRemoveSynapse); var sendNewMessage = function (event, data) { self.sendNewMessage(data); }; $(document).on(Metamaps.Views.room.events.newMessage, sendNewMessage); }, attachMapListener: function(){ var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; socket.on('mapChangeFromServer', self.mapChange); }, sendRealtimeOn: function () { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; // send this new mapper back your details, and the awareness that you're online var update = { username: Metamaps.Active.Mapper.get("name"), userid: Metamaps.Active.Mapper.id, mapid: Metamaps.Active.Map.id }; socket.emit('notifyStartRealtime', update); }, sendRealtimeOff: function () { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; // send this new mapper back your details, and the awareness that you're online var update = { username: Metamaps.Active.Mapper.get("name"), userid: Metamaps.Active.Mapper.id, mapid: Metamaps.Active.Map.id }; socket.emit('notifyStopRealtime', update); }, updateMapperList: function (data) { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; // data.userid // data.username // data.userimage // data.userrealtime self.mappersOnMap[data.userid] = { id: data.userid, name: data.username, username: data.username, image: data.userimage, color: Metamaps.Util.getPastelColor(), realtime: data.userrealtime, inConversation: data.userinconversation, coords: { x: 0, y: 0 } }; if (data.userid !== Metamaps.Active.Mapper.id) { self.room.chat.addParticipant(self.mappersOnMap[data.userid]); if (data.userinconversation) self.room.chat.mapperJoinedCall(data.userid); // create a div for the collaborators compass self.createCompass(data.username, data.userid, data.userimage, self.mappersOnMap[data.userid].color, !self.status); } }, newPeerOnMap: function (data) { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; // data.userid // data.username // data.userimage // data.coords var firstOtherPerson = Object.keys(self.mappersOnMap).length === 0; self.mappersOnMap[data.userid] = { id: data.userid, name: data.username, username: data.username, image: data.userimage, color: Metamaps.Util.getPastelColor(), realtime: true, coords: { x: 0, y: 0 }, }; // create an item for them in the realtime box if (data.userid !== Metamaps.Active.Mapper.id && self.status) { self.room.chat.sound.play('joinmap'); self.room.chat.addParticipant(self.mappersOnMap[data.userid]); // create a div for the collaborators compass self.createCompass(data.username, data.userid, data.userimage, self.mappersOnMap[data.userid].color, !self.status); var notifyMessage = data.username + ' just joined the map'; if (firstOtherPerson) { notifyMessage += ' '; } Metamaps.GlobalUI.notifyUser(notifyMessage); // send this new mapper back your details, and the awareness that you've loaded the map var update = { userToNotify: data.userid, username: Metamaps.Active.Mapper.get("name"), userimage: Metamaps.Active.Mapper.get("image"), userid: Metamaps.Active.Mapper.id, userrealtime: self.status, userinconversation: self.inConversation, mapid: Metamaps.Active.Map.id }; socket.emit('updateNewMapperList', update); } }, createCompass: function(name, id, image, color, hide) { var str = '

    '+name+'

    '; str += '
    '; $('#compass' + id).remove(); $('
    ', { id: 'compass' + id, class: 'collabCompass' }).html(str).appendTo('#wrapper'); if (hide) { $('#compass' + id).hide(); } $('#compass' + id + ' img').css({ 'border': '2px solid ' + color }); $('#compass' + id + ' p').css({ 'background-color': color }); }, lostPeerOnMap: function (data) { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; // data.userid // data.username delete self.mappersOnMap[data.userid]; self.room.chat.sound.play('leavemap'); //$('#mapper' + data.userid).remove(); $('#compass' + data.userid).remove(); self.room.chat.removeParticipant(data.username); Metamaps.GlobalUI.notifyUser(data.username + ' just left the map'); if ((self.inConversation && self.countOthersInConversation() === 0) || (!self.inConversation && self.countOthersInConversation() === 1)) { self.callEnded(); } }, newCollaborator: function (data) { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; // data.userid // data.username self.mappersOnMap[data.userid].realtime = true; //$('#mapper' + data.userid).removeClass('littleRtOff').addClass('littleRtOn'); $('#compass' + data.userid).show(); Metamaps.GlobalUI.notifyUser(data.username + ' just turned on realtime'); }, lostCollaborator: function (data) { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; // data.userid // data.username self.mappersOnMap[data.userid].realtime = false; //$('#mapper' + data.userid).removeClass('littleRtOn').addClass('littleRtOff'); $('#compass' + data.userid).hide(); Metamaps.GlobalUI.notifyUser(data.username + ' just turned off realtime'); }, updatePeerCoords: function (data) { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; self.mappersOnMap[data.userid].coords={x: data.usercoords.x,y:data.usercoords.y}; self.positionPeerIcon(data.userid); }, positionPeerIcons: function () { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; if (self.status) { // if i have realtime turned on for (var key in self.mappersOnMap) { var mapper = self.mappersOnMap[key]; if (mapper.realtime) { self.positionPeerIcon(key); } } } }, positionPeerIcon: function (id) { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; var boundary = self.chatOpen ? '#wrapper' : document; var mapper = self.mappersOnMap[id]; var xMax=$(boundary).width(); var yMax=$(boundary).height(); var compassDiameter=56; var compassArrowSize=24; var origPixels = Metamaps.Util.coordsToPixels(mapper.coords); var pixels = self.limitPixelsToScreen(origPixels); $('#compass' + id).css({ left: pixels.x + 'px', top: pixels.y + 'px' }); /* showing the arrow if the collaborator is off of the viewport screen */ if (origPixels.x !== pixels.x || origPixels.y !== pixels.y) { var dy = origPixels.y - pixels.y; //opposite var dx = origPixels.x - pixels.x; // adjacent var ratio = dy / dx; var angle = Math.atan2(dy, dx); $('#compassArrow' + id).show().css({ transform: 'rotate(' + angle + 'rad)', "-webkit-transform": 'rotate(' + angle + 'rad)', }); if (dx > 0) { $('#compass' + id).addClass('labelLeft'); } } else { $('#compassArrow' + id).hide(); $('#compass' + id).removeClass('labelLeft'); } }, limitPixelsToScreen: function (pixels) { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; var boundary = self.chatOpen ? '#wrapper' : document; var xLimit, yLimit; var xMax=$(boundary).width(); var yMax=$(boundary).height(); var compassDiameter=56; var compassArrowSize=24; xLimit = Math.max(0 + compassArrowSize, pixels.x); xLimit = Math.min(xLimit, xMax - compassDiameter); yLimit = Math.max(0 + compassArrowSize, pixels.y); yLimit = Math.min(yLimit, yMax - compassDiameter); return {x:xLimit,y:yLimit}; }, sendCoords: function (coords) { var self = Metamaps.Realtime; var socket = Metamaps.Realtime.socket; var map = Metamaps.Active.Map; var mapper = Metamaps.Active.Mapper; if (self.status && map.authorizeToEdit(mapper) && socket) { var update = { usercoords: coords, userid: Metamaps.Active.Mapper.id, mapid: Metamaps.Active.Map.id }; socket.emit('updateMapperCoords', update); } }, sendTopicDrag: function (positions) { var self = Metamaps.Realtime; var socket = self.socket; if (Metamaps.Active.Map && self.status) { positions.mapid = Metamaps.Active.Map.id; socket.emit('topicDrag', positions); } }, topicDrag: function (positions) { var self = Metamaps.Realtime; var socket = self.socket; var topic; var node; if (Metamaps.Active.Map && self.status) { for (var key in positions) { topic = Metamaps.Topics.get(key); if (topic) node = topic.get('node'); if (node) node.pos.setc(positions[key].x, positions[key].y); } //for Metamaps.Visualize.mGraph.plot(); } }, sendTopicChange: function (topic) { var self = Metamaps.Realtime; var socket = self.socket; var data = { topicId: topic.id } socket.emit('topicChangeFromClient', data); }, topicChange: function (data) { var topic = Metamaps.Topics.get(data.topicId); if (topic) { var node = topic.get('node'); topic.fetch({ success: function (model) { model.set({ node: node }); model.trigger('changeByOther'); } }); } }, sendSynapseChange: function (synapse) { var self = Metamaps.Realtime; var socket = self.socket; var data = { synapseId: synapse.id } socket.emit('synapseChangeFromClient', data); }, synapseChange: function (data) { var synapse = Metamaps.Synapses.get(data.synapseId); if (synapse) { // edge reset necessary because fetch causes model reset var edge = synapse.get('edge'); synapse.fetch({ success: function (model) { model.set({ edge: edge }); model.trigger('changeByOther'); } }); } }, sendMapChange: function (map) { var self = Metamaps.Realtime; var socket = self.socket; var data = { mapId: map.id } socket.emit('mapChangeFromClient', data); }, mapChange: function (data) { var map = Metamaps.Active.Map; var isActiveMap = map && data.mapId === map.id; if (isActiveMap) { var permBefore = map.get('permission'); var idBefore = map.id; map.fetch({ success: function (model, response) { var idNow = model.id; var permNow = model.get('permission'); if (idNow !== idBefore) { Metamaps.Map.leavePrivateMap(); // this means the map has been changed to private } else if (permNow === 'public' && permBefore === 'commons') { Metamaps.Map.commonsToPublic(); } else if (permNow === 'commons' && permBefore === 'public') { Metamaps.Map.publicToCommons(); } else { model.fetchContained(); model.trigger('changeByOther'); } } }); } }, // newMessage sendNewMessage: function (data) { var self = Metamaps.Realtime; var socket = self.socket; var message = data.attributes; message.mapid = Metamaps.Active.Map.id; socket.emit('newMessage', message); }, newMessage: function (data) { var self = Metamaps.Realtime; var socket = self.socket; self.room.addMessages(new Metamaps.Backbone.MessageCollection(data)); }, // newTopic sendNewTopic: function (data) { var self = Metamaps.Realtime; var socket = self.socket; if (Metamaps.Active.Map && self.status) { data.mapperid = Metamaps.Active.Mapper.id; data.mapid = Metamaps.Active.Map.id; socket.emit('newTopic', data); } }, newTopic: function (data) { var topic, mapping, mapper, mapperCallback, cancel; var self = Metamaps.Realtime; var socket = self.socket; if (!self.status) return; function waitThenRenderTopic() { if (topic && mapping && mapper) { Metamaps.Topic.renderTopic(mapping, topic, false, false); } else if (!cancel) { setTimeout(waitThenRenderTopic, 10); } } mapper = Metamaps.Mappers.get(data.mapperid); if (mapper === undefined) { mapperCallback = function (m) { Metamaps.Mappers.add(m); mapper = m; }; Metamaps.Mapper.get(data.mapperid, mapperCallback); } $.ajax({ url: "/topics/" + data.mappableid + ".json", success: function (response) { Metamaps.Topics.add(response); topic = Metamaps.Topics.get(response.id); }, error: function () { cancel = true; } }); $.ajax({ url: "/mappings/" + data.mappingid + ".json", success: function (response) { Metamaps.Mappings.add(response); mapping = Metamaps.Mappings.get(response.id); }, error: function () { cancel = true; } }); waitThenRenderTopic(); }, // removeTopic sendDeleteTopic: function (data) { var self = Metamaps.Realtime; var socket = self.socket; if (Metamaps.Active.Map) { socket.emit('deleteTopicFromClient', data); } }, // removeTopic sendRemoveTopic: function (data) { var self = Metamaps.Realtime; var socket = self.socket; if (Metamaps.Active.Map) { data.mapid = Metamaps.Active.Map.id; socket.emit('removeTopic', data); } }, removeTopic: function (data) { var self = Metamaps.Realtime; var socket = self.socket; if (!self.status) return; var topic = Metamaps.Topics.get(data.mappableid); if (topic) { var node = topic.get('node'); var mapping = topic.getMapping(); Metamaps.Control.hideNode(node.id); Metamaps.Topics.remove(topic); Metamaps.Mappings.remove(mapping); } }, // newSynapse sendNewSynapse: function (data) { var self = Metamaps.Realtime; var socket = self.socket; if (Metamaps.Active.Map) { data.mapperid = Metamaps.Active.Mapper.id; data.mapid = Metamaps.Active.Map.id; socket.emit('newSynapse', data); } }, newSynapse: function (data) { var topic1, topic2, node1, node2, synapse, mapping, cancel; var self = Metamaps.Realtime; var socket = self.socket; if (!self.status) return; function waitThenRenderSynapse() { if (synapse && mapping && mapper) { topic1 = synapse.getTopic1(); node1 = topic1.get('node'); topic2 = synapse.getTopic2(); node2 = topic2.get('node'); Metamaps.Synapse.renderSynapse(mapping, synapse, node1, node2, false); } else if (!cancel) { setTimeout(waitThenRenderSynapse, 10); } } mapper = Metamaps.Mappers.get(data.mapperid); if (mapper === undefined) { mapperCallback = function (m) { Metamaps.Mappers.add(m); mapper = m; }; Metamaps.Mapper.get(data.mapperid, mapperCallback); } $.ajax({ url: "/synapses/" + data.mappableid + ".json", success: function (response) { Metamaps.Synapses.add(response); synapse = Metamaps.Synapses.get(response.id); }, error: function () { cancel = true; } }); $.ajax({ url: "/mappings/" + data.mappingid + ".json", success: function (response) { Metamaps.Mappings.add(response); mapping = Metamaps.Mappings.get(response.id); }, error: function () { cancel = true; } }); waitThenRenderSynapse(); }, // deleteSynapse sendDeleteSynapse: function (data) { var self = Metamaps.Realtime; var socket = self.socket; if (Metamaps.Active.Map) { data.mapid = Metamaps.Active.Map.id; socket.emit('deleteSynapseFromClient', data); } }, // removeSynapse sendRemoveSynapse: function (data) { var self = Metamaps.Realtime; var socket = self.socket; if (Metamaps.Active.Map) { data.mapid = Metamaps.Active.Map.id; socket.emit('removeSynapse', data); } }, removeSynapse: function (data) { var self = Metamaps.Realtime; var socket = self.socket; if (!self.status) return; var synapse = Metamaps.Synapses.get(data.mappableid); if (synapse) { var edge = synapse.get('edge'); var mapping = synapse.getMapping(); if (edge.getData("mappings").length - 1 === 0) { Metamaps.Control.hideEdge(edge); } var index = _.indexOf(edge.getData("synapses"), synapse); edge.getData("mappings").splice(index, 1); edge.getData("synapses").splice(index, 1); if (edge.getData("displayIndex")) { delete edge.data.$displayIndex; } Metamaps.Synapses.remove(synapse); Metamaps.Mappings.remove(mapping); } }, }; // end Metamaps.Realtime /* * * CONTROL * */ Metamaps.Control = { init: function () { }, selectNode: function (node,e) { var filtered = node.getData('alpha') === 0; if (filtered || Metamaps.Selected.Nodes.indexOf(node) != -1) return; node.selected = true; node.setData('dim', 30, 'current'); Metamaps.Selected.Nodes.push(node); }, deselectAllNodes: function () { var l = Metamaps.Selected.Nodes.length; for (var i = l - 1; i >= 0; i -= 1) { var node = Metamaps.Selected.Nodes[i]; Metamaps.Control.deselectNode(node); } Metamaps.Visualize.mGraph.plot(); }, deselectNode: function (node) { delete node.selected; node.setData('dim', 25, 'current'); //remove the node Metamaps.Selected.Nodes.splice( Metamaps.Selected.Nodes.indexOf(node), 1); }, deleteSelected: function () { if (!Metamaps.Active.Map) return; var n = Metamaps.Selected.Nodes.length; var e = Metamaps.Selected.Edges.length; var ntext = n == 1 ? "1 topic" : n + " topics"; var etext = e == 1 ? "1 synapse" : e + " synapses"; var text = "You have " + ntext + " and " + etext + " selected. "; var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper); if (!authorized) { Metamaps.GlobalUI.notifyUser("Cannot edit Public map."); return; } var r = confirm(text + "Are you sure you want to permanently delete them all? This will remove them from all maps they appear on."); if (r == true) { Metamaps.Control.deleteSelectedEdges(); Metamaps.Control.deleteSelectedNodes(); } }, deleteSelectedNodes: function () { // refers to deleting topics permanently if (!Metamaps.Active.Map) return; var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper); if (!authorized) { Metamaps.GlobalUI.notifyUser("Cannot edit Public map."); return; } var l = Metamaps.Selected.Nodes.length; for (var i = l - 1; i >= 0; i -= 1) { var node = Metamaps.Selected.Nodes[i]; Metamaps.Control.deleteNode(node.id); } }, deleteNode: function (nodeid) { // refers to deleting topics permanently if (!Metamaps.Active.Map) return; var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper); if (!authorized) { Metamaps.GlobalUI.notifyUser("Cannot edit Public map."); return; } var node = Metamaps.Visualize.mGraph.graph.getNode(nodeid); var topic = node.getData('topic'); var permToDelete = Metamaps.Active.Mapper.id === topic.get('user_id') || Metamaps.Active.Mapper.get('admin'); if (permToDelete) { var mappableid = topic.id; var mapping = node.getData('mapping'); topic.destroy(); Metamaps.Mappings.remove(mapping); $(document).trigger(Metamaps.JIT.events.deleteTopic, [{ mappableid: mappableid }]); Metamaps.Control.hideNode(nodeid); } else { Metamaps.GlobalUI.notifyUser('Only topics you created can be deleted'); } }, removeSelectedNodes: function () { // refers to removing topics permanently from a map if (!Metamaps.Active.Map) return; var l = Metamaps.Selected.Nodes.length, i, node, authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper); if (!authorized) { Metamaps.GlobalUI.notifyUser("Cannot edit Public map."); return; } for (i = l - 1; i >= 0; i -= 1) { node = Metamaps.Selected.Nodes[i]; Metamaps.Control.removeNode(node.id); } }, removeNode: function (nodeid) { // refers to removing topics permanently from a map if (!Metamaps.Active.Map) return; var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper); var node = Metamaps.Visualize.mGraph.graph.getNode(nodeid); if (!authorized) { Metamaps.GlobalUI.notifyUser("Cannot edit Public map."); return; } var topic = node.getData('topic'); var mappableid = topic.id; var mapping = node.getData('mapping'); mapping.destroy(); Metamaps.Topics.remove(topic); $(document).trigger(Metamaps.JIT.events.removeTopic, [{ mappableid: mappableid }]); Metamaps.Control.hideNode(nodeid); }, hideSelectedNodes: function () { var l = Metamaps.Selected.Nodes.length, i, node; for (i = l - 1; i >= 0; i -= 1) { node = Metamaps.Selected.Nodes[i]; Metamaps.Control.hideNode(node.id); } }, hideNode: function (nodeid) { var node = Metamaps.Visualize.mGraph.graph.getNode(nodeid); var graph = Metamaps.Visualize.mGraph; Metamaps.Control.deselectNode(node); node.setData('alpha', 0, 'end'); node.eachAdjacency(function (adj) { adj.setData('alpha', 0, 'end'); }); Metamaps.Visualize.mGraph.fx.animate({ modes: ['node-property:alpha', 'edge-property:alpha' ], duration: 500 }); setTimeout(function () { if (nodeid == Metamaps.Visualize.mGraph.root) { // && Metamaps.Visualize.type === "RGraph" var newroot = _.find(graph.graph.nodes, function(n){ return n.id !== nodeid; }); graph.root = newroot ? newroot.id : null; } Metamaps.Visualize.mGraph.graph.removeNode(nodeid); }, 500); Metamaps.Filter.checkMetacodes(); Metamaps.Filter.checkMappers(); }, selectEdge: function (edge) { var filtered = edge.getData('alpha') === 0; // don't select if the edge is filtered if (filtered || Metamaps.Selected.Edges.indexOf(edge) != -1) return; var width = Metamaps.Mouse.edgeHoveringOver === edge ? 4 : 2; edge.setDataset('current', { showDesc: true, lineWidth: width, color: Metamaps.Settings.colors.synapses.selected }); Metamaps.Visualize.mGraph.plot(); Metamaps.Selected.Edges.push(edge); }, deselectAllEdges: function () { var l = Metamaps.Selected.Edges.length; for (var i = l - 1; i >= 0; i -= 1) { var edge = Metamaps.Selected.Edges[i]; Metamaps.Control.deselectEdge(edge); } Metamaps.Visualize.mGraph.plot(); }, deselectEdge: function (edge) { edge.setData('showDesc', false, 'current'); edge.setDataset('current', { lineWidth: 2, color: Metamaps.Settings.colors.synapses.normal }); if (Metamaps.Mouse.edgeHoveringOver == edge) { edge.setDataset('current', { showDesc: true, lineWidth: 4 }); } Metamaps.Visualize.mGraph.plot(); //remove the edge Metamaps.Selected.Edges.splice( Metamaps.Selected.Edges.indexOf(edge), 1); }, deleteSelectedEdges: function () { // refers to deleting topics permanently var edge, l = Metamaps.Selected.Edges.length; if (!Metamaps.Active.Map) return; var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper); if (!authorized) { Metamaps.GlobalUI.notifyUser("Cannot edit Public map."); return; } for (var i = l - 1; i >= 0; i -= 1) { edge = Metamaps.Selected.Edges[i]; Metamaps.Control.deleteEdge(edge); } }, deleteEdge: function (edge) { if (!Metamaps.Active.Map) return; var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper); if (!authorized) { Metamaps.GlobalUI.notifyUser("Cannot edit Public map."); return; } var index = edge.getData("displayIndex") ? edge.getData("displayIndex") : 0; var synapse = edge.getData("synapses")[index]; var mapping = edge.getData("mappings")[index]; var permToDelete = Metamaps.Active.Mapper.id === synapse.get('user_id') || Metamaps.Active.Mapper.get('admin'); if (permToDelete) { if (edge.getData("synapses").length - 1 === 0) { Metamaps.Control.hideEdge(edge); } var mappableid = synapse.id; synapse.destroy(); // the server will destroy the mapping, we just need to remove it here Metamaps.Mappings.remove(mapping); edge.getData("mappings").splice(index, 1); edge.getData("synapses").splice(index, 1); if (edge.getData("displayIndex")) { delete edge.data.$displayIndex; } $(document).trigger(Metamaps.JIT.events.deleteSynapse, [{ mappableid: mappableid }]); } else { Metamaps.GlobalUI.notifyUser('Only synapses you created can be deleted'); } }, removeSelectedEdges: function () { var l = Metamaps.Selected.Edges.length, i, edge; if (!Metamaps.Active.Map) return; var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper); if (!authorized) { Metamaps.GlobalUI.notifyUser("Cannot edit Public map."); return; } for (i = l - 1; i >= 0; i -= 1) { edge = Metamaps.Selected.Edges[i]; Metamaps.Control.removeEdge(edge); } Metamaps.Selected.Edges = new Array(); }, removeEdge: function (edge) { if (!Metamaps.Active.Map) return; var authorized = Metamaps.Active.Map.authorizeToEdit(Metamaps.Active.Mapper); if (!authorized) { Metamaps.GlobalUI.notifyUser("Cannot edit Public map."); return; } if (edge.getData("mappings").length - 1 === 0) { Metamaps.Control.hideEdge(edge); } var index = edge.getData("displayIndex") ? edge.getData("displayIndex") : 0; var synapse = edge.getData("synapses")[index]; var mapping = edge.getData("mappings")[index]; var mappableid = synapse.id; mapping.destroy(); Metamaps.Synapses.remove(synapse); edge.getData("mappings").splice(index, 1); edge.getData("synapses").splice(index, 1); if (edge.getData("displayIndex")) { delete edge.data.$displayIndex; } $(document).trigger(Metamaps.JIT.events.removeSynapse, [{ mappableid: mappableid }]); }, hideSelectedEdges: function () { var edge, l = Metamaps.Selected.Edges.length, i; for (i = l - 1; i >= 0; i -= 1) { edge = Metamaps.Selected.Edges[i]; Metamaps.Control.hideEdge(edge); } Metamaps.Selected.Edges = new Array(); }, hideEdge: function (edge) { var from = edge.nodeFrom.id; var to = edge.nodeTo.id; edge.setData('alpha', 0, 'end'); Metamaps.Control.deselectEdge(edge); Metamaps.Visualize.mGraph.fx.animate({ modes: ['edge-property:alpha'], duration: 500 }); setTimeout(function () { Metamaps.Visualize.mGraph.graph.removeAdjacence(from, to); }, 500); Metamaps.Filter.checkSynapses(); Metamaps.Filter.checkMappers(); }, updateSelectedPermissions: function (permission) { var edge, synapse, node, topic; Metamaps.GlobalUI.notifyUser('Working...'); // variables to keep track of how many nodes and synapses you had the ability to change the permission of var nCount = 0, sCount = 0; // change the permission of the selected synapses, if logged in user is the original creator var l = Metamaps.Selected.Edges.length; for (var i = l - 1; i >= 0; i -= 1) { edge = Metamaps.Selected.Edges[i]; synapse = edge.getData('synapses')[0]; if (synapse.authorizePermissionChange(Metamaps.Active.Mapper)) { synapse.save({ permission: permission }); sCount++; } } // change the permission of the selected topics, if logged in user is the original creator var l = Metamaps.Selected.Nodes.length; for (var i = l - 1; i >= 0; i -= 1) { node = Metamaps.Selected.Nodes[i]; topic = node.getData('topic'); if (topic.authorizePermissionChange(Metamaps.Active.Mapper)) { topic.save({ permission: permission }); nCount++; } } var nString = nCount == 1 ? (nCount.toString() + ' topic and ') : (nCount.toString() + ' topics and '); var sString = sCount == 1 ? (sCount.toString() + ' synapse') : (sCount.toString() + ' synapses'); var message = nString + sString + ' you created updated to ' + permission; Metamaps.GlobalUI.notifyUser(message); }, updateSelectedMetacodes: function (metacode_id) { var node, topic; Metamaps.GlobalUI.notifyUser('Working...'); var metacode = Metamaps.Metacodes.get(metacode_id); // variables to keep track of how many nodes and synapses you had the ability to change the permission of var nCount = 0; // change the permission of the selected topics, if logged in user is the original creator var l = Metamaps.Selected.Nodes.length; for (var i = l - 1; i >= 0; i -= 1) { node = Metamaps.Selected.Nodes[i]; topic = node.getData('topic'); if (topic.authorizeToEdit(Metamaps.Active.Mapper)) { topic.save({ 'metacode_id': metacode_id }); nCount++; } } var nString = nCount == 1 ? (nCount.toString() + ' topic') : (nCount.toString() + ' topics'); var message = nString + ' you can edit updated to ' + metacode.get('name'); Metamaps.GlobalUI.notifyUser(message); Metamaps.Visualize.mGraph.plot(); }, }; // end Metamaps.Control