Work on day separators.
* Overrode ``EntryModel.{get,set}`` to return a ``Date`` object and allow you to
  set the value with either a ``Date`` object, or the JSON date string.
* Updated code to reflect the ``EntryModel.timestamp`` data change.
* Added ``daysApart`` to calculate calendar days between given ``Date``s
* Added ``getEnglishDate`` to return a date description relative to the current
  day ("Today", "Last year" for example).
			
			
This commit is contained in:
		
							
								
								
									
										113
									
								
								www/js/ts.js
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								www/js/ts.js
									
									
									
									
									
								
							| @@ -11,10 +11,40 @@ $(document).ready(function(){ | |||||||
|      *  - id |      *  - id | ||||||
|      *  - mark |      *  - mark | ||||||
|      *  - notes |      *  - notes | ||||||
|      *  - start |      *  - timestamp | ||||||
|      */ |      */ | ||||||
|     TS.EntryModel = Backbone.Model.extend({ |     TS.EntryModel = Backbone.Model.extend({ | ||||||
|  |  | ||||||
|  |         get: function(attribute) { | ||||||
|  |             if (attribute == "timestamp") { | ||||||
|  |                 if (!this.timestampDate) { | ||||||
|  |                     this.timestampDate = new Date( | ||||||
|  |                         Backbone.Model.prototype.get.call(this, attribute)); | ||||||
|  |                 } | ||||||
|  |                 return this.timestampDate; | ||||||
|  |             } else { | ||||||
|  |                 return Backbone.Model.prototype.get.call(this, attribute); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         set: function(attributes, options) { | ||||||
|  |             var attrsToSet = {} | ||||||
|  |             _.each(attributes, function(val, key) { | ||||||
|  |                 if (key == "timestamp") { | ||||||
|  |                     if (val instanceof Date) { | ||||||
|  |                         this.timestampDate = val; | ||||||
|  |                         attrsToSet.timestamp = dateToJSON(val); | ||||||
|  |                     } else { | ||||||
|  |                         this.timestampDate = new Date(val); | ||||||
|  |                         attrsToSet.timestamp = dateToJSON(this.timestampDate); | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     attrsToSet[key] = val; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             return Backbone.Model.prototype.set.call(this, attrsToSet, options); | ||||||
|  |         } | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     /* Timeline model. |     /* Timeline model. | ||||||
| @@ -207,7 +237,7 @@ $(document).ready(function(){ | |||||||
|         save: function() { |         save: function() { | ||||||
|             this.model.save({ |             this.model.save({ | ||||||
|                 mark: this.$('.mark-input').val(),  |                 mark: this.$('.mark-input').val(),  | ||||||
|                 timestamp: this.$('.timestamp-input').val(), |                 timestamp: new Date(this.$('.timestamp-input').val()), | ||||||
|                 notes: this.$('.notes-input').val()}); |                 notes: this.$('.notes-input').val()}); | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
| @@ -250,12 +280,12 @@ $(document).ready(function(){ | |||||||
|          * @return the duration between model and nextModel, formatted for  |          * @return the duration between model and nextModel, formatted for  | ||||||
|          *         display: `Xd Yhr Zm`. */ |          *         display: `Xd Yhr Zm`. */ | ||||||
|         formatDuration: function(model, nextModel) { |         formatDuration: function(model, nextModel) { | ||||||
|             var d1 = new Date(model.get('timestamp')); |             var d1 = model.get('timestamp'); | ||||||
|             var d2, diff; |             var d2, diff; | ||||||
|             var day, hr, min; |             var day, hr, min; | ||||||
|  |  | ||||||
|             // if no next model, assume it's an onoing task |             // if no next model, assume it's an onoing task | ||||||
|             if (nextModel) { d2 = new Date(nextModel.get('timestamp')); } |             if (nextModel) { d2 = nextModel.get('timestamp'); } | ||||||
|             else { d2 = new Date(); } |             else { d2 = new Date(); } | ||||||
|  |  | ||||||
|             diff= d2.getTime() - d1.getTime(); |             diff= d2.getTime() - d1.getTime(); | ||||||
| @@ -345,7 +375,7 @@ $(document).ready(function(){ | |||||||
|                 // create the mark. Immediately fetch to get server-side timestamp |                 // create the mark. Immediately fetch to get server-side timestamp | ||||||
|                 this.collection.create({mark: entryMark, |                 this.collection.create({mark: entryMark, | ||||||
|                                         notes: '', |                                         notes: '', | ||||||
|                                         timestamp: getUTCTimestamp()}).fetch(); |                                         timestamp: new Date()}).fetch(); | ||||||
|  |  | ||||||
|                 // clear the input for the next entry |                 // clear the input for the next entry | ||||||
|                 this.$("#new-entry-input").val(""); |                 this.$("#new-entry-input").val(""); | ||||||
| @@ -367,11 +397,23 @@ $(document).ready(function(){ | |||||||
|                 userExclusions.concat(timelineExclusions), |                 userExclusions.concat(timelineExclusions), | ||||||
|                 function(exclusion) { return new RegExp(exclusion)} ); |                 function(exclusion) { return new RegExp(exclusion)} ); | ||||||
|  |  | ||||||
|  |             // clear existing elements in the view container | ||||||
|             this.entryContainer.empty(); |             this.entryContainer.empty(); | ||||||
|  |  | ||||||
|  |             // last day we have printed a separator for | ||||||
|  |             var currentDay = null; | ||||||
|  |             var today = new Date(); | ||||||
|  |  | ||||||
|  |             // iterate through the collection and render the elements. | ||||||
|             for (var i = 0, len = this.collection.length; i < len; i++) { |             for (var i = 0, len = this.collection.length; i < len; i++) { | ||||||
|                 var entry = this.collection.at(i); |                 var entry = this.collection.at(i); | ||||||
|                 var nextEntry = (i + 1 < len ? this.collection.at(i + 1) : null); |                 var nextEntry = (i + 1 < len ? this.collection.at(i + 1) : null); | ||||||
|  |  | ||||||
|  |                 if (currentDay != entry.get('timestamp').getDate()) { | ||||||
|  |                     currentDay = entry.get('timestamp').getDate(); | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 this.renderOne(entry, nextEntry); |                 this.renderOne(entry, nextEntry); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -687,7 +729,7 @@ $(document).ready(function(){ | |||||||
|             var timelineDesc = this.$("#new-timeline-desc").val(); |             var timelineDesc = this.$("#new-timeline-desc").val(); | ||||||
|             this.timelineCollection.create( |             this.timelineCollection.create( | ||||||
|                 {id: timelineId, description: timelineDesc,  |                 {id: timelineId, description: timelineDesc,  | ||||||
|                  created: getUTCTimestamp()}); |                  created: dateToJSON(new Date())}); | ||||||
|             this.hide(); |             this.hide(); | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
| @@ -705,9 +747,7 @@ $(document).ready(function(){ | |||||||
|  |  | ||||||
| }) | }) | ||||||
|  |  | ||||||
| function getUTCTimestamp() { | function dateToJSON(d) { | ||||||
|     var d = new Date(); |  | ||||||
|  |  | ||||||
|     function pad(n){return n<10 ? '0'+n : n} |     function pad(n){return n<10 ? '0'+n : n} | ||||||
|  |  | ||||||
|     return d.getUTCFullYear()+'-' |     return d.getUTCFullYear()+'-' | ||||||
| @@ -718,3 +758,58 @@ function getUTCTimestamp() { | |||||||
|         + pad(d.getUTCSeconds())+'Z'; |         + pad(d.getUTCSeconds())+'Z'; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function daysApart(d1, d2) { | ||||||
|  |     var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30]; | ||||||
|  |  | ||||||
|  |     days1 = (d1.getFullYear() * 365) + daysInMonth[d1.getMonth()] + d1.getDate(); | ||||||
|  |     days2 = (d2.getFullYear() * 365) + daysInMonth[d2.getMonth()] + d2.getDate(); | ||||||
|  |  | ||||||
|  |     return days1 - days2; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function getEnglishDate(d) { | ||||||
|  |     if (typeof getEnglishDate.today == 'undefined') { | ||||||
|  |         getEnglishDate.today = new Date() }; | ||||||
|  |  | ||||||
|  |     var yearDiff = today.getFullYear() - d.getFullYear(); | ||||||
|  |     var monthDiff = today.getMonth() - d.getMonth(); | ||||||
|  |     var dayDiff = today.getDate() - d.getDate(); | ||||||
|  |  | ||||||
|  |     if (yearDiff > 1) { return capitalize(toWords(yearDiff)) + " years ago."; } | ||||||
|  |     else if (yearDiff > 0) { return "Last year"; } | ||||||
|  |     else if (monthDiff > 1) { | ||||||
|  |         return capitalize(toWords(monthDiff)) + "months ago."; } | ||||||
|  |     else if (monthDiff > 0) { return "Last month."; } | ||||||
|  |     else if (dayDiff > 0) { | ||||||
|  |         // weeks as 7-day periods | ||||||
|  |         var weekDiff = Math.ceil(dayDiff / 7) | ||||||
|  |  | ||||||
|  |         // adjust for the hard boundary of the weekend | ||||||
|  |         if (today.getDay() > d.getDay()) { weekDiff--; } | ||||||
|  |  | ||||||
|  |         if (weekDiff > 1) { | ||||||
|  |             return capitalize(toWords(weekDiff)) + " weeks ago."; } | ||||||
|  |         else if (weekDiff > 0) { return "Last week."; } | ||||||
|  |         else { return capitalize(toWords(dayDiff)) + " days ago."; } | ||||||
|  |  | ||||||
|  |     } else if (yearDiff < 0 || monthDiff < 0 || daysDiff < 0) { | ||||||
|  |         return "In the future."; } | ||||||
|  |     else { return "Today."; } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function toWords(i) { | ||||||
|  |     if (typeof toWords.words == 'undefined') { | ||||||
|  |         toWords.words = ['zero','one','two','three','four', 'five','six', | ||||||
|  |                           'seven','eight','nine','ten','eleven','twelve', | ||||||
|  |                           'thirteen', 'fourteen','fifteen','sixteen', | ||||||
|  |                           'seventeen','eighteen','nineteen' ]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (i < 20) { return toWords.words[i]; } | ||||||
|  |     else { return i.toString(); } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function capitalize(s) { | ||||||
|  |     return s.slice(0, 1).toUpperCase() + s.slice(1); | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user