32 if ( window.console && revisions.debug ) { |
32 if ( window.console && revisions.debug ) { |
33 window.console.log.apply( window.console, arguments ); |
33 window.console.log.apply( window.console, arguments ); |
34 } |
34 } |
35 }; |
35 }; |
36 |
36 |
37 // Handy functions to help with positioning |
37 // Handy functions to help with positioning. |
38 $.fn.allOffsets = function() { |
38 $.fn.allOffsets = function() { |
39 var offset = this.offset() || {top: 0, left: 0}, win = $(window); |
39 var offset = this.offset() || {top: 0, left: 0}, win = $(window); |
40 return _.extend( offset, { |
40 return _.extend( offset, { |
41 right: win.width() - offset.left - this.outerWidth(), |
41 right: win.width() - offset.left - this.outerWidth(), |
42 bottom: win.height() - offset.top - this.outerHeight() |
42 bottom: win.height() - offset.top - this.outerHeight() |
69 |
69 |
70 initialize: function( options ) { |
70 initialize: function( options ) { |
71 this.frame = options.frame; |
71 this.frame = options.frame; |
72 this.revisions = options.revisions; |
72 this.revisions = options.revisions; |
73 |
73 |
74 // Listen for changes to the revisions or mode from outside |
74 // Listen for changes to the revisions or mode from outside. |
75 this.listenTo( this.frame, 'update:revisions', this.receiveRevisions ); |
75 this.listenTo( this.frame, 'update:revisions', this.receiveRevisions ); |
76 this.listenTo( this.frame, 'change:compareTwoMode', this.updateMode ); |
76 this.listenTo( this.frame, 'change:compareTwoMode', this.updateMode ); |
77 |
77 |
78 // Listen for internal changes |
78 // Listen for internal changes. |
79 this.on( 'change:from', this.handleLocalChanges ); |
79 this.on( 'change:from', this.handleLocalChanges ); |
80 this.on( 'change:to', this.handleLocalChanges ); |
80 this.on( 'change:to', this.handleLocalChanges ); |
81 this.on( 'change:compareTwoMode', this.updateSliderSettings ); |
81 this.on( 'change:compareTwoMode', this.updateSliderSettings ); |
82 this.on( 'update:revisions', this.updateSliderSettings ); |
82 this.on( 'update:revisions', this.updateSliderSettings ); |
83 |
83 |
84 // Listen for changes to the hovered revision |
84 // Listen for changes to the hovered revision. |
85 this.on( 'change:hoveredRevision', this.hoverRevision ); |
85 this.on( 'change:hoveredRevision', this.hoverRevision ); |
86 |
86 |
87 this.set({ |
87 this.set({ |
88 max: this.revisions.length - 1, |
88 max: this.revisions.length - 1, |
89 compareTwoMode: this.frame.get('compareTwoMode'), |
89 compareTwoMode: this.frame.get('compareTwoMode'), |
115 }); |
115 }); |
116 } |
116 } |
117 this.trigger( 'update:slider' ); |
117 this.trigger( 'update:slider' ); |
118 }, |
118 }, |
119 |
119 |
120 // Called when a revision is hovered |
120 // Called when a revision is hovered. |
121 hoverRevision: function( model, value ) { |
121 hoverRevision: function( model, value ) { |
122 this.trigger( 'hovered:revision', value ); |
122 this.trigger( 'hovered:revision', value ); |
123 }, |
123 }, |
124 |
124 |
125 // Called when `compareTwoMode` changes |
125 // Called when `compareTwoMode` changes. |
126 updateMode: function( model, value ) { |
126 updateMode: function( model, value ) { |
127 this.set({ compareTwoMode: value }); |
127 this.set({ compareTwoMode: value }); |
128 }, |
128 }, |
129 |
129 |
130 // Called when `from` or `to` changes in the local model |
130 // Called when `from` or `to` changes in the local model. |
131 handleLocalChanges: function() { |
131 handleLocalChanges: function() { |
132 this.frame.set({ |
132 this.frame.set({ |
133 from: this.get('from'), |
133 from: this.get('from'), |
134 to: this.get('to') |
134 to: this.get('to') |
135 }); |
135 }); |
136 }, |
136 }, |
137 |
137 |
138 // Receives revisions changes from outside the model |
138 // Receives revisions changes from outside the model. |
139 receiveRevisions: function( from, to ) { |
139 receiveRevisions: function( from, to ) { |
140 // Bail if nothing changed |
140 // Bail if nothing changed. |
141 if ( this.get('from') === from && this.get('to') === to ) { |
141 if ( this.get('from') === from && this.get('to') === to ) { |
142 return; |
142 return; |
143 } |
143 } |
144 |
144 |
145 this.set({ from: from, to: to }, { silent: true }); |
145 this.set({ from: from, to: to }, { silent: true }); |
150 |
150 |
151 revisions.model.Tooltip = Backbone.Model.extend({ |
151 revisions.model.Tooltip = Backbone.Model.extend({ |
152 defaults: { |
152 defaults: { |
153 revision: null, |
153 revision: null, |
154 offset: {}, |
154 offset: {}, |
155 hovering: false, // Whether the mouse is hovering |
155 hovering: false, // Whether the mouse is hovering. |
156 scrubbing: false // Whether the mouse is scrubbing |
156 scrubbing: false // Whether the mouse is scrubbing. |
157 }, |
157 }, |
158 |
158 |
159 initialize: function( options ) { |
159 initialize: function( options ) { |
160 this.frame = options.frame; |
160 this.frame = options.frame; |
161 this.revisions = options.revisions; |
161 this.revisions = options.revisions; |
253 if ( diff ) { |
253 if ( diff ) { |
254 deferred.resolveWith( context, [ diff ] ); |
254 deferred.resolveWith( context, [ diff ] ); |
255 } else { |
255 } else { |
256 this.trigger( 'ensure:load', ids, from, to, deferred.promise() ); |
256 this.trigger( 'ensure:load', ids, from, to, deferred.promise() ); |
257 _.each( ids, _.bind( function( id ) { |
257 _.each( ids, _.bind( function( id ) { |
258 // Remove anything that has an ongoing request |
258 // Remove anything that has an ongoing request. |
259 if ( this.requests[ id ] ) { |
259 if ( this.requests[ id ] ) { |
260 delete ids[ id ]; |
260 delete ids[ id ]; |
261 } |
261 } |
262 // Remove anything we already have |
262 // Remove anything we already have. |
263 if ( this.get( id ) ) { |
263 if ( this.get( id ) ) { |
264 delete ids[ id ]; |
264 delete ids[ id ]; |
265 } |
265 } |
266 }, this ) ); |
266 }, this ) ); |
267 if ( ! request ) { |
267 if ( ! request ) { |
268 // Always include the ID that started this ensure |
268 // Always include the ID that started this ensure. |
269 ids[ id ] = true; |
269 ids[ id ] = true; |
270 request = this.load( _.keys( ids ) ); |
270 request = this.load( _.keys( ids ) ); |
271 } |
271 } |
272 |
272 |
273 request.done( _.bind( function() { |
273 request.done( _.bind( function() { |
278 } |
278 } |
279 |
279 |
280 return deferred.promise(); |
280 return deferred.promise(); |
281 }, |
281 }, |
282 |
282 |
283 // Returns an array of proximal diffs |
283 // Returns an array of proximal diffs. |
284 getClosestUnloaded: function( ids, centerId ) { |
284 getClosestUnloaded: function( ids, centerId ) { |
285 var self = this; |
285 var self = this; |
286 return _.chain([0].concat( ids )).initial().zip( ids ).sortBy( function( pair ) { |
286 return _.chain([0].concat( ids )).initial().zip( ids ).sortBy( function( pair ) { |
287 return Math.abs( centerId - pair[1] ); |
287 return Math.abs( centerId - pair[1] ); |
288 }).map( function( pair ) { |
288 }).map( function( pair ) { |
315 return deferred; |
315 return deferred; |
316 }, |
316 }, |
317 |
317 |
318 load: function( comparisons ) { |
318 load: function( comparisons ) { |
319 wp.revisions.log( 'load', comparisons ); |
319 wp.revisions.log( 'load', comparisons ); |
320 // Our collection should only ever grow, never shrink, so remove: false |
320 // Our collection should only ever grow, never shrink, so `remove: false`. |
321 return this.fetch({ data: { compare: comparisons }, remove: false }).done( function() { |
321 return this.fetch({ data: { compare: comparisons }, remove: false }).done( function() { |
322 wp.revisions.log( 'load:complete', comparisons ); |
322 wp.revisions.log( 'load:complete', comparisons ); |
323 }); |
323 }); |
324 }, |
324 }, |
325 |
325 |
392 } ); |
392 } ); |
393 |
393 |
394 // Set the initial diffs collection. |
394 // Set the initial diffs collection. |
395 this.diffs.set( this.get( 'diffData' ) ); |
395 this.diffs.set( this.get( 'diffData' ) ); |
396 |
396 |
397 // Set up internal listeners |
397 // Set up internal listeners. |
398 this.listenTo( this, 'change:from', this.changeRevisionHandler ); |
398 this.listenTo( this, 'change:from', this.changeRevisionHandler ); |
399 this.listenTo( this, 'change:to', this.changeRevisionHandler ); |
399 this.listenTo( this, 'change:to', this.changeRevisionHandler ); |
400 this.listenTo( this, 'change:compareTwoMode', this.changeMode ); |
400 this.listenTo( this, 'change:compareTwoMode', this.changeMode ); |
401 this.listenTo( this, 'update:revisions', this.updatedRevisions ); |
401 this.listenTo( this, 'update:revisions', this.updatedRevisions ); |
402 this.listenTo( this.diffs, 'ensure:load', this.updateLoadingStatus ); |
402 this.listenTo( this.diffs, 'ensure:load', this.updateLoadingStatus ); |
408 to : this.revisions.get( state.to ), |
408 to : this.revisions.get( state.to ), |
409 from : this.revisions.get( state.from ), |
409 from : this.revisions.get( state.from ), |
410 compareTwoMode : state.compareTwoMode |
410 compareTwoMode : state.compareTwoMode |
411 } ); |
411 } ); |
412 |
412 |
413 // Start the router if browser supports History API |
413 // Start the router if browser supports History API. |
414 if ( window.history && window.history.pushState ) { |
414 if ( window.history && window.history.pushState ) { |
415 this.router = new revisions.Router({ model: this }); |
415 this.router = new revisions.Router({ model: this }); |
416 if ( Backbone.History.started ) { |
416 if ( Backbone.History.started ) { |
417 Backbone.history.stop(); |
417 Backbone.history.stop(); |
418 } |
418 } |
427 |
427 |
428 changeMode: function( model, value ) { |
428 changeMode: function( model, value ) { |
429 var toIndex = this.revisions.indexOf( this.get( 'to' ) ); |
429 var toIndex = this.revisions.indexOf( this.get( 'to' ) ); |
430 |
430 |
431 // If we were on the first revision before switching to two-handled mode, |
431 // If we were on the first revision before switching to two-handled mode, |
432 // bump the 'to' position over one |
432 // bump the 'to' position over one. |
433 if ( value && 0 === toIndex ) { |
433 if ( value && 0 === toIndex ) { |
434 this.set({ |
434 this.set({ |
435 from: this.revisions.at( toIndex ), |
435 from: this.revisions.at( toIndex ), |
436 to: this.revisions.at( toIndex + 1 ) |
436 to: this.revisions.at( toIndex + 1 ) |
437 }); |
437 }); |
438 } |
438 } |
439 |
439 |
440 // When switching back to single-handled mode, reset 'from' model to |
440 // When switching back to single-handled mode, reset 'from' model to |
441 // one position before the 'to' model |
441 // one position before the 'to' model. |
442 if ( ! value && 0 !== toIndex ) { // '! value' means switching to single-handled mode |
442 if ( ! value && 0 !== toIndex ) { // '! value' means switching to single-handled mode. |
443 this.set({ |
443 this.set({ |
444 from: this.revisions.at( toIndex - 1 ), |
444 from: this.revisions.at( toIndex - 1 ), |
445 to: this.revisions.at( toIndex ) |
445 to: this.revisions.at( toIndex ) |
446 }); |
446 }); |
447 } |
447 } |
448 }, |
448 }, |
449 |
449 |
450 updatedRevisions: function( from, to ) { |
450 updatedRevisions: function( from, to ) { |
451 if ( this.get( 'compareTwoMode' ) ) { |
451 if ( this.get( 'compareTwoMode' ) ) { |
452 // TODO: compare-two loading strategy |
452 // @todo Compare-two loading strategy. |
453 } else { |
453 } else { |
454 this.diffs.loadAll( this.revisions.pluck('id'), to.id, 40 ); |
454 this.diffs.loadAll( this.revisions.pluck('id'), to.id, 40 ); |
455 } |
455 } |
456 }, |
456 }, |
457 |
457 |
458 // Fetch the currently loaded diff. |
458 // Fetch the currently loaded diff. |
459 diff: function() { |
459 diff: function() { |
460 return this.diffs.get( this._diffId ); |
460 return this.diffs.get( this._diffId ); |
461 }, |
461 }, |
462 |
462 |
463 // So long as `from` and `to` are changed at the same time, the diff |
463 /* |
464 // will only be updated once. This is because Backbone updates all of |
464 * So long as `from` and `to` are changed at the same time, the diff |
465 // the changed attributes in `set`, and then fires the `change` events. |
465 * will only be updated once. This is because Backbone updates all of |
|
466 * the changed attributes in `set`, and then fires the `change` events. |
|
467 */ |
466 updateDiff: function( options ) { |
468 updateDiff: function( options ) { |
467 var from, to, diffId, diff; |
469 var from, to, diffId, diff; |
468 |
470 |
469 options = options || {}; |
471 options = options || {}; |
470 from = this.get('from'); |
472 from = this.get('from'); |
591 className: 'revisions-controls', |
593 className: 'revisions-controls', |
592 |
594 |
593 initialize: function() { |
595 initialize: function() { |
594 _.bindAll( this, 'setWidth' ); |
596 _.bindAll( this, 'setWidth' ); |
595 |
597 |
596 // Add the button view |
598 // Add the button view. |
597 this.views.add( new revisions.view.Buttons({ |
599 this.views.add( new revisions.view.Buttons({ |
598 model: this.model |
600 model: this.model |
599 }) ); |
601 }) ); |
600 |
602 |
601 // Add the checkbox view |
603 // Add the checkbox view. |
602 this.views.add( new revisions.view.Checkbox({ |
604 this.views.add( new revisions.view.Checkbox({ |
603 model: this.model |
605 model: this.model |
604 }) ); |
606 }) ); |
605 |
607 |
606 // Prep the slider model |
608 // Prep the slider model. |
607 var slider = new revisions.model.Slider({ |
609 var slider = new revisions.model.Slider({ |
608 frame: this.model, |
610 frame: this.model, |
609 revisions: this.model.revisions |
611 revisions: this.model.revisions |
610 }), |
612 }), |
611 |
613 |
612 // Prep the tooltip model |
614 // Prep the tooltip model. |
613 tooltip = new revisions.model.Tooltip({ |
615 tooltip = new revisions.model.Tooltip({ |
614 frame: this.model, |
616 frame: this.model, |
615 revisions: this.model.revisions, |
617 revisions: this.model.revisions, |
616 slider: slider |
618 slider: slider |
617 }); |
619 }); |
618 |
620 |
619 // Add the tooltip view |
621 // Add the tooltip view. |
620 this.views.add( new revisions.view.Tooltip({ |
622 this.views.add( new revisions.view.Tooltip({ |
621 model: tooltip |
623 model: tooltip |
622 }) ); |
624 }) ); |
623 |
625 |
624 // Add the tickmarks view |
626 // Add the tickmarks view. |
625 this.views.add( new revisions.view.Tickmarks({ |
627 this.views.add( new revisions.view.Tickmarks({ |
626 model: tooltip |
628 model: tooltip |
627 }) ); |
629 }) ); |
628 |
630 |
629 // Add the slider view |
631 // Add the slider view. |
630 this.views.add( new revisions.view.Slider({ |
632 this.views.add( new revisions.view.Slider({ |
631 model: slider |
633 model: slider |
632 }) ); |
634 }) ); |
633 |
635 |
634 // Add the Metabox view |
636 // Add the Metabox view. |
635 this.views.add( new revisions.view.Metabox({ |
637 this.views.add( new revisions.view.Metabox({ |
636 model: this.model |
638 model: this.model |
637 }) ); |
639 }) ); |
638 }, |
640 }, |
639 |
641 |
670 setWidth: function() { |
672 setWidth: function() { |
671 this.$el.css('width', this.$el.parent().width() + 'px'); |
673 this.$el.css('width', this.$el.parent().width() + 'px'); |
672 } |
674 } |
673 }); |
675 }); |
674 |
676 |
675 // The tickmarks view |
677 // The tickmarks view. |
676 revisions.view.Tickmarks = wp.Backbone.View.extend({ |
678 revisions.view.Tickmarks = wp.Backbone.View.extend({ |
677 className: 'revisions-tickmarks', |
679 className: 'revisions-tickmarks', |
678 direction: isRtl ? 'right' : 'left', |
680 direction: isRtl ? 'right' : 'left', |
679 |
681 |
680 initialize: function() { |
682 initialize: function() { |
684 reportTickPosition: function( model, revision ) { |
686 reportTickPosition: function( model, revision ) { |
685 var offset, thisOffset, parentOffset, tick, index = this.model.revisions.indexOf( revision ); |
687 var offset, thisOffset, parentOffset, tick, index = this.model.revisions.indexOf( revision ); |
686 thisOffset = this.$el.allOffsets(); |
688 thisOffset = this.$el.allOffsets(); |
687 parentOffset = this.$el.parent().allOffsets(); |
689 parentOffset = this.$el.parent().allOffsets(); |
688 if ( index === this.model.revisions.length - 1 ) { |
690 if ( index === this.model.revisions.length - 1 ) { |
689 // Last one |
691 // Last one. |
690 offset = { |
692 offset = { |
691 rightPlusWidth: thisOffset.left - parentOffset.left + 1, |
693 rightPlusWidth: thisOffset.left - parentOffset.left + 1, |
692 leftPlusWidth: thisOffset.right - parentOffset.right + 1 |
694 leftPlusWidth: thisOffset.right - parentOffset.right + 1 |
693 }; |
695 }; |
694 } else { |
696 } else { |
695 // Normal tick |
697 // Normal tick. |
696 tick = this.$('div:nth-of-type(' + (index + 1) + ')'); |
698 tick = this.$('div:nth-of-type(' + (index + 1) + ')'); |
697 offset = tick.allPositions(); |
699 offset = tick.allPositions(); |
698 _.extend( offset, { |
700 _.extend( offset, { |
699 left: offset.left + thisOffset.left - parentOffset.left, |
701 left: offset.left + thisOffset.left - parentOffset.left, |
700 right: offset.right + thisOffset.right - parentOffset.right |
702 right: offset.right + thisOffset.right - parentOffset.right |
717 this.$el.append( '<div style="' + this.direction + ': ' + ( 100 * tickWidth * index ) + '%"></div>' ); |
719 this.$el.append( '<div style="' + this.direction + ': ' + ( 100 * tickWidth * index ) + '%"></div>' ); |
718 }, this ); |
720 }, this ); |
719 } |
721 } |
720 }); |
722 }); |
721 |
723 |
722 // The metabox view |
724 // The metabox view. |
723 revisions.view.Metabox = wp.Backbone.View.extend({ |
725 revisions.view.Metabox = wp.Backbone.View.extend({ |
724 className: 'revisions-meta', |
726 className: 'revisions-meta', |
725 |
727 |
726 initialize: function() { |
728 initialize: function() { |
727 // Add the 'from' view |
729 // Add the 'from' view. |
728 this.views.add( new revisions.view.MetaFrom({ |
730 this.views.add( new revisions.view.MetaFrom({ |
729 model: this.model, |
731 model: this.model, |
730 className: 'diff-meta diff-meta-from' |
732 className: 'diff-meta diff-meta-from' |
731 }) ); |
733 }) ); |
732 |
734 |
733 // Add the 'to' view |
735 // Add the 'to' view. |
734 this.views.add( new revisions.view.MetaTo({ |
736 this.views.add( new revisions.view.MetaTo({ |
735 model: this.model |
737 model: this.model |
736 }) ); |
738 }) ); |
737 } |
739 } |
738 }); |
740 }); |
739 |
741 |
740 // The revision meta view (to be extended) |
742 // The revision meta view (to be extended). |
741 revisions.view.Meta = wp.Backbone.View.extend({ |
743 revisions.view.Meta = wp.Backbone.View.extend({ |
742 template: wp.template('revisions-meta'), |
744 template: wp.template('revisions-meta'), |
743 |
745 |
744 events: { |
746 events: { |
745 'click .restore-revision': 'restoreRevision' |
747 'click .restore-revision': 'restoreRevision' |
758 restoreRevision: function() { |
760 restoreRevision: function() { |
759 document.location = this.model.get('to').attributes.restoreUrl; |
761 document.location = this.model.get('to').attributes.restoreUrl; |
760 } |
762 } |
761 }); |
763 }); |
762 |
764 |
763 // The revision meta 'from' view |
765 // The revision meta 'from' view. |
764 revisions.view.MetaFrom = revisions.view.Meta.extend({ |
766 revisions.view.MetaFrom = revisions.view.Meta.extend({ |
765 className: 'diff-meta diff-meta-from', |
767 className: 'diff-meta diff-meta-from', |
766 type: 'from' |
768 type: 'from' |
767 }); |
769 }); |
768 |
770 |
769 // The revision meta 'to' view |
771 // The revision meta 'to' view. |
770 revisions.view.MetaTo = revisions.view.Meta.extend({ |
772 revisions.view.MetaTo = revisions.view.Meta.extend({ |
771 className: 'diff-meta diff-meta-to', |
773 className: 'diff-meta diff-meta-to', |
772 type: 'to' |
774 type: 'to' |
773 }); |
775 }); |
774 |
776 |
893 } |
895 } |
894 |
896 |
895 this.model.set( attributes ); |
897 this.model.set( attributes ); |
896 }, |
898 }, |
897 |
899 |
898 // Go to the 'next' revision |
900 // Go to the 'next' revision. |
899 nextRevision: function() { |
901 nextRevision: function() { |
900 var toIndex = this.model.revisions.indexOf( this.model.get('to') ) + 1; |
902 var toIndex = this.model.revisions.indexOf( this.model.get('to') ) + 1; |
901 this.gotoModel( toIndex ); |
903 this.gotoModel( toIndex ); |
902 }, |
904 }, |
903 |
905 |
904 // Go to the 'previous' revision |
906 // Go to the 'previous' revision. |
905 previousRevision: function() { |
907 previousRevision: function() { |
906 var toIndex = this.model.revisions.indexOf( this.model.get('to') ) - 1; |
908 var toIndex = this.model.revisions.indexOf( this.model.get('to') ) - 1; |
907 this.gotoModel( toIndex ); |
909 this.gotoModel( toIndex ); |
908 }, |
910 }, |
909 |
911 |
954 |
956 |
955 this.applySliderSettings(); |
957 this.applySliderSettings(); |
956 }, |
958 }, |
957 |
959 |
958 mouseMove: function( e ) { |
960 mouseMove: function( e ) { |
959 var zoneCount = this.model.revisions.length - 1, // One fewer zone than models |
961 var zoneCount = this.model.revisions.length - 1, // One fewer zone than models. |
960 sliderFrom = this.$el.allOffsets()[this.direction], // "From" edge of slider |
962 sliderFrom = this.$el.allOffsets()[this.direction], // "From" edge of slider. |
961 sliderWidth = this.$el.width(), // Width of slider |
963 sliderWidth = this.$el.width(), // Width of slider. |
962 tickWidth = sliderWidth / zoneCount, // Calculated width of zone |
964 tickWidth = sliderWidth / zoneCount, // Calculated width of zone. |
963 actualX = ( isRtl ? $(window).width() - e.pageX : e.pageX ) - sliderFrom, // Flipped for RTL - sliderFrom; |
965 actualX = ( isRtl ? $(window).width() - e.pageX : e.pageX ) - sliderFrom, // Flipped for RTL - sliderFrom. |
964 currentModelIndex = Math.floor( ( actualX + ( tickWidth / 2 ) ) / tickWidth ); // Calculate the model index |
966 currentModelIndex = Math.floor( ( actualX + ( tickWidth / 2 ) ) / tickWidth ); // Calculate the model index. |
965 |
967 |
966 // Ensure sane value for currentModelIndex. |
968 // Ensure sane value for currentModelIndex. |
967 if ( currentModelIndex < 0 ) { |
969 if ( currentModelIndex < 0 ) { |
968 currentModelIndex = 0; |
970 currentModelIndex = 0; |
969 } else if ( currentModelIndex >= this.model.revisions.length ) { |
971 } else if ( currentModelIndex >= this.model.revisions.length ) { |
970 currentModelIndex = this.model.revisions.length - 1; |
972 currentModelIndex = this.model.revisions.length - 1; |
971 } |
973 } |
972 |
974 |
973 // Update the tooltip mode |
975 // Update the tooltip mode. |
974 this.model.set({ hoveredRevision: this.model.revisions.at( currentModelIndex ) }); |
976 this.model.set({ hoveredRevision: this.model.revisions.at( currentModelIndex ) }); |
975 }, |
977 }, |
976 |
978 |
977 mouseLeave: function() { |
979 mouseLeave: function() { |
978 this.model.set({ hovering: false }); |
980 this.model.set({ hovering: false }); |
985 applySliderSettings: function() { |
987 applySliderSettings: function() { |
986 this.$el.slider( _.pick( this.model.toJSON(), 'value', 'values', 'range' ) ); |
988 this.$el.slider( _.pick( this.model.toJSON(), 'value', 'values', 'range' ) ); |
987 var handles = this.$('a.ui-slider-handle'); |
989 var handles = this.$('a.ui-slider-handle'); |
988 |
990 |
989 if ( this.model.get('compareTwoMode') ) { |
991 if ( this.model.get('compareTwoMode') ) { |
990 // in RTL mode the 'left handle' is the second in the slider, 'right' is first |
992 // In RTL mode the 'left handle' is the second in the slider, 'right' is first. |
991 handles.first() |
993 handles.first() |
992 .toggleClass( 'to-handle', !! isRtl ) |
994 .toggleClass( 'to-handle', !! isRtl ) |
993 .toggleClass( 'from-handle', ! isRtl ); |
995 .toggleClass( 'from-handle', ! isRtl ); |
994 handles.last() |
996 handles.last() |
995 .toggleClass( 'from-handle', !! isRtl ) |
997 .toggleClass( 'from-handle', !! isRtl ) |
1017 |
1019 |
1018 // In two handle mode, ensure handles can't be dragged past each other. |
1020 // In two handle mode, ensure handles can't be dragged past each other. |
1019 // Adjust left/right boundaries and reset points. |
1021 // Adjust left/right boundaries and reset points. |
1020 if ( view.model.get('compareTwoMode') ) { |
1022 if ( view.model.get('compareTwoMode') ) { |
1021 handles = handle.parent().find('.ui-slider-handle'); |
1023 handles = handle.parent().find('.ui-slider-handle'); |
1022 if ( handle.is( handles.first() ) ) { // We're the left handle |
1024 if ( handle.is( handles.first() ) ) { |
|
1025 // We're the left handle. |
1023 rightDragBoundary = handles.last().offset().left; |
1026 rightDragBoundary = handles.last().offset().left; |
1024 rightDragReset = rightDragBoundary - sliderOffset; |
1027 rightDragReset = rightDragBoundary - sliderOffset; |
1025 } else { // We're the right handle |
1028 } else { |
|
1029 // We're the right handle. |
1026 leftDragBoundary = handles.first().offset().left + handles.first().width(); |
1030 leftDragBoundary = handles.first().offset().left + handles.first().width(); |
1027 leftDragReset = leftDragBoundary - sliderOffset; |
1031 leftDragReset = leftDragBoundary - sliderOffset; |
1028 } |
1032 } |
1029 } |
1033 } |
1030 |
1034 |
1041 |
1045 |
1042 getPosition: function( position ) { |
1046 getPosition: function( position ) { |
1043 return isRtl ? this.model.revisions.length - position - 1: position; |
1047 return isRtl ? this.model.revisions.length - position - 1: position; |
1044 }, |
1048 }, |
1045 |
1049 |
1046 // Responds to slide events |
1050 // Responds to slide events. |
1047 slide: function( event, ui ) { |
1051 slide: function( event, ui ) { |
1048 var attributes, movedRevision; |
1052 var attributes, movedRevision; |
1049 // Compare two revisions mode |
1053 // Compare two revisions mode. |
1050 if ( this.model.get('compareTwoMode') ) { |
1054 if ( this.model.get('compareTwoMode') ) { |
1051 // Prevent sliders from occupying same spot |
1055 // Prevent sliders from occupying same spot. |
1052 if ( ui.values[1] === ui.values[0] ) { |
1056 if ( ui.values[1] === ui.values[0] ) { |
1053 return false; |
1057 return false; |
1054 } |
1058 } |
1055 if ( isRtl ) { |
1059 if ( isRtl ) { |
1056 ui.values.reverse(); |
1060 ui.values.reverse(); |
1070 attributes.from = undefined; |
1074 attributes.from = undefined; |
1071 } |
1075 } |
1072 } |
1076 } |
1073 movedRevision = this.model.revisions.at( this.getPosition( ui.value ) ); |
1077 movedRevision = this.model.revisions.at( this.getPosition( ui.value ) ); |
1074 |
1078 |
1075 // If we are scrubbing, a scrub to a revision is considered a hover |
1079 // If we are scrubbing, a scrub to a revision is considered a hover. |
1076 if ( this.model.get('scrubbing') ) { |
1080 if ( this.model.get('scrubbing') ) { |
1077 attributes.hoveredRevision = movedRevision; |
1081 attributes.hoveredRevision = movedRevision; |
1078 } |
1082 } |
1079 |
1083 |
1080 this.model.set( attributes ); |
1084 this.model.set( attributes ); |
1081 }, |
1085 }, |
1082 |
1086 |
1083 stop: function() { |
1087 stop: function() { |
1084 $( window ).off('mousemove.wp.revisions'); |
1088 $( window ).off('mousemove.wp.revisions'); |
1085 this.model.updateSliderSettings(); // To snap us back to a tick mark |
1089 this.model.updateSliderSettings(); // To snap us back to a tick mark. |
1086 this.model.set({ scrubbing: false }); |
1090 this.model.set({ scrubbing: false }); |
1087 } |
1091 } |
1088 }); |
1092 }); |
1089 |
1093 |
1090 // The diff view. |
1094 // The diff view. |
1103 // Maintains the URL routes so browser URL matches state. |
1107 // Maintains the URL routes so browser URL matches state. |
1104 revisions.Router = Backbone.Router.extend({ |
1108 revisions.Router = Backbone.Router.extend({ |
1105 initialize: function( options ) { |
1109 initialize: function( options ) { |
1106 this.model = options.model; |
1110 this.model = options.model; |
1107 |
1111 |
1108 // Maintain state and history when navigating |
1112 // Maintain state and history when navigating. |
1109 this.listenTo( this.model, 'update:diff', _.debounce( this.updateUrl, 250 ) ); |
1113 this.listenTo( this.model, 'update:diff', _.debounce( this.updateUrl, 250 ) ); |
1110 this.listenTo( this.model, 'change:compareTwoMode', this.updateUrl ); |
1114 this.listenTo( this.model, 'change:compareTwoMode', this.updateUrl ); |
1111 }, |
1115 }, |
1112 |
1116 |
1113 baseUrl: function( url ) { |
1117 baseUrl: function( url ) { |