Created
September 19, 2011 12:46
-
-
Save jamiely/1226428 to your computer and use it in GitHub Desktop.
Diff of final changes for intermap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- a/css/global.css | |
| +++ b/css/global.css | |
| @@ -300,6 +300,10 @@ ul#menu-main-menu li a { | |
| #matrix #interactionMatrixParent { | |
| padding-top: 10px; | |
| } | |
| + #matrix #interactionMatrixParent h2 .button { | |
| + font-size: x-small; | |
| + float: right; | |
| + } | |
| #matrixContainer { | |
| padding-top: 200px; /* this needs to match {.terms .term} width so that the top terms have enough room */ | |
| @@ -682,7 +686,7 @@ is pending or invalid, etc. | |
| } | |
| #reorder div.projectsList ul { | |
| margin: 20px; | |
| - max-width: 400px; | |
| + max-width: 600px; | |
| } | |
| #reorder li.term { | |
| font-size: x-large; | |
| @@ -737,20 +741,23 @@ is pending or invalid, etc. | |
| padding: 3px; | |
| border: 1px #CCC solid; | |
| } | |
| -#instructions ul { | |
| - font-size: small; | |
| - list-style: disc; | |
| - margin-left: 10px; | |
| +#instructions { | |
| + display: none; | |
| } | |
| - #instructions > ul > ul { | |
| - margin-top: 10px; | |
| - font-weight: bold; | |
| + #instructions ul { | |
| + font-size: small; | |
| + list-style: disc; | |
| + margin-left: 10px; | |
| } | |
| - #instructions > ul > ul > li { | |
| - font-weight: normal; | |
| - margin-left: 20px; | |
| - padding: 3px; | |
| + #instructions > ul > ul { | |
| + margin-top: 10px; | |
| + font-weight: bold; | |
| } | |
| + #instructions > ul > ul > li { | |
| + font-weight: normal; | |
| + margin-left: 20px; | |
| + padding: 3px; | |
| + } | |
| #bottom > div:nth-child(2) { | |
| text-align: right; | |
| @@ -774,4 +781,8 @@ is pending or invalid, etc. | |
| } | |
| #register fieldset a { | |
| font-size: medium; | |
| - } | |
| \ No newline at end of file | |
| + } | |
| + | |
| +#linkDeleteActiveTerm { | |
| + display: none; | |
| +} | |
| \ No newline at end of file | |
| diff --git a/index.dev.html b/index.dev.html | |
| index 417d961..00e21c9 100644 | |
| --- a/index.dev.html | |
| +++ b/index.dev.html | |
| @@ -290,7 +290,12 @@ | |
| </script> | |
| <div id="interactionMatrixParent"> | |
| - <h2>Interaction Matrix</h2> | |
| + <!-- Add new term handled by IntermapApplicationView --> | |
| + <h2> | |
| + Interaction Matrix | |
| + <a href="javascript:" id="linkDeleteActiveTerm" class="deleteTerm button" title="Either click this button to delete the active term or press the DELETE key.">Delete Term</a> | |
| + <a href="javascript:" class="addTerm button" title="Either click this button to add a new term or press ENTER in the last term input box.">Add New Term</a> | |
| + </h2> | |
| <div id="interactionMatrix"> | |
| <div class="preview"> | |
| @@ -301,7 +306,7 @@ | |
| <div id="topTermsContainer"> | |
| <div class="terms" id="topTerms"> | |
| <script class="template" id="tmplTopTerm" type="text/x-jquery-tmpl"> | |
| - <span class="text">${text}</span> | |
| + <span class="text">${$item.getTerm()}</span> | |
| <span class="tagText" style="color: ${$item.getTagForegroundColor()}; background-color: ${$item.getTagBackgroundColor()}">${$item.getTag()}</span> | |
| </script> | |
| </div> | |
| @@ -405,6 +410,7 @@ | |
| <li>Term centrality may be toggled by clicking the <img src="img/star_whartonblue_16x16.png" alt="star" title="Denotes term centrality"> icon.</li> | |
| <li>Terms may be tagged by clicking on the box labeled <em>click to tag</em> and entering a tag.</li> | |
| <li>Please avoid creating duplicate terms.</li> | |
| + <li>Terms may be deleted by pressing the DELETE key inside a term input box.</li> | |
| </ul> | |
| <ul> | |
| Interactions | |
| diff --git a/index.prod.html b/index.prod.html | |
| index a38be14..205a672 100644 | |
| --- a/index.prod.html | |
| +++ b/index.prod.html | |
| @@ -288,7 +288,12 @@ | |
| </script> | |
| <div id="interactionMatrixParent"> | |
| - <h2>Interaction Matrix</h2> | |
| + <!-- Add new term handled by IntermapApplicationView --> | |
| + <h2> | |
| + Interaction Matrix | |
| + <a href="javascript:" id="linkDeleteActiveTerm" class="deleteTerm button" title="Either click this button to delete the active term or press the DELETE key.">Delete Term</a> | |
| + <a href="javascript:" class="addTerm button" title="Either click this button to add a new term or press ENTER in the last term input box.">Add New Term</a> | |
| + </h2> | |
| <div id="interactionMatrix"> | |
| <div class="preview"> | |
| @@ -299,7 +304,7 @@ | |
| <div id="topTermsContainer"> | |
| <div class="terms" id="topTerms"> | |
| <script class="template" id="tmplTopTerm" type="text/x-jquery-tmpl"> | |
| - <span class="text">${text}</span> | |
| + <span class="text">${$item.getTerm()}</span> | |
| <span class="tagText" style="color: ${$item.getTagForegroundColor()}; background-color: ${$item.getTagBackgroundColor()}">${$item.getTag()}</span> | |
| </script> | |
| </div> | |
| @@ -403,6 +408,7 @@ | |
| <li>Term centrality may be toggled by clicking the <img src="img/star_whartonblue_16x16.png" alt="star" title="Denotes term centrality"> icon.</li> | |
| <li>Terms may be tagged by clicking on the box labeled <em>click to tag</em> and entering a tag.</li> | |
| <li>Please avoid creating duplicate terms.</li> | |
| + <li>Terms may be deleted by pressing the DELETE key inside a term input box.</li> | |
| </ul> | |
| <ul> | |
| Interactions | |
| diff --git a/index.template.html b/index.template.html | |
| index b1fb391..86b9f96 100755 | |
| --- a/index.template.html | |
| +++ b/index.template.html | |
| @@ -287,7 +287,12 @@ | |
| </script> | |
| <div id="interactionMatrixParent"> | |
| - <h2>Interaction Matrix</h2> | |
| + <!-- Add new term handled by IntermapApplicationView --> | |
| + <h2> | |
| + Interaction Matrix | |
| + <a href="javascript:" id="linkDeleteActiveTerm" class="deleteTerm button" title="Either click this button to delete the active term or press the DELETE key.">Delete Term</a> | |
| + <a href="javascript:" class="addTerm button" title="Either click this button to add a new term or press ENTER in the last term input box.">Add New Term</a> | |
| + </h2> | |
| <div id="interactionMatrix"> | |
| <div class="preview"> | |
| @@ -298,7 +303,7 @@ | |
| <div id="topTermsContainer"> | |
| <div class="terms" id="topTerms"> | |
| <script class="template" id="tmplTopTerm" type="text/x-jquery-tmpl"> | |
| - <span class="text">${text}</span> | |
| + <span class="text">${$item.getTerm()}</span> | |
| <span class="tagText" style="color: ${$item.getTagForegroundColor()}; background-color: ${$item.getTagBackgroundColor()}">${$item.getTag()}</span> | |
| </script> | |
| </div> | |
| @@ -402,6 +407,7 @@ | |
| <li>Term centrality may be toggled by clicking the <img src="img/star_whartonblue_16x16.png" alt="star" title="Denotes term centrality"> icon.</li> | |
| <li>Terms may be tagged by clicking on the box labeled <em>click to tag</em> and entering a tag.</li> | |
| <li>Please avoid creating duplicate terms.</li> | |
| + <li>Terms may be deleted by pressing the DELETE key inside a term input box.</li> | |
| </ul> | |
| <ul> | |
| Interactions | |
| diff --git a/js/intermap/IntermapRouter.js b/js/intermap/IntermapRouter.js | |
| index 78948d3..0ed5a3d 100644 | |
| --- a/js/intermap/IntermapRouter.js | |
| +++ b/js/intermap/IntermapRouter.js | |
| @@ -14,6 +14,8 @@ Intermap.Router = Backbone.Router.extend({ | |
| 'register': 'register', | |
| 'help': 'help' // #help | |
| }, | |
| + // Tracks whether the entire list of projects has ever been loaded | |
| + _projectsHaveBeenLoaded: false, | |
| _currentProject: 0, | |
| _currentScreen: null, | |
| _newProjectIndex: 1, | |
| @@ -94,13 +96,22 @@ Intermap.Router = Backbone.Router.extend({ | |
| * Loads the project with the passed id. Both cid and regular id may be used. | |
| */ | |
| _loadProject: function(id){ | |
| - var self = this; | |
| + var self = this, | |
| + projects = Intermap.App.get('projects'); | |
| try { | |
| - if(this._currentProject != id) { | |
| + var exists = projects.get(id); | |
| + | |
| + if(this._currentProject != id && exists) { | |
| Intermap.App.changeProject(id); | |
| this._currentProject = id; | |
| } | |
| + else if (! exists) { | |
| + this._loadProjects(function(collection, response) { | |
| + Intermap.View.getProjectListView().renderProjectsSubmenu(); | |
| + self._loadProject(id); | |
| + }, {preventScreen: true}); | |
| + } | |
| else { | |
| this._showScreen('#matrix'); | |
| } | |
| @@ -113,14 +124,7 @@ Intermap.Router = Backbone.Router.extend({ | |
| } | |
| catch(e) { // project doesn't exist most likely. forward to list of projects | |
| - if(Intermap.App.get('projects').length == 0) { | |
| - this._loadProjects(function(collection, response) { | |
| - self._loadProject(id); | |
| - }); | |
| - } | |
| - else { | |
| - this.navigate('projects', true); | |
| - } | |
| + this.navigate('projects', true); | |
| } | |
| }, | |
| projects: function(id) { | |
| @@ -154,7 +158,8 @@ Intermap.Router = Backbone.Router.extend({ | |
| jQuery('.matrixLink').attr('href', '#projects/' + projectId); | |
| }, | |
| - _loadProjects: function(callback) { | |
| + _loadProjects: function(callback, options) { | |
| + options = options || {}; | |
| this._currentProject = null; | |
| callback = callback || function(){}; | |
| @@ -162,7 +167,8 @@ Intermap.Router = Backbone.Router.extend({ | |
| var self = this, | |
| result = Intermap.App.get('projects').fetch({ | |
| success: function(collection, response) { | |
| - self._showScreen('#projects'); | |
| + if(! options.preventScreen) | |
| + self._showScreen('#projects'); | |
| callback(collection, response); | |
| }, | |
| error: function(collection, response) { | |
| diff --git a/js/intermap/models/IntermapApplication.js b/js/intermap/models/IntermapApplication.js | |
| index aa77b00..42a7deb 100755 | |
| --- a/js/intermap/models/IntermapApplication.js | |
| +++ b/js/intermap/models/IntermapApplication.js | |
| @@ -70,6 +70,26 @@ Intermap.Application = Backbone.Model.extend({ | |
| self.trigger('error', newTerm, 'Term is blank or already exists.'); | |
| return null; | |
| }, | |
| + | |
| + /** | |
| + * Retrieves a term of the active project matching the passed text. | |
| + */ | |
| + getTermByText: function(text) { | |
| + var terms = this.get('project').get('terms').getByText(text); | |
| + return _(terms).first(); | |
| + }, | |
| + | |
| + /** | |
| + * Deletes the term of the active project corresponding to the passed text | |
| + */ | |
| + deleteTermFromText: function(text, options) { | |
| + if(! text) return; | |
| + | |
| + var term = this.getTermByText(text); | |
| + | |
| + if(term) term.destroy(options); | |
| + }, | |
| + | |
| /** | |
| * This will create a new interaction, and call the passed callback with the | |
| * newly created interaction model. | |
| diff --git a/js/intermap/views/InteractionMatrixView.js b/js/intermap/views/InteractionMatrixView.js | |
| index 4426537..aeb4bd8 100644 | |
| --- a/js/intermap/views/InteractionMatrixView.js | |
| +++ b/js/intermap/views/InteractionMatrixView.js | |
| @@ -146,14 +146,15 @@ Intermap.InteractionMatrixView = Backbone.View.extend({ | |
| }, | |
| _removeThenDecrementFollowing: function(views, viewToRemove) { | |
| + // first, remove the view | |
| + var i = _.indexOf(views, viewToRemove); | |
| + views.splice(i,1); | |
| + | |
| // wrap in underscore | |
| views = _(views); | |
| - var i = views.indexOf(viewToRemove); | |
| - | |
| // remove 1 item at index i, then decrement row index of the items after | |
| - return views.chain().splice(i, 1). | |
| - slice(i).invoke('decrementRowIndex'); | |
| + return views.chain().slice(i).invoke('decrementRowIndex'); | |
| }, | |
| onTermCollectionAdd: function(addedTerm) { | |
| diff --git a/js/intermap/views/InteractionView.js b/js/intermap/views/InteractionView.js | |
| index aea9d04..7fff9a2 100755 | |
| --- a/js/intermap/views/InteractionView.js | |
| +++ b/js/intermap/views/InteractionView.js | |
| @@ -79,6 +79,8 @@ Intermap.InteractionView = Backbone.View.extend({ | |
| else { | |
| el.removeClass('null').removeAttr('title'); | |
| } | |
| + | |
| + this._updateCommentStyle(); | |
| }, | |
| cycleStatus: function(){ | |
| var self = this, | |
| @@ -209,6 +211,15 @@ Intermap.InteractionView = Backbone.View.extend({ | |
| this.commentingFlag = false; | |
| this.delayedHover = setTimeout(this.showCommentBox, 1000); | |
| }, | |
| + // | |
| + // Updates the class name of the element depending on whether the associated | |
| + // model has a comment or not. | |
| + // | |
| + _updateCommentStyle: function() { | |
| + var fun = this.model.hasComment() ? 'addClass' : 'removeClass'; | |
| + | |
| + jQuery(this.el)[fun]('hasComment'); | |
| + }, | |
| updateAttributes: function() { | |
| var title = '', | |
| hasComment = this.model.hasComment(); | |
| @@ -223,7 +234,9 @@ Intermap.InteractionView = Backbone.View.extend({ | |
| else if(this.model.hasRelationship()) { | |
| title = this.messages.relationship; | |
| } | |
| - jQuery(this.el).attr('title', title)[hasComment ? 'addClass' : 'removeClass']('hasComment'); | |
| + | |
| + jQuery(this.el).attr('title', title); | |
| + this._updateCommentStyle(); | |
| if(title == '') jQuery(this.el).removeAttr('title'); | |
| }, | |
| diff --git a/js/intermap/views/IntermapApplicationView.js b/js/intermap/views/IntermapApplicationView.js | |
| index b566302..5e3e564 100644 | |
| --- a/js/intermap/views/IntermapApplicationView.js | |
| +++ b/js/intermap/views/IntermapApplicationView.js | |
| @@ -1,152 +1,244 @@ | |
| // the view for the application model | |
| Intermap.ApplicationView = Backbone.View.extend({ | |
| - el: '#application', | |
| - elError: null, | |
| - _router: null, | |
| - initialize: function() { | |
| - _.bindAll(this, 'error', 'updateStatusPosition', 'closeError'); | |
| - | |
| - new Intermap.RegisterView(); | |
| - | |
| - // displays the graph | |
| - var dotView = new Intermap.IntermapDotView({model: this.model.get('dotModel')}); | |
| - | |
| - // routing for the application | |
| - var router = new Intermap.Router(); | |
| - this._router = router; | |
| - Backbone.history.start(); // required to perform initial routing | |
| - | |
| - // the main screens | |
| - this._loginView = new Intermap.LoginView({router: router, model: this.model.get('auth')}); | |
| - new Intermap.ProjectsListView({model: this.model.get('projects')}); | |
| - | |
| - | |
| - var projectView = null; | |
| - if(this.model.get('project')) { | |
| - projectView = new Intermap.ProjectView({model: this.model.get('project')}); | |
| - } | |
| - | |
| - var self = this; | |
| - function loadProject() { | |
| - dotView.clear(); | |
| - // removes bound events. | |
| - // @todo, reconsider how this works, and instead set a new model | |
| - // and allow the view to re-render. | |
| - if(projectView) projectView.unbind(); | |
| - | |
| - projectView = new Intermap.ProjectView({model: self.model.get('project')}); | |
| - | |
| - // shows the matrix only after the project has been rendered above | |
| - router._showScreen('#matrix'); | |
| - _.delay(self.updateStatusPosition, 500); | |
| - } | |
| - this.model.bind('change:project', loadProject); | |
| - | |
| - // when we sort the terms, we want to reload the entire project. | |
| - // someday, we should just reload the matrix view, but the matrix | |
| - // view is what takes the longest anyway | |
| - this.model.bind('sort:terms', loadProject); | |
| - | |
| - // setup info box | |
| - this.elError = jQuery('<div class="message error"><div class="text"></div><button class="closeError">Close</button></div>'); | |
| - this.elError.prependTo('body').hide(); | |
| - | |
| - // setup close button | |
| - this.elError.find('button.closeError').click(this.closeError); | |
| - | |
| - this.elError.children('.button').click(function(){ | |
| - self.elError.slideUp(); | |
| - }); | |
| - | |
| - this.model.bind('error', function(model, error) { | |
| - if(error == 'Could not load project') { | |
| - router.navigate('projects', true); | |
| - } | |
| - self.error(error); | |
| - }); | |
| - | |
| - this.model.bind('info', function(model, info) { | |
| - self.info(info); | |
| - }); | |
| - | |
| - jQuery(window).scroll(this.updateStatusPosition).resize(this.updateStatusPosition); | |
| - jQuery(window).scroll(this.updateMessagePosition).resize(this.updateMessagePosition); | |
| - }, | |
| - getRouter: function() { return this._router; }, | |
| - getLoginView: function() { return this._loginView; }, | |
| - | |
| - _errorBoxDelay: 5000, | |
| - info: function(msg) { | |
| - this.clearMessageClasses('info'); | |
| - this.message(msg); | |
| - return this; | |
| - }, | |
| - clearMessageClasses: function(newClass){ | |
| - var self = this; | |
| - _(['error', 'info', 'warning']).each(function(className) { | |
| - self.elError.removeClass(className); | |
| - }); | |
| - if(newClass) { | |
| - this.elError.addClass(newClass); | |
| - } | |
| - }, | |
| - error: function(msg) { | |
| - this.clearMessageClasses('error'); | |
| - this.message(msg); | |
| - }, | |
| - message: function(msg) { | |
| - var elError = this.elError; | |
| - this.updateMessagePosition(); | |
| - elError.children('.text').html(msg); | |
| - elError.stop(true, false).slideDown().delay(this._errorBoxDelay).slideUp(); | |
| - }, | |
| - errorFun: function(msg) { | |
| - var self = this; | |
| - return function() { | |
| - self.error(msg); | |
| - }; | |
| - }, | |
| - setStatusVisibility: function(isVisible) { | |
| - jQuery('#status')[isVisible?'show':'hide'](); | |
| - }, | |
| - status: function(msg) { | |
| - var el = jQuery('#status > .statusText'); | |
| - el.html(msg); | |
| - if(msg == '') { | |
| - this.setStatusVisibility(false); | |
| - } | |
| - else { | |
| - this.setStatusVisibility(true); | |
| - this.updateStatusPosition(); | |
| - } | |
| - }, | |
| - closeError: function() { | |
| - this.elError.stop(true).slideUp(); | |
| - }, | |
| - // sticky footer | |
| - // http://css-tricks.com/snippets/jquery/jquery-sticky-footer/ | |
| - updateStatusPosition: function() { | |
| + el: '#application', | |
| + elError: null, | |
| + _router: null, | |
| + events: { | |
| + 'click .addTerm': 'onAddTermClick', | |
| + 'focus input.termText': 'showDeleteButton', | |
| + 'click #linkDeleteActiveTerm': 'onDeleteTermClick' | |
| + }, | |
| + // stores the ProjectListView for the application view | |
| + _projectListView: null, | |
| + initialize: function() { | |
| + _.bindAll(this, 'error', 'updateStatusPosition', 'closeError', | |
| + 'processMessageQueue', 'getProjectListView', 'onAddTermClick', | |
| + 'showDeleteButton', 'hideDeleteButton'); | |
| + | |
| + new Intermap.RegisterView(); | |
| + | |
| + // displays the graph | |
| + var dotView = new Intermap.IntermapDotView({model: this.model.get('dotModel')}); | |
| + | |
| + // routing for the application | |
| + var router = new Intermap.Router(); | |
| + this._router = router; | |
| + Backbone.history.start(); // required to perform initial routing | |
| + | |
| + // the main screens | |
| + this._loginView = new Intermap.LoginView({router: router, model: this.model.get('auth')}); | |
| + this._projectListView = new Intermap.ProjectsListView({model: this.model.get('projects')}); | |
| + | |
| + | |
| + var projectView = null; | |
| + if(this.model.get('project')) { | |
| + projectView = new Intermap.ProjectView({model: this.model.get('project')}); | |
| + } | |
| + | |
| + var self = this; | |
| + function loadProject() { | |
| + dotView.clear(); | |
| + // removes bound events. | |
| + // @todo, reconsider how this works, and instead set a new model | |
| + // and allow the view to re-render. | |
| + if(projectView) projectView.unbind(); | |
| + | |
| + projectView = new Intermap.ProjectView({model: self.model.get('project')}); | |
| + | |
| + // shows the matrix only after the project has been rendered above | |
| + router._showScreen('#matrix'); | |
| + _.delay(self.updateStatusPosition, 500); | |
| + } | |
| + this.model.bind('change:project', loadProject); | |
| + | |
| + // when we sort the terms, we want to reload the entire project. | |
| + // someday, we should just reload the matrix view, but the matrix | |
| + // view is what takes the longest anyway | |
| + this.model.bind('sort:terms', loadProject); | |
| + | |
| + // setup info box | |
| + this.elError = jQuery('<div class="message error"><div class="text"></div><button class="closeError">Close</button></div>'); | |
| + this.elError.prependTo('body').hide(); | |
| + | |
| + // setup close button | |
| + this.elError.find('button.closeError').click(this.closeError); | |
| + | |
| + this.elError.children('.button').click(function(){ | |
| + self.elError.slideUp(); | |
| + }); | |
| + | |
| + this.model.bind('error', function(model, error) { | |
| + if(error == 'Could not load project') { | |
| + router.navigate('projects', true); | |
| + } | |
| + self.error(error); | |
| + }); | |
| + | |
| + this.model.bind('info', function(model, info) { | |
| + self.info(info); | |
| + }); | |
| + | |
| + jQuery(window).scroll(this.updateStatusPosition).resize(this.updateStatusPosition); | |
| + jQuery(window).scroll(this.updateMessagePosition).resize(this.updateMessagePosition); | |
| + | |
| + // this will hold messages that need to be processed | |
| + this._messageQueue = []; | |
| + }, | |
| + | |
| + // retrieves the IntermapRouter for the application | |
| + getRouter: function() { return this._router; }, | |
| + | |
| + // retrieves the LoginView object for the application | |
| + getLoginView: function() { return this._loginView; }, | |
| + | |
| + // called when the "Add Term" button is clicked | |
| + onAddTermClick: function(evt) { | |
| + Intermap.App.addTermFromText(); | |
| + }, | |
| + | |
| + // called when the "Delete Term" button is clicked to delete the term | |
| + onDeleteTermClick: function(evt) { | |
| + var termText = jQuery('#linkDeleteActiveTerm').data('termText'); | |
| + if(termText) { | |
| + Intermap.App.deleteTermFromText(termText); | |
| + } | |
| + | |
| + }, | |
| + | |
| + // Used to show corresponding delete button for the activated TermInputView | |
| + // input box. | |
| + | |
| + showDeleteButton: function(evt) { | |
| + // unbind the hide button from the previous term | |
| + if(this._showDeleteButtonTerm) | |
| + this._showDeleteButtonTerm.unbind('destroy', this.hideDeleteButton); | |
| + | |
| + var el = jQuery(evt.target), | |
| + termText = el.val(), | |
| + term = Intermap.App.getTermByText(termText); | |
| + | |
| + this.$('#linkDeleteActiveTerm') | |
| + .data('termText', termText) | |
| + .html('Delete Term "' + _.shorten(termText) + '"') | |
| + .show(); | |
| + | |
| + // bind the hide delete button so that if the term is destroyed, | |
| + // the delete button will be hidden | |
| + term.bind('destroy', this.hideDeleteButton); | |
| + this._showDeleteButtonTerm = term; | |
| + }, | |
| + | |
| + // Used to hide the delete button | |
| + // | |
| + hideDeleteButton: function() { | |
| + this.$('#linkDeleteActiveTerm').hide(); | |
| + }, | |
| + | |
| + _errorBoxDelay: 5000, | |
| + info: function(msg) { | |
| + this.clearMessageClasses('info'); | |
| + this.message(msg); | |
| + return this; | |
| + }, | |
| + clearMessageClasses: function(newClass){ | |
| + var self = this; | |
| + _(['error', 'info', 'warning']).each(function(className) { | |
| + self.elError.removeClass(className); | |
| + }); | |
| + if(newClass) { | |
| + this.elError.addClass(newClass); | |
| + } | |
| + }, | |
| + error: function(msg) { | |
| + this.clearMessageClasses('error'); | |
| + this.message(msg); | |
| + }, | |
| + _messageQueue: null, | |
| + _lastMessageTime: null, | |
| + message: function(msg) { | |
| + var self = this; | |
| + | |
| + var displayMessage = function() { | |
| + var elError = self.elError; | |
| + self.updateMessagePosition(); | |
| + elError.children('.text').html(msg); | |
| + elError.stop(true, false).slideDown().delay(self._errorBoxDelay).slideUp(); | |
| + }; | |
| + | |
| + // queue this function to run when the next opportunity emerges | |
| + this._messageQueue.push(displayMessage); | |
| + | |
| + this.processMessageQueue(); | |
| + }, | |
| + // Used to process messages in the queue | |
| + processMessageQueue: function() { | |
| + // if we are already processing a message, skip, cuz we don't want to | |
| + // clobber the other message | |
| + if(this._messageInProgress || this._messageQueue.length == 0) return; | |
| + | |
| + // process the first message in the queue | |
| + this._messageInProgress = true; // a message is being shown | |
| + this._messageQueue.shift().apply(); | |
| + | |
| + // process the other messages in a few seconds | |
| + var self = this; | |
| + _.delay(function(){ | |
| + // message has finished being shown | |
| + self._messageInProgress = false; | |
| + // resume processing queue | |
| + self.processMessageQueue(); | |
| + }, 2000); | |
| + | |
| + }, | |
| + errorFun: function(msg) { | |
| + var self = this; | |
| + return function() { | |
| + self.error(msg); | |
| + }; | |
| + }, | |
| + setStatusVisibility: function(isVisible) { | |
| + jQuery('#status')[isVisible?'show':'hide'](); | |
| + }, | |
| + status: function(msg) { | |
| + var el = jQuery('#status > .statusText'); | |
| + el.html(msg); | |
| + if(msg == '') { | |
| + this.setStatusVisibility(false); | |
| + } | |
| + else { | |
| + this.setStatusVisibility(true); | |
| + this.updateStatusPosition(); | |
| + } | |
| + }, | |
| + closeError: function() { | |
| + this.elError.stop(true).slideUp(); | |
| + }, | |
| + // sticky footer | |
| + // http://css-tricks.com/snippets/jquery/jquery-sticky-footer/ | |
| + updateStatusPosition: function() { | |
| var $ = jQuery, | |
| - el = $("#status"), | |
| - win = $(window); | |
| - | |
| + el = $("#status"), | |
| + win = $(window); | |
| + | |
| if(!el.is(':visible')) return; | |
| - | |
| + | |
| // update height using its child | |
| el.height(el.children('.statusText').height()); | |
| var top = win.scrollTop() + win.height() - el.outerHeight() - 150; | |
| el.stop().animate({top: top}, 'fast'); | |
| - }, | |
| - updateMessagePosition: function() { | |
| - var $ = jQuery, | |
| - el = $(".message"), | |
| - win = $(window); | |
| + }, | |
| + updateMessagePosition: function() { | |
| + var $ = jQuery, | |
| + el = $(".message"), | |
| + win = $(window); | |
| if(!el.is(':visible')) return; | |
| - var top = win.scrollTop(); | |
| + var top = win.scrollTop(); | |
| - el.stop(true, true).animate({top: top}, 'fast').delay(this._errorBoxDelay).slideUp(); | |
| - } | |
| + el.stop(true, true).animate({top: top}, 'fast').delay(this._errorBoxDelay).slideUp(); | |
| + }, | |
| + // Returns the ProjectListView for the application view. | |
| + getProjectListView: function() { return this._projectListView; } | |
| }); | |
| diff --git a/js/intermap/views/IntermapTermTagView.js b/js/intermap/views/IntermapTermTagView.js | |
| index b4c25e4..35fb127 100644 | |
| --- a/js/intermap/views/IntermapTermTagView.js | |
| +++ b/js/intermap/views/IntermapTermTagView.js | |
| @@ -31,7 +31,7 @@ Intermap.TermTagView = Backbone.View.extend({ | |
| tag = termTag.get('tag'); | |
| if (tag){ | |
| - jQuery(this.el).html(tag.escape('text')) | |
| + jQuery(this.el).html(_.shorten(tag.escape('text'))) | |
| .css({ | |
| backgroundColor: tag.escape('bgcolor'), | |
| color: tag.escape('fgcolor') | |
| diff --git a/js/intermap/views/ProjectsListView.js b/js/intermap/views/ProjectsListView.js | |
| index 7b1272e..666f0dd 100644 | |
| --- a/js/intermap/views/ProjectsListView.js | |
| +++ b/js/intermap/views/ProjectsListView.js | |
| @@ -5,7 +5,7 @@ Intermap.ProjectsListView = Backbone.View.extend({ | |
| 'click .cloneLink': 'cloneProject' | |
| }, | |
| initialize: function() { | |
| - _.bindAll(this, 'render'); | |
| + _.bindAll(this, 'render', 'renderProjectsSubmenu'); | |
| var self = this; | |
| _.each(['add', 'delete', 'reset'], function(event){ | |
| self.model.bind(event, self.render); | |
| @@ -46,8 +46,8 @@ Intermap.ProjectsListView = Backbone.View.extend({ | |
| error: Intermap.View.errorFun('Could not copy project.') | |
| }); | |
| }, | |
| - render: function() { | |
| - var projects = this.model.map(function(model) { | |
| + getProjectsAttributes: function() { | |
| + return this.model.map(function(model) { | |
| return _.extend({}, | |
| model.attributes, | |
| { | |
| @@ -56,13 +56,21 @@ Intermap.ProjectsListView = Backbone.View.extend({ | |
| }) | |
| }); | |
| }); | |
| + }, | |
| + render: function() { | |
| this.$('.projects-list').html( | |
| - jQuery('#tmplProjectListItem').tmpl(projects) | |
| + jQuery('#tmplProjectListItem').tmpl(this.getProjectsAttributes()) | |
| ); | |
| + this.renderProjectsSubmenu(); | |
| + }, | |
| + // Renders the projects sub menu that is displayed as a menu on the | |
| + // matrix screen. This should be called whenever the projects list | |
| + // is updated. Maybe it should be bound to the reset? | |
| + renderProjectsSubmenu: function() { | |
| // this is the menu that appears on the #matrix screen | |
| jQuery('.projectsSubmenu').html( | |
| - jQuery('#tmplProjectSubmenuItem').tmpl(projects) | |
| + jQuery('#tmplProjectSubmenuItem').tmpl(this.getProjectsAttributes()) | |
| ); | |
| } | |
| }); | |
| \ No newline at end of file | |
| diff --git a/js/intermap/views/TermReorderView.js b/js/intermap/views/TermReorderView.js | |
| index 37f4cf6..dbd5c2d 100644 | |
| --- a/js/intermap/views/TermReorderView.js | |
| +++ b/js/intermap/views/TermReorderView.js | |
| @@ -44,7 +44,7 @@ Intermap.TermReorderView = Backbone.View.extend({ | |
| }, | |
| render: function(){ | |
| var el = jQuery(this.el).empty().append( | |
| - '<a href="javascript:" class="orderAlpha order">Alphabetically</a><a href="javascript:" class="orderTag order">By Tag</a>' | |
| + '<a href="javascript:" class="orderAlpha order">Alphabetically</a><a href="javascript:" class="orderTag order">By Tag</a><button class="submit">Save</button>' | |
| ), | |
| ul = jQuery('<ul></ul>').appendTo(el), | |
| tmpl = jQuery('#tmplReorderTermsListItem'), | |
| diff --git a/js/intermap/views/TermTopView.js b/js/intermap/views/TermTopView.js | |
| index 6f4d6ec..79f9563 100644 | |
| --- a/js/intermap/views/TermTopView.js | |
| +++ b/js/intermap/views/TermTopView.js | |
| @@ -37,22 +37,26 @@ Intermap.TermTopView = Backbone.View.extend({ | |
| jQuery(this.el).remove(); | |
| }, | |
| render: function() { | |
| - var newEl = jQuery("#topTerms .template").tmpl(this.model.attributes, { | |
| - // these methods are duplicated in TermReorderView, and should | |
| - // be refactored to be DRY | |
| - getTag: function() { | |
| - var tag = this.data.tag; | |
| - return tag ? tag.escape('text') : ''; | |
| - }, | |
| - getTagBackgroundColor: function() { | |
| - var tag = this.data.tag; | |
| - return tag ? tag.get('bgcolor') : 'transparent'; | |
| - }, | |
| - getTagForegroundColor: function() { | |
| - var tag = this.data.tag; | |
| - return tag ? tag.get('fgcolor') : 'black'; | |
| - } | |
| - }).unwrap(); | |
| + var self = this, | |
| + newEl = jQuery("#topTerms .template").tmpl(this.model, { | |
| + // these methods are duplicated in TermReorderView, and should | |
| + // be refactored to be DRY | |
| + getTag: function() { | |
| + var tag = this.data.get('tag'); | |
| + return tag ? _.shorten(tag.escape('text')) : ''; | |
| + }, | |
| + getTagBackgroundColor: function() { | |
| + var tag = this.data.get('tag'); | |
| + return tag ? tag.get('bgcolor') : 'transparent'; | |
| + }, | |
| + getTagForegroundColor: function() { | |
| + var tag = this.data.get('tag'); | |
| + return tag ? tag.get('fgcolor') : 'black'; | |
| + }, | |
| + getTerm: function() { | |
| + return _.shorten(this.data.escape('text'), 30); | |
| + } | |
| + }).unwrap(); | |
| jQuery(this.el).html(newEl); | |
| return this; | |
| }, | |
| diff --git a/js/underscore-intermap.js b/js/underscore-intermap.js | |
| index 574fe13..b888d38 100644 | |
| --- a/js/underscore-intermap.js | |
| +++ b/js/underscore-intermap.js | |
| @@ -17,5 +17,11 @@ _.mixin({ | |
| replacement = replacement || '\\"'; | |
| return _.quote(str.replace(/"/g, replacement), '"'); | |
| + }, | |
| + | |
| + // shortens the string | |
| + shorten: function(str, len) { | |
| + len || (len = 10); | |
| + return str.length > len ? str.substr(0, len-3) + '...' : str; | |
| } | |
| }); | |
| diff --git a/tests/jasmine/dot.js b/tests/jasmine/dot.js | |
| index 1937da8..746d6ba 100644 | |
| --- a/tests/jasmine/dot.js | |
| +++ b/tests/jasmine/dot.js | |
| @@ -2,6 +2,13 @@ describe("DOT file", function(){ | |
| var | |
| reusableTerms, | |
| project; | |
| + | |
| + function forceTermsNotNew(terms) { | |
| + terms.each(function(term) { | |
| + // force id to be something so that it isn't new | |
| + term.id = 1; | |
| + }); | |
| + } | |
| //custom matchers | |
| beforeEach(function() { | |
| @@ -40,11 +47,14 @@ describe("DOT file", function(){ | |
| it("can have multiple-word terms", function(){ | |
| // the dot file uses ids for the nodes of the form c\d+ ie, c200, c1 | |
| var dotRE = /^graph myGraph { label="New Project\\lCentral terms are shown as octagons" labelloc="t" c\d+ \[label="test me"\]; }$/ig, | |
| - localIntermap = new Intermap.Application(); | |
| + localIntermap = new Intermap.Application(), | |
| + project = localIntermap.get('project'); | |
| localIntermap.addTermFromText('test me'); | |
| + forceTermsNotNew (project.get('terms')); | |
| + | |
| var dotModel = new Intermap.DotFile({ | |
| - project: localIntermap.get('project') | |
| + project: project | |
| }); | |
| expect(dotModel.getDot()).toMatch(dotRE); | |
| }); | |
| @@ -56,6 +66,7 @@ describe("DOT file", function(){ | |
| dotModel = new Intermap.DotFile({ | |
| project: project | |
| }); | |
| + forceTermsNotNew (project.get('terms')); | |
| expect(interactions.length).not.toEqual(0); | |
| expect(dotModel.getDot()).toMatch(dotFile); | |
| }); | |
| @@ -69,7 +80,7 @@ describe("DOT file", function(){ | |
| dotModel = new Intermap.DotFile({ | |
| project: project | |
| }), | |
| - dotMatcher = /graph myGraph { label="New Project\\lCentral terms are shown as octagons" labelloc="t" c\d+ \[style=filled,fillcolor=([A-Za-z]+),label="A"\];c\d+ \[style=filled,fillcolor=\1,label="B"\];c\d+ \[style=filled,fillcolor=([A-Za-z]+),label="C"\];\s+{ rank=sink; legend \[label="Tag Legend", shape=none, fontsize=14\]; c\d+ \[label=dog,fillcolor=\1,style=filled,shape=box,fontsize=14\];c\d+ \[label=cat,fillcolor=\2,style=filled,shape=box,fontsize=14\]; }}/ | |
| + dotMatcher = /graph myGraph { label="New Project\\lCentral terms are shown as octagons" labelloc="t" c\d+ \[style=filled,fillcolor="([#A-Za-z\d]+)",label="A"\];c\d+ \[style=filled,fillcolor="\1",label="B"\];c\d+ \[style=filled,fillcolor="([#A-Za-z\d]+)",fontcolor="white",label="C"\];\s+{ rank=sink; legend \[label="Tag Legend", shape=none, fontsize=14\]; c\d+ \[label="dog",fillcolor="\1",style=filled,shape=box,fontsize=14\];c\d+ \[label="cat",fillcolor="\2",style=filled,shape=box,fontsize=14,fontcolor="white"\]; }}/; | |
| _.each([ | |
| @@ -79,6 +90,7 @@ describe("DOT file", function(){ | |
| ], function(termProps) { | |
| terms.add(new Intermap.Term(termProps)); | |
| }); | |
| + forceTermsNotNew (terms); | |
| expect(dotModel.getDot()).toMatch(dotMatcher); | |
| }); | |
| diff --git a/tests/jasmine/index.html b/tests/jasmine/index.html | |
| index 7225cb8..0431c08 100644 | |
| --- a/tests/jasmine/index.html | |
| +++ b/tests/jasmine/index.html | |
| @@ -10,6 +10,7 @@ | |
| <!-- include source files here... --> | |
| <script type="text/javascript" src="../../js/underscore.js"></script> | |
| + <script type="text/javascript" src="../../js/underscore-intermap.js"></script> | |
| <script type="text/javascript" src="../../js/backbone.js"></script> | |
| <!-- Intermap.App Source Files --> | |
| diff --git a/tests/selenium/GraphFromSample.html b/tests/selenium/GraphFromSample.html | |
| index 80118eb..c5b802e 100644 | |
| --- a/tests/selenium/GraphFromSample.html | |
| +++ b/tests/selenium/GraphFromSample.html | |
| @@ -156,12 +156,12 @@ | |
| </tr> | |
| <tr> | |
| <td>click</td> | |
| - <td>//div[@id='leftTerms']/div[2]/span[2]/img</td> | |
| + <td>//div[@id='leftTerms']/div[2]/span[2]</td> | |
| <td></td> | |
| </tr> | |
| <tr> | |
| <td>click</td> | |
| - <td>//div[@id='leftTerms']/div[1]/span[2]/img</td> | |
| + <td>//div[@id='leftTerms']/div[1]/span[2]</td> | |
| <td></td> | |
| </tr> | |
| <tr> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment