Make WordPress Core

Ticket #24425: 24425.draft.18.diff

File 24425.draft.18.diff, 16.6 KB (added by adamsilverstein, 12 years ago)

adds compare two mode, history tracking

  • wp-admin/js/revisions.js

     
    33(function($) {
    44        var revisions;
    55
    6         revisions = wp.revisions = { model: {}, view: {}, controller: {} };
     6        revisions = wp.revisions = { model: {}, view: {}, controller: {}, router: {} };
    77
    88        // Link settings.
    99        revisions.settings = typeof _wpRevisionsSettings === 'undefined' ? {} : _wpRevisionsSettings;
     
    101101                        return this.fetch({ data: { compare: comparisons }, remove: false });
    102102                },
    103103
    104 /**/
    105104                loadLast: function( num ) {
    106105                        num     = num || 1;
    107106                        var ids = this.getProximalDiffIds();
     
    194193                initialize: function( attributes, options ) {
    195194                        this.revisions = options.revisions;
    196195                        this.diffs     = new revisions.model.Diffs( [], {revisions: this.revisions} );
     196                        this.listenTo( this, 'change:from', this.updateDiffFrom );
     197                        this.listenTo( this, 'change:to', this.updateDiffTo );
     198                        this.revisionsRouter = new revisions.router.Router();
     199                        this.revisionsRouter.model = this;
    197200
    198                         this.listenTo( this, 'change:from change:to', this.updateDiffId );
    199201                },
    200202
    201                 updateDiffId: function() {
     203                updateDiffTo: function() {
    202204                        var from = this.get( 'from' );
    203205                        var to   = this.get( 'to' );
    204                         this.set( 'diffId', (from ? from.id : '0') + ':' + to.id );
     206                        this.set( 'diffId', (from ? from.id : '0' ) + ':' + to.id );
     207                },
     208
     209                updateDiffFrom: function() {
     210                        if ( true === this.get( 'compareTwoMode' ) ) {
     211                                this.updateDiffTo();
     212                        }
    205213                }
     214
    206215        });
    207216
    208217
     
    243252                                        this.model.diffs.loadAllBy( 50 );
    244253                                }, this ) );
    245254                        }
     255                        Backbone.history.start();
     256
    246257                },
    247258
    248259                render: function() {
     
    266277        });
    267278
    268279        // The control view.
    269         // This contains the revision slider, previous/next buttons, and the compare checkbox.
     280        // This contains the revision slider, previous/next buttons, the meta info and the compare checkbox.
    270281        revisions.view.Controls = wp.Backbone.View.extend({
    271282                tagName: 'div',
    272283                className: 'revisions-controls',
    273284
    274285                initialize: function() {
    275286                        // Add the button view
    276                         this.views.add( new revisions.view.Buttons({ 
     287                        this.views.add( new revisions.view.Buttons({
    277288                                model: this.model
    278289                        }));
    279290
     
    282293                                model: this.model
    283294                        }) );
    284295
     296
    285297                        // Add the Meta view
    286298                        this.views.add( new revisions.view.Meta({
    287299                                model: this.model
    288300                        }) );
     301
     302
     303
    289304                }
    290305        });
    291306
     
    319334                }
    320335        });
    321336
    322 
    323337        // The buttons view.
    324338        // Encapsulates all of the configuration for the previous/next buttons, and the compare checkbox.
    325339        revisions.view.Buttons = wp.Backbone.View.extend({
     
    329343
    330344                initialize: function() {
    331345                        this.$el.html( this.template() )
     346                        this.listenTo( this.model, 'change:compareTwoMode', this.updateCompareTwoMode );
     347
    332348                },
    333349
    334350                events: {
    335351                        'click #next': 'nextRevision',
    336                         'click #previous': 'previousRevision'
     352                        'click #previous': 'previousRevision',
     353                        'click .compare-two-revisions': 'compareTwoToggle'
    337354                },
    338                
     355
     356                updateCompareTwoMode: function(){
     357                        if ( true === this.model.get( 'compareTwoMode' ) ){
     358                                $( '#revision-diff-container' ).addClass( 'comparing-two-revisions' );
     359                                // in RTL mode the 'left handle' is the second in the slider, 'right' is first
     360                                $( '.wp-slider a.ui-slider-handle' ).first().addClass( isRtl ? 'right-handle' : 'left-handle' );
     361                                $( '.wp-slider a.ui-slider-handle' ).last().addClass( isRtl ? 'left-handle' : 'right-handle' );
     362                        } else {
     363                                $( '#revision-diff-container' ).removeClass( 'comparing-two-revisions' );
     364                                $( '.wp-slider a.ui-slider-handle' ).removeClass( 'left-handle' ).removeClass( 'right-handle' );
     365                        }
     366
     367                },
     368
     369                // Toggle the compare two mode feature when the compare two checkbox is checked.
     370                compareTwoToggle: function( event ){
     371                        // Activate compare two mode?
     372                        if ( $( '.compare-two-revisions' ).is( ':checked' ) ) {
     373                                this.model.set( { compareTwoMode: true } );
     374                        } else {
     375                                this.model.set( { compareTwoMode: false } );
     376                        }
     377                        this.updateCompareTwoMode();
     378                        this.model.revisionsRouter.navigateRoute( this.model.get( 'to').id, this.model.get( 'from' ).id );
     379
     380                },
     381
     382                // Go to a specific modelindex, taking into account Rtl mode.
    339383                gotoModel: function( toIndex ) {
    340384                        var attributes = {
    341                                 to: this.model.revisions.at( isRtl ? this.model.revisions.length - toIndex - 1 : toIndex ) // Reverse directions for Rtl
     385                                to: this.model.revisions.at( isRtl ? this.model.revisions.length - toIndex - 1 : toIndex ) // Reverse directions for Rtl.
    342386                        };
    343387                        // If we're at the first revision, unset 'from'.
    344388                        if ( isRtl ? this.model.revisions.length - toIndex - 1 : toIndex ) // Reverse directions for Rtl
     
    347391                                this.model.unset('from', { silent: true });
    348392
    349393                        this.model.set( attributes );
     394
     395                        this.model.revisionsRouter.navigateRoute( attributes.to.id, attributes.from ? attributes.from.id : 0 );
     396
    350397                },
    351398
     399                // Go to the 'next' revision, direction takes into account Rtl mode.
    352400                nextRevision: function() {
    353                         var toIndex = this.model.revisions.indexOf( this.model.get( 'to' ) );
     401                        var toIndex = isRtl ? this.model.revisions.length - this.model.revisions.indexOf( this.model.get( 'to' ) ) - 1 : this.model.revisions.indexOf( this.model.get( 'to' ) );
    354402                        toIndex     = isRtl ? toIndex - 1 : toIndex + 1;
    355403                        this.gotoModel( toIndex );
    356404                },
    357                
     405
     406                // Go to the 'previous' revision, direction takes into account Rtl mode.
    358407                previousRevision: function() {
    359                         var toIndex = this.model.revisions.indexOf( this.model.get('to') );
     408                        var toIndex = isRtl ? this.model.revisions.length - this.model.revisions.indexOf( this.model.get( 'to' ) ) - 1 : this.model.revisions.indexOf( this.model.get( 'to' ) );
    360409                        toIndex     = isRtl ? toIndex + 1 : toIndex - 1;
    361410                        this.gotoModel( toIndex );
    362411                },
    363412
    364413                ready: function() {
    365414                        this.listenTo( this.model, 'change:diffId', this.disabledButtonCheck );
     415
     416                        // Hide compare two mode toggle when fewer than three revisions.
     417                        if ( this.model.revisions.length < 3 ) {
     418                                $( '.revision-toggle-compare-mode' ).hide();
     419                        }
    366420                },
    367421
    368                 // Check to see if the Previous or Next buttons need to be disabled or enabled
     422                // Check to see if the Previous or Next buttons need to be disabled or enabled.
    369423                disabledButtonCheck: function() {
    370                         var maxVal   = isRtl ? 0 : this.model.revisions.length - 1,
    371                                 minVal   = isRtl ? this.model.revisions.length - 1 : 0,
    372                                 next     = $( '.revisions-next .button' ),
     424                        var maxVal = this.model.revisions.length - 1,
     425                                minVal = 0,
     426                                next = $( '.revisions-next .button' ),
    373427                                previous = $( '.revisions-previous .button' ),
    374                                 val      = this.model.revisions.indexOf( this.model.get( 'to' ) );
     428                                val = this.model.revisions.indexOf( this.model.get( 'to' ) );
    375429
    376                         // Disable "Next" button if you're on the last node
     430                        // Disable "Next" button if you're on the last node.
    377431                        if ( maxVal === val )
    378432                                next.prop( 'disabled', true );
    379433                        else
    380434                                next.prop( 'disabled', false );
    381435
    382                         // Disable "Previous" button if you're on the first node
     436                        // Disable "Previous" button if you're on the first node.
    383437                        if ( minVal === val )
    384438                                previous.prop( 'disabled', true );
    385439                        else
     
    404458
    405459                        // Find the initially selected revision
    406460                        var initiallySelectedRevisionIndex =
    407                                 this.model.revisions.indexOf( 
     461                                this.model.revisions.indexOf(
    408462                                        this.model.revisions.findWhere(  { id: Number( revisions.settings.selectedRevision ) } ) );
    409463
    410464                        this.settings = new revisions.model.Slider({
     
    418472
    419473                ready: function() {
    420474                        this.$el.slider( this.settings.toJSON() );
     475
     476                        // Listen for changes in Compare Two Mode setting
     477                        this.listenTo( this.model, 'change:compareTwoMode', this.updateSliderSettings );
     478
    421479                        this.settings.on( 'change', function( model, options ) {
    422                                 // Apply changes to slider settings here.
    423                                 this.$el.slider( { value: this.model.revisions.indexOf( this.model.get( 'to' ) ) } ); // Set handle to current to model
     480                                this.updateSliderSettings();
    424481                        }, this );
    425                         // Reset to the initially selected revision
    426                         this.slide( '', this.settings.attributes );
    427482
    428483                        // Listen for changes in the diffId
    429484                        this.listenTo( this.model, 'change:diffId', this.diffIdChanged );
    430485
     486                        // Reset to the initially selected revision
     487                        this.slide( '', this.settings.attributes );
     488
     489                        if ( true === this.model.get( 'compareTwoMode' ) )
     490                                $( '.compare-two-revisions' ).trigger( 'click' );
    431491                },
    432492
     493                updateSliderSettings: function() {
     494                        if ( isRtl ) {
     495                                this.$el.slider( { // Order reversed in Rtl mode
     496                                        value: this.model.revisions.length - this.model.revisions.indexOf( this.model.get( 'to' ) ) - 1
     497                                } );
     498                        } else {
     499                                if ( true === this.model.get( 'compareTwoMode' ) ) {
     500                                        this.$el.slider( { // Set handles to current from/to models
     501                                                values: [
     502                                                        this.model.revisions.indexOf( this.model.get( 'from' ) ),
     503                                                        this.model.revisions.indexOf( this.model.get( 'to' ) )
     504                                                                ],
     505                                                value: null,
     506                                                range: true // Range mode ensures handles can't cross
     507                                        } );
     508                                } else {
     509                                        this.$el.slider( { // Set handle to current to model
     510                                                value: this.model.revisions.indexOf( this.model.get( 'to' ) ),
     511                                                values: null, // Clear existing two handled values
     512                                                range: false
     513                                        } );
     514                                }
     515                        }
     516                        if ( true === this.model.get( 'compareTwoMode' ) ){
     517                                $( '.revisions' ).addClass( 'comparing-two-revisions' );
     518                                // in RTL mode the 'left handle' is the second in the slider, 'right' is first
     519
     520                                $( '.wp-slider a.ui-slider-handle' )
     521                                        .first()
     522                                        .addClass( isRtl ? 'right-handle' : 'left-handle' )
     523                                        .removeClass( isRtl ? 'left-handle' : 'right-handle' );
     524                                $( '.wp-slider a.ui-slider-handle' )
     525                                        .last()
     526                                        .addClass( isRtl ? 'left-handle' : 'right-handle' )
     527                                        .removeClass( isRtl ? 'right-handle' : 'left-handle' );
     528                        } else {
     529                                $( '.revisions' ).removeClass( 'comparing-two-revisions' );
     530                        }
     531                },
     532
    433533                diffIdChanged: function() {
    434534                        // Reset the view settings when diffId is changed
    435                         this.settings.set( { 'value': this.model.revisions.indexOf( this.model.get( 'to' ) ) } );
     535                        if ( true === this.model.get( 'compareTwoMode' ) ) {
     536                                this.settings.set( { 'values': [
     537                                                this.model.revisions.indexOf( this.model.get( 'from' ) ),
     538                                                this.model.revisions.indexOf( this.model.get( 'to' ) )
     539                                        ] } );
     540                        } else {
     541                                this.settings.set( { 'value': this.model.revisions.indexOf( this.model.get( 'to' ) ) } );
     542                        }
    436543                },
    437544
    438545                start: function( event, ui ) {
    439                         // Track the mouse position to enable smooth dragging, overrides default jquery ui step behaviour
    440                         $( window ).mousemove( function( e ) {
    441                                 var sliderLeft  = $( '.wp-slider' ).offset().left,
    442                                         sliderRight = sliderLeft + $( '.wp-slider' ).width();
     546                        if ( true !== this.model.get( 'compareTwoMode' )) {
     547                                // Track the mouse position to enable smooth dragging, overrides default jquery ui step behaviour .
     548                                $( window ).mousemove( function( e ) {
     549                                        var sliderLeft = $( '.wp-slider' ).offset().left,
     550                                                sliderRight = sliderLeft + $( '.wp-slider' ).width();
    443551
    444                                 // Follow mouse movements, as long as handle remains inside slider
    445                                 if ( e.clientX < sliderLeft ) {
    446                                         $( ui.handle ).css( 'left', 0 ); // Mouse to left of slider
    447                                 } else if ( e.clientX > sliderRight ) {
    448                                         $( ui.handle ).css( 'left', sliderRight - sliderLeft); // Mouse to right of slider
    449                                 } else {
    450                                         $( ui.handle ).css( 'left', e.clientX - sliderLeft ); // Mouse in slider
    451                                 }
    452                         } ); // End mousemove
     552                                        // Follow mouse movements, as long as handle remains inside slider.
     553                                        if ( e.clientX < sliderLeft ) {
     554                                                $( ui.handle ).css( 'left', 0 ); // Mouse to left of slider.
     555                                        } else if ( e.clientX > sliderRight ) {
     556                                                $( ui.handle ).css( 'left', sliderRight - sliderLeft); // Mouse to right of slider.
     557                                        } else {
     558                                                $( ui.handle ).css( 'left', e.clientX - sliderLeft ); // Mouse in slider.
     559                                        }
     560                                } ); // End mousemove.
     561                        }
    453562                },
    454563
     564                getSliderPosition: function( ui ){
     565                        return isRtl ? this.model.revisions.length - ui.value - 1 : ui.value;
     566                },
     567
    455568                slide: function( event, ui ) {
    456                         var attributes = {
    457                                 to: this.model.revisions.at( isRtl ? this.model.revisions.length - ui.value - 1 : ui.value ) // Reverse directions for Rtl
    458                         };
     569                        var attributes;
     570                        // Compare two revisions mode
     571                        if ( 'undefined' !== typeof ui.values && true == this.model.get( 'compareTwoMode' )) {
     572                                // Prevent sliders from occupying same spot
     573                                if ( ui.values[1] === ui.values[0] )
     574                                        return false;
     575                                attributes = {
     576                                        to: this.model.revisions.at( isRtl ? this.model.revisions.length - ui.values[1] - 1 : ui.values[1] ), // Reverse directions for Rtl.
     577                                        from: this.model.revisions.at( isRtl ? this.model.revisions.length - ui.values[0] - 1 : ui.values[0] ) // Reverse directions for Rtl.
     578                                };
    459579
    460                         // If we're at the first revision, unset 'from'.
    461                         if ( isRtl ? this.model.revisions.length - ui.value - 1 : ui.value ) // Reverse directions for Rtl
    462                                 attributes.from = this.model.revisions.at( isRtl ? this.model.revisions.length - ui.value - 2 : ui.value - 1 );
    463                         else
    464                                 this.model.unset('from', { silent: true });
     580                        } else {
     581                                // Compare single revision mode
     582                                var sliderPosition = this.getSliderPosition( ui );
     583                                attributes = {
     584                                        to: this.model.revisions.at( sliderPosition )
     585                                };
    465586
     587                                // If we're at the first revision, unset 'from'.
     588                                if ( sliderPosition ) // Reverse directions for Rtl.
     589                                        attributes.from = this.model.revisions.at( sliderPosition - 1  );
     590                                else
     591                                        this.model.unset('from', { silent: true });
     592
     593                        }
    466594                        this.model.set( attributes );
     595
     596                        // Maintain state history when dragging
     597                        this.model.revisionsRouter.navigateRoute( attributes.to.id, ( attributes.from ? attributes.from.id : 0 ) );
     598
    467599                },
    468600
    469601                stop: function( event, ui ) {
    470                         $( window ).unbind( 'mousemove' ); // Stop tracking the mouse
    471                         // Reset settings pops handle back to the step position
    472                         this.settings.trigger( 'change' );
     602                        if ( true !== this.model.get( 'compareTwoMode' )) {
     603                                $( window ).unbind( 'mousemove' ); // Stop tracking the mouse.
     604                                // Reset settings pops handle back to the step position.
     605                                this.settings.trigger( 'change' );
     606                        }
    473607                }
    474608        });
    475609
     
    486620                }
    487621        });
    488622
     623        // The revisions router
     624        // takes URLs with #hash fragments and routes them
     625        revisions.router.Router = Backbone.Router.extend({
     626                model: null,
     627
     628                routes: {
     629                        'revision/from/:from/to/:to/handles/:handles': 'gotoRevisionId'
     630                },
     631
     632                navigateRoute: function( to, from ) {
     633                        var navigateTo = '/revision/from/' + from + '/to/' + to + '/handles/';
     634                        if ( true === this.model.get( 'compareTwoMode' ) ){
     635                                navigateTo = navigateTo + '2';
     636                        } else {
     637                                navigateTo = navigateTo + '1';
     638                        }
     639                        this.navigate( navigateTo );
     640                },
     641
     642                gotoRevisionId: function( to, from, handles ) {
     643                        if ( '2' === handles ) {
     644                                this.model.set( { compareTwoMode: true } );
     645                                $( '.compare-two-revisions' ).attr( 'checked', true );
     646                        } else {
     647                                this.model.set( { compareTwoMode: false } );
     648                                $( '.compare-two-revisions' ).attr( 'checked', false );
     649                        }
     650                        if ('undefined' !== typeof this.model ) {
     651                                var selectedToRevision =
     652                                        this.model.revisions.findWhere( { 'id': Number( to ) } ),
     653                                        selectedFromRevision =
     654                                        this.model.revisions.findWhere( { 'id': Number( from ) } );
     655
     656                                this.model.set( {
     657                                        to:   selectedToRevision,
     658                                        from: selectedFromRevision } );
     659
     660                        }
     661
     662                },
     663
     664        });
     665
    489666        // Initialize the revisions UI.
    490667        revisions.init = function() {
    491668                revisions.view.frame = new revisions.view.Frame({
    492669                        collection: new revisions.model.Revisions( revisions.settings.revisionData )
    493670                }).render();
     671                // Only allow compare two mode if three or more revisions
    494672        };
    495673
    496674        $( revisions.init );
  • wp-admin/css/wp-admin-rtl.css

     
    975975        left: 6px;
    976976}
    977977
    978 #toggle-revision-compare-mode {
     978.revision-toggle-compare-mode {
    979979        right: auto;
    980980        left: 0;
    981981        padding: 9px 0 0 9px;
    982982}
    983983
    984 #diff-next-revision {
     984.revisions-next {
    985985        float: left;
    986986}
    987987
    988 #diff-previous-revision {
     988.revisions-previous {
    989989        float: right;
    990990}
    991991
  • wp-admin/css/wp-admin.css

     
    35023502        margin-bottom: 10px;
    35033503}
    35043504
     3505.comparing-two-revisions .revisions-controls {
     3506        height: 90px;
     3507}
     3508
    35053509.revisions-meta {
    35063510        margin-top: 15px;
    35073511}
     
    36553659        margin-top: 20px;
    36563660}
    36573661
    3658 .comparing-two-revisions #diff-previous-revision,
    3659 .comparing-two-revisions #diff-next-revision,
     3662#diff-previous-revision,
     3663#diff-next-revision,
    36603664#diff-header-from {
    36613665        display: none;
    36623666}