From 65b08eeecef836fa895111861bc0b65743a4b5bf Mon Sep 17 00:00:00 2001 From: Mark Jaquith Date: Fri, 19 Jul 2013 13:49:15 +0000 Subject: [PATCH] Revisions: Re-work how tick marks and tooltips are aligned. IE fixes. * Pixel-based alignment of tooltips. * Bottom-based alignment, so arrow lines up using CSS only. * Better RTL styles (mostly mirror-imaged). * Better RTL calculations in `revisions.js` (less logic). * Better IE support. See #24736. Props markjaquith, adamsilverstein, ocean90. git-svn-id: http://core.svn.wordpress.org/trunk@24751 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/css/colors-classic.css | 2 +- wp-admin/css/colors-fresh.css | 7 +- wp-admin/css/ie.css | 13 ++ wp-admin/css/wp-admin-rtl.css | 25 +++- wp-admin/css/wp-admin.css | 51 +++++--- wp-admin/js/revisions.js | 224 ++++++++++++++++++-------------- wp-admin/revision.php | 2 +- 7 files changed, 190 insertions(+), 134 deletions(-) diff --git a/wp-admin/css/colors-classic.css b/wp-admin/css/colors-classic.css index d7ee872f8e..703beef660 100644 --- a/wp-admin/css/colors-classic.css +++ b/wp-admin/css/colors-classic.css @@ -1486,7 +1486,7 @@ table.diff .diff-addedline ins { } .revisions-tooltip, -.revisions-tooltip-arrow:after { +.revisions-tooltip-arrow span { border-color: #d1e5ee; background-color: #fff; } diff --git a/wp-admin/css/colors-fresh.css b/wp-admin/css/colors-fresh.css index b3d0968a8e..a5733d4189 100644 --- a/wp-admin/css/colors-fresh.css +++ b/wp-admin/css/colors-fresh.css @@ -1380,16 +1380,11 @@ table.diff .diff-addedline ins { } .revisions-tooltip, -.revisions-tooltip-arrow:after { +.revisions-tooltip-arrow span { border-color: #d7d7d7; background-color: #fff; } - -.revisions-tickmarks { - background-color: #f7f7f7; -} - .revisions-tickmarks > div { border-color: #aaa; } diff --git a/wp-admin/css/ie.css b/wp-admin/css/ie.css index fe6f93933e..4113318407 100644 --- a/wp-admin/css/ie.css +++ b/wp-admin/css/ie.css @@ -630,3 +630,16 @@ table.ie-fixed { * html #adminmenu div.wp-menu-image { height: 29px; } + +/* Revisions */ +.revisions-tooltip-arrow span { + left: 25px; + top: -24px; + filter: progid:DXImageTransform.Microsoft.Matrix(SizingMethod='auto expand', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476); /* IE7 */ +} + +.revisions-controls .revisions-tickmarks > div { + margin-right: -1px; + border-width: 0 0 0 0; + border-style: none; +} \ No newline at end of file diff --git a/wp-admin/css/wp-admin-rtl.css b/wp-admin/css/wp-admin-rtl.css index d21ecab0d7..2c2272aaad 100644 --- a/wp-admin/css/wp-admin-rtl.css +++ b/wp-admin/css/wp-admin-rtl.css @@ -954,18 +954,18 @@ th.sorted a span { /*------------------------------------------------------------------------------ 11.2 - Post Revisions ------------------------------------------------------------------------------*/ -.wp-slider .ui-slider-handle.left-handle:before, -.wp-slider .ui-slider-handle.right-handle:before { +.wp-slider .ui-slider-handle.from-handle:before, +.wp-slider .ui-slider-handle.to-handle:before { height: 8px; width: 7px; } -.wp-slider .ui-slider-handle.left-handle:before { +.wp-slider .ui-slider-handle.from-handle:before { background-position: -5px -10px; left: 6px; } -.wp-slider .ui-slider-handle.right-handle:before { +.wp-slider .ui-slider-handle.to-handle:before { background-position: -4px -29px; left: 6px; } @@ -1025,8 +1025,8 @@ th.sorted a span { } .revisions-tooltip { - margin-right: -73px; margin-left: 0; + margin-right: -70px; -webkit-transition: right 15ms; -moz-transition: right 15ms; -ms-transition: right 15ms; @@ -1034,17 +1034,30 @@ th.sorted a span { transition: right 15ms; } +.ie8 .revisions-tooltip { + margin-right: -75px; +} + .revisions-tooltip-arrow { right: 0; margin-left: 0; margin-right: 35px; } -.revisions-tooltip-image { +.revisions-tooltip-arrow > span { + right: 20px; +} + +.revisions-tooltip img { float: right; margin: 2px 0 0 5px; } +.revisions-tickmarks > div { + float: right; + border-width: 0 0 0 1px; +} + /*------------------------------------------------------------------------------ 11.3 - Featured Images ------------------------------------------------------------------------------*/ diff --git a/wp-admin/css/wp-admin.css b/wp-admin/css/wp-admin.css index fd829950e5..fb8f49975d 100644 --- a/wp-admin/css/wp-admin.css +++ b/wp-admin/css/wp-admin.css @@ -3511,10 +3511,6 @@ td.plugin-title p { /*------------------------------------------------------------------------------ 11.2 - Post Revisions ------------------------------------------------------------------------------*/ -body.revision-php { - overflow-y: scroll; /* Force a scrollbar, so centering doesn't jump */ -} - .revisions-control-frame, .revisions-diff-frame { position: relative; @@ -3533,14 +3529,14 @@ body.revision-php { .revisions-tickmarks { position: relative; - margin: 0 auto 0; + margin: 0 auto; height: 0.8em; z-index: 2; top: 7px; - width: 70%; + width: 100%; + padding: 0 15%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; - -ms-box-sizing: border-box; /* ie8 only */ box-sizing: border-box; } @@ -3548,17 +3544,16 @@ body.revision-php { position: relative; height: 100%; float: left; - z-index: 10002; + z-index: 3; border-style: solid; - border-width: 0 0 0 1px; + border-width: 0 1px 0 0; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; - -ms-box-sizing: border-box; /* ie8 only */ box-sizing: border-box; } -.revisions-tickmarks > div:first-of-type { - border-left-width: 0; +.revisions-tickmarks > div:last-child { + border-width: 0; } .comparing-two-revisions .revisions-controls { @@ -3588,6 +3583,7 @@ body.revision-php { -ms-transition: opacity 0.5s; -o-transition: opacity 0.5s; transition: opacity 0.5s; + filter: alpha(opacity=0); /* ie8 and earlier */ } .revisions .loading-indicator span.spinner { @@ -3598,6 +3594,7 @@ body.revision-php { .revisions.loading .loading-indicator { opacity: 1; + filter: alpha(opacity=100); /* ie8 and earlier */ } .revisions .diff { @@ -3610,6 +3607,7 @@ body.revision-php { .revisions.loading .diff { opacity: 0.5; + filter: alpha(opacity=50); /* ie8 and earlier */ } .revisions.diff-error .diff { @@ -3631,6 +3629,12 @@ body.revision-php { display: none; } +.revisions-previous, +.revisions-next { + position: relative; + z-index: 4; +} + .revisions-previous { float: left; } @@ -3641,7 +3645,7 @@ body.revision-php { .wp-slider { width: 70%; - margin: 0 auto 0; + margin: 0 auto; top: -3px; } @@ -3736,7 +3740,8 @@ table.diff .diff-addedline ins { .revisions-tooltip { position: absolute; bottom: 105px; - margin-left: -69px; + margin-right: 0; + margin-left: -70px; line-height: 28px; z-index: 9999; max-width: 350px; @@ -3773,7 +3778,7 @@ table.diff .diff-addedline ins { z-index: 10000; } -.revisions-tooltip-arrow:after { +.revisions-tooltip-arrow > span { content: ""; position: absolute; left: 20px; @@ -3787,8 +3792,14 @@ table.diff .diff-addedline ins { tranform: rotate(45deg); } +.ie8 .revisions-tooltip-arrow > span { + left: 14px; + top: -25px; + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(SizingMethod='auto expand', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476)"; /* IE8 */ +} + .revisions-tooltip, -.revisions-tooltip-arrow:after { +.revisions-tooltip-arrow > span { border-width: 1px; border-style: solid; } @@ -3828,18 +3839,18 @@ div.revisions-controls > .wp-slider > .ui-slider-handle { background: url(../images/arrows-pr.png) no-repeat -2px -47px; } -.wp-slider .ui-slider-handle.left-handle:before, -.wp-slider .ui-slider-handle.right-handle:before { +.wp-slider .ui-slider-handle.from-handle:before, +.wp-slider .ui-slider-handle.to-handle:before { height: 8px; width: 7px; } -.wp-slider .ui-slider-handle.left-handle:before { +.wp-slider .ui-slider-handle.from-handle:before { background-position: -5px -84px; left: 7px; } -.wp-slider .ui-slider-handle.right-handle:before { +.wp-slider .ui-slider-handle.to-handle:before { background-position: -4px -65px; left: 5px; } diff --git a/wp-admin/js/revisions.js b/wp-admin/js/revisions.js index d72186fd31..d122f535b7 100644 --- a/wp-admin/js/revisions.js +++ b/wp-admin/js/revisions.js @@ -16,6 +16,23 @@ window.wp = window.wp || {}; console.log.apply( console, arguments ); }; + // Handy functions to help with positioning + $.fn.allOffsets = function() { + var offset = this.offset() || {top: 0, left: 0}, win = $(window); + return _.extend( offset, { + right: win.width() - offset.left - this.outerWidth(), + bottom: win.height() - offset.top - this.outerHeight() + }); + }; + + $.fn.allPositions = function() { + var position = this.position() || {top: 0, left: 0}, parent = this.parent(); + return _.extend( position, { + right: parent.outerWidth() - position.left - this.outerWidth(), + bottom: parent.outerHeight() - position.top - this.outerHeight() + }); + }; + // wp_localize_script transforms top-level numbers into strings. Undo that. if ( revisions.settings.to ) revisions.settings.to = parseInt( revisions.settings.to, 10 ); @@ -125,19 +142,24 @@ window.wp = window.wp || {}; revisions.model.Tooltip = Backbone.Model.extend({ defaults: { revision: null, + offset: {}, hovering: false, // Whether the mouse is hovering scrubbing: false // Whether the mouse is scrubbing }, initialize: function( options ) { + this.frame = options.frame; this.revisions = options.revisions; this.slider = options.slider; this.listenTo( this.slider, 'hovered:revision', this.updateRevision ); this.listenTo( this.slider, 'change:hovering', this.setHovering ); this.listenTo( this.slider, 'change:scrubbing', this.setScrubbing ); + + this.set({ revision: this.frame.diff() }); }, + updateRevision: function( revision ) { this.set({ revision: revision }); }, @@ -475,6 +497,7 @@ window.wp = window.wp || {}; render: function() { wp.Backbone.View.prototype.render.apply( this, arguments ); + $('html').css( 'overflow-y', 'scroll' ); $('#wpbody-content .wrap').append( this.el ); this.updateCompareTwoMode(); this.renderDiff( this.model.diff() ); @@ -524,17 +547,21 @@ window.wp = window.wp || {}; revisions: this.model.revisions }); + // Prep the tooltip model + var tooltip = new revisions.model.Tooltip({ + frame: this.model, + revisions: this.model.revisions, + slider: slider + }); + // Add the tooltip view this.views.add( new revisions.view.Tooltip({ - model: new revisions.model.Tooltip({ - revisions: this.model.revisions, - slider: slider - }) + model: tooltip }) ); // Add the tickmarks view this.views.add( new revisions.view.Tickmarks({ - model: this.model + model: tooltip }) ); // Add the slider view @@ -554,12 +581,37 @@ window.wp = window.wp || {}; revisions.view.Tickmarks = wp.Backbone.View.extend({ className: 'revisions-tickmarks', + initialize: function() { + this.listenTo( this.model, 'change:revision', this.reportTickPosition ); + }, + + reportTickPosition: function( model, revision ) { + var elWidth, offset, tick, index = this.model.revisions.indexOf( revision ); + if ( index === this.model.revisions.length - 1 ) { + // Last one + tick = this.$('div:nth-of-type(' + index + ')'); + offset = tick.allPositions(); + elWidth = tick.outerWidth(); + // adjust + _.extend( offset, { + right: offset.right + elWidth + 1, + left: offset.left + elWidth + 1 + }); + } else { + // Normal tick + tick = this.$('div:nth-of-type(' + (index + 1) + ')'); + offset = tick.allPositions(); + } + this.model.set({ offset: offset }); + }, + ready: function() { var tickCount, tickWidth; tickCount = this.model.revisions.length - 1; tickWidth = 1 / tickCount; _(tickCount).times( function(){ this.$el.append( '
' ); }, this ); + this.$('div').css( 'width', ( 100 * tickWidth ) + '%' ); } }); @@ -625,13 +677,25 @@ window.wp = window.wp || {}; revisions.view.Tooltip = wp.Backbone.View.extend({ className: 'revisions-tooltip', template: wp.template('revisions-tooltip'), + direction: isRtl ? 'right' : 'left', initialize: function( options ) { - this.listenTo( this.model, 'change:revision', this.render ); + this.listenTo( this.model, 'change:offset', this.render ); this.listenTo( this.model, 'change:hovering', this.toggleVisibility ); this.listenTo( this.model, 'change:scrubbing', this.toggleVisibility ); }, + prepare: function() { + return this.model.get('revision').toJSON(); + }, + + render: function() { + var css = {}; + wp.Backbone.View.prototype.render.apply( this, arguments ); + css[this.direction] = this.model.get('offset')[this.direction] + 'px'; + this.$el.css( css ); + }, + visible: function() { return this.model.get( 'scrubbing' ) || this.model.get( 'hovering' ); }, @@ -642,23 +706,6 @@ window.wp = window.wp || {}; else this.$el.stop().fadeTo( this.el.style.opacity * 300, 0, function(){ $(this).hide(); } ); return; - }, - - render: function() { - var offset; - // Check if a revision exists. - if ( _.isNull( this.model.get('revision') ) ) - return; - - this.$el.html( this.template( this.model.get('revision').toJSON() ) ); - - // Set the position. - offset = this.model.revisions.indexOf( this.model.get('revision') ) / ( this.model.revisions.length - 1 ); - // 15% to get us to the start of the slider - // 0.7 to convert the slider-relative percentage to a page-relative percentage - // 100 to convert to a percentage - offset = 15 + (0.7 * offset * 100 ); // Now in a percentage - this.$el.css( isRtl ? 'right' : 'left', offset + '%' ); } }); @@ -681,31 +728,29 @@ window.wp = window.wp || {}; this.disabledButtonCheck(); }, - // Go to a specific modelindex, taking into account RTL mode. + // Go to a specific model index gotoModel: function( toIndex ) { var attributes = { - to: this.model.revisions.at( isRtl ? this.model.revisions.length - toIndex - 1 : toIndex ) // Reverse directions for RTL. + to: this.model.revisions.at( toIndex ) }; // If we're at the first revision, unset 'from'. - if ( isRtl ? this.model.revisions.length - toIndex - 1 : toIndex ) // Reverse directions for RTL - attributes.from = this.model.revisions.at( isRtl ? this.model.revisions.length - toIndex - 2 : toIndex - 1 ); + if ( toIndex ) + attributes.from = this.model.revisions.at( toIndex - 1 ); else this.model.unset('from', { silent: true }); this.model.set( attributes ); }, - // Go to the 'next' revision, direction takes into account RTL mode. + // Go to the 'next' revision nextRevision: function() { - var toIndex = isRtl ? this.model.revisions.length - this.model.revisions.indexOf( this.model.get('to') ) - 1 : this.model.revisions.indexOf( this.model.get('to') ); - toIndex = isRtl ? toIndex - 1 : toIndex + 1; + var toIndex = this.model.revisions.indexOf( this.model.get('to') ) + 1; this.gotoModel( toIndex ); }, - // Go to the 'previous' revision, direction takes into account RTL mode. + // Go to the 'previous' revision previousRevision: function() { - var toIndex = isRtl ? this.model.revisions.length - this.model.revisions.indexOf( this.model.get('to') ) - 1 : this.model.revisions.indexOf( this.model.get('to') ); - toIndex = isRtl ? toIndex + 1 : toIndex - 1; + var toIndex = this.model.revisions.indexOf( this.model.get('to') ) - 1; this.gotoModel( toIndex ); }, @@ -729,6 +774,7 @@ window.wp = window.wp || {}; // The slider view. revisions.view.Slider = wp.Backbone.View.extend({ className: 'wp-slider', + direction: isRtl ? 'right' : 'left', events: { 'mousemove' : 'mouseMove' @@ -757,15 +803,12 @@ window.wp = window.wp || {}; mouseMove: function( e ) { var zoneCount = this.model.revisions.length - 1, // One fewer zone than models - sliderLeft = this.$el.offset().left, // Left edge of slider + sliderFrom = this.$el.allOffsets()[this.direction], // "From" edge of slider sliderWidth = this.$el.width(), // Width of slider tickWidth = sliderWidth / zoneCount, // Calculated width of zone - actualX = e.clientX - sliderLeft, // Offset of mouse position in slider - currentModelIndex = Math.floor( ( actualX + ( tickWidth / 2 ) ) / tickWidth ); // Calculate the model index - - // Reverse direction in RTL mode. - if ( isRtl ) - currentModelIndex = this.model.revisions.length - currentModelIndex - 1; + actualX = isRtl? $(window).width() - e.pageX : e.pageX; // Flipped for RTL - sliderFrom; + actualX = actualX - sliderFrom; // Offset of mouse position in slider + var currentModelIndex = Math.floor( ( actualX + ( tickWidth / 2 ) ) / tickWidth ); // Calculate the model index // Ensure sane value for currentModelIndex. if ( currentModelIndex < 0 ) @@ -773,7 +816,7 @@ window.wp = window.wp || {}; else if ( currentModelIndex >= this.model.revisions.length ) currentModelIndex = this.model.revisions.length - 1; - // Update the tooltip model + // Update the tooltip mode this.model.set({ hoveredRevision: this.model.revisions.at( currentModelIndex ) }); }, @@ -792,103 +835,84 @@ window.wp = window.wp || {}; if ( this.model.get('compareTwoMode') ) { // in RTL mode the 'left handle' is the second in the slider, 'right' is first handles.first() - .toggleClass( 'right-handle', !! isRtl ) - .toggleClass( 'left-handle', ! isRtl ); + .toggleClass( 'to-handle', !! isRtl ) + .toggleClass( 'from-handle', ! isRtl ); handles.last() - .toggleClass( 'left-handle', !! isRtl ) - .toggleClass( 'right-handle', ! isRtl ); + .toggleClass( 'from-handle', !! isRtl ) + .toggleClass( 'to-handle', ! isRtl ); } else { - handles.removeClass('left-handle right-handle'); + handles.removeClass('from-handle to-handle'); } }, - getSliderPosition: function( ui ){ - return isRtl ? this.model.revisions.length - ui.value - 1 : ui.value; - }, - start: function( event, ui ) { this.model.set({ scrubbing: true }); // Track the mouse position to enable smooth dragging, // overrides default jQuery UI step behavior. $( window ).on( 'mousemove.wp.revisions', { view: this }, function( e ) { - var view = e.data.view, - leftDragBoundary = view.$el.offset().left, // Initial left boundary - sliderOffset = leftDragBoundary, - sliderRightEdge = leftDragBoundary + view.$el.width(), - rightDragBoundary = sliderRightEdge, // Initial right boundary - leftDragReset = 0, // Initial left drag reset - rightDragReset = sliderRightEdge - sliderOffset; // Initial right drag reset + var view = e.data.view, + leftDragBoundary = view.$el.offset().left, + sliderOffset = leftDragBoundary, + sliderRightEdge = leftDragBoundary + view.$el.width(), + rightDragBoundary = sliderRightEdge, + leftDragReset = '0', + rightDragReset = '100%', + handle = $( ui.handle ); // In two handle mode, ensure handles can't be dragged past each other. // Adjust left/right boundaries and reset points. if ( view.model.get('compareTwoMode') ) { - var rightHandle = $( ui.handle ).parent().find('.right-handle'), - leftHandle = $( ui.handle ).parent().find('.left-handle'); - - if ( $( ui.handle ).hasClass('left-handle') ) { - // Dragging the left handle, boundary is right handle. - // RTL mode calculations reverse directions. - if ( isRtl ) { - leftDragBoundary = rightHandle.offset().left + rightHandle.width(); - leftDragReset = leftDragBoundary - sliderOffset; - } else { - rightDragBoundary = rightHandle.offset().left; - rightDragReset = rightDragBoundary - sliderOffset; - } - } else { - // Dragging the right handle, boundary is the left handle. - // RTL mode calculations reverse directions. - if ( isRtl ) { - rightDragBoundary = leftHandle.offset().left; - rightDragReset = rightDragBoundary - sliderOffset; - } else { - leftDragBoundary = leftHandle.offset().left + leftHandle.width() ; - leftDragReset = leftDragBoundary - sliderOffset; - } + var handles = handle.parent().find('.ui-slider-handle'); + if ( handle.is( handles.first() ) ) { // We're the left handle + rightDragBoundary = handles.last().offset().left; + rightDragReset = rightDragBoundary - sliderOffset; + } else { // We're the right handle + leftDragBoundary = handles.first().offset().left + handles.first().width(); + leftDragReset = leftDragBoundary - sliderOffset; } } // Follow mouse movements, as long as handle remains inside slider. - if ( e.clientX < leftDragBoundary ) { - $( ui.handle ).css( 'left', leftDragReset ); // Mouse to left of slider. - } else if ( e.clientX > rightDragBoundary ) { - $( ui.handle ).css( 'left', rightDragReset ); // Mouse to right of slider. + if ( e.pageX < leftDragBoundary ) { + handle.css( 'left', leftDragReset ); // Mouse to left of slider. + } else if ( e.pageX > rightDragBoundary ) { + handle.css( 'left', rightDragReset ); // Mouse to right of slider. } else { - $( ui.handle ).css( 'left', e.clientX - sliderOffset ); // Mouse in slider. + handle.css( 'left', e.pageX - sliderOffset ); // Mouse in slider. } } ); }, + getPosition: function( position ) { + return isRtl ? this.model.revisions.length - position - 1: position; + }, + // Responds to slide events slide: function( event, ui ) { - var attributes, movedRevision, sliderPosition; + var attributes, movedRevision; // Compare two revisions mode if ( this.model.get('compareTwoMode') ) { // Prevent sliders from occupying same spot if ( ui.values[1] === ui.values[0] ) return false; - - attributes = { - to: this.model.revisions.at( isRtl ? this.model.revisions.length - ui.values[0] - 1 : ui.values[1] ), - from: this.model.revisions.at( isRtl ? this.model.revisions.length - ui.values[1] - 1 : ui.values[0] ) - }; if ( isRtl ) - movedRevision = ui.value === ui.values[1] ? attributes.from : attributes.to; - else - movedRevision = ui.value === ui.values[0] ? attributes.from : attributes.to; - } else { - sliderPosition = this.getSliderPosition( ui ); + ui.values.reverse(); attributes = { - to: this.model.revisions.at( sliderPosition ) + from: this.model.revisions.at( this.getPosition( ui.values[0] ) ), + to: this.model.revisions.at( this.getPosition( ui.values[1] ) ) + }; + } else { + attributes = { + to: this.model.revisions.at( this.getPosition( ui.value ) ) }; - movedRevision = attributes.to; // If we're at the first revision, unset 'from'. - if ( sliderPosition ) // Reverse directions for RTL. - attributes.from = this.model.revisions.at( sliderPosition - 1 ); + if ( this.getPosition( ui.value ) > 0 ) + attributes.from = this.model.revisions.at( this.getPosition( ui.value ) - 1 ); else attributes.from = undefined; } + movedRevision = this.model.revisions.at( this.getPosition( ui.value ) ); // If we are scrubbing, a scrub to a revision is considered a hover if ( this.model.get('scrubbing') ) diff --git a/wp-admin/revision.php b/wp-admin/revision.php index d87ab4e420..602adc9dfd 100644 --- a/wp-admin/revision.php +++ b/wp-admin/revision.php @@ -138,7 +138,7 @@ require_once( './admin-header.php' ); ({{ data.dateShort }}) <# } #> -
+