// 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.Erb = {} Metamaps.Erb['REALTIME_SERVER'] = '<%= ENV['REALTIME_SERVER'] %>' Metamaps.Erb['junto_spinner_darkgrey.gif'] = '<%= asset_path('junto_spinner_darkgrey.gif') %>' 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('calculated_permission') === "commons" || this.get('collaborator_ids').includes(mapper.get('id')) || 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('calculated_permission') === "commons" || this.get('collaborator_ids').includes(mapper.get('id')) || 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(); Metamaps.Collaborators = Metamaps.Collaborators ? new self.MapperCollection(Metamaps.Collaborators) : 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