diff --git a/wp-admin/admin-ajax.php b/wp-admin/admin-ajax.php
index ebe7550dc6..c638d78aad 100644
--- a/wp-admin/admin-ajax.php
+++ b/wp-admin/admin-ajax.php
@@ -42,7 +42,7 @@ do_action( 'admin_init' );
$core_actions_get = array(
'fetch-list', 'ajax-tag-search', 'wp-compression-test', 'imgedit-preview', 'oembed-cache',
- 'autocomplete-user', 'dashboard-widgets', 'logged-in', 'revisions-data'
+ 'autocomplete-user', 'dashboard-widgets', 'logged-in',
);
$core_actions_post = array(
@@ -56,7 +56,7 @@ $core_actions_post = array(
'save-widget', 'set-post-thumbnail', 'date_format', 'time_format', 'wp-fullscreen-save-post',
'wp-remove-post-lock', 'dismiss-wp-pointer', 'upload-attachment', 'get-attachment',
'query-attachments', 'save-attachment', 'save-attachment-compat', 'send-link-to-editor',
- 'send-attachment-to-editor', 'save-attachment-order', 'heartbeat',
+ 'send-attachment-to-editor', 'save-attachment-order', 'heartbeat', 'get-revision-diffs',
);
// Register core Ajax calls.
diff --git a/wp-admin/css/wp-admin.css b/wp-admin/css/wp-admin.css
index 7a0da1db0b..668cf2c6c1 100644
--- a/wp-admin/css/wp-admin.css
+++ b/wp-admin/css/wp-admin.css
@@ -3481,6 +3481,48 @@ td.plugin-title p {
/*------------------------------------------------------------------------------
11.2 - Post Revisions
------------------------------------------------------------------------------*/
+.revisions .spinner {
+ float: none;
+ margin: 100px auto;
+}
+
+.revisions.loading .spinner {
+ display: block;
+}
+
+.revisions-control-frame,
+.revisions-diff-frame {
+ position: relative;
+}
+
+.revisions-controls {
+ height: 60px;
+ padding: 40px 0 20px;
+ border-bottom: 1px solid #dfdfdf;
+ margin-bottom: 10px;
+}
+
+.revisions-meta {
+ margin-top: 15px;
+}
+.revision-toggle-compare-mode {
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+.revisions-previous {
+ float: left;
+}
+
+.revisions-next {
+ float: right;
+}
+
+.wp-slider {
+ width: 70%;
+ margin: 6px auto 0;
+}
/* Revision meta box */
.post-revisions li img,
@@ -3527,13 +3569,6 @@ table.diff .diff-addedline ins {
position: relative;
}
-#toggle-revision-compare-mode {
- position: absolute;
- top: 0;
- right: 0;
- padding: 9px 9px 0 0;
-}
-
#loading-status {
display: none;
position: absolute;
@@ -3551,24 +3586,6 @@ table.diff .diff-addedline ins {
padding: 20px 0;
}
-#diff-next-revision,
-#diff-previous-revision {
- margin-top: -.4em; /* Same line as the slider (height: .8em) */
-}
-
-#diff-next-revision {
- float: right;
-}
-
-#diff-previous-revision {
- float: left;
-}
-
-#diff-slider {
- width: 70%;
- margin: 0 auto;
-}
-
.comparetwo #diff-slider {
width: 95%;
}
@@ -3588,7 +3605,7 @@ table.diff .diff-addedline ins {
}
#diff-title-to strong {
- display: none;
+ display: inline;
}
.comparing-two-revisions #diff-title-to strong {
@@ -3605,6 +3622,7 @@ table.diff .diff-addedline ins {
-webkit-border-radius: 3px;
border-radius: 3px;
padding: 5px 200px 5px 5px;
+ clear: both;
}
.diff-header {
diff --git a/wp-admin/includes/ajax-actions.php b/wp-admin/includes/ajax-actions.php
index 13b62628cb..257195018f 100644
--- a/wp-admin/includes/ajax-actions.php
+++ b/wp-admin/includes/ajax-actions.php
@@ -2082,219 +2082,31 @@ function wp_ajax_heartbeat() {
wp_send_json($response);
}
-function wp_ajax_revisions_data() {
- check_ajax_referer( 'revisions-ajax-nonce', 'nonce' );
+function wp_ajax_get_revision_diffs() {
+ require ABSPATH . 'wp-admin/includes/revision.php';
- $compare_to = ! empty( $_GET['compare_to'] ) ? absint( $_GET['compare_to'] ) : 0;
- $show_autosaves = ! empty( $_GET['show_autosaves'] );
- $show_split_view = ! empty( $_GET['show_split_view'] );
- $post_id = ! empty( $_GET['post_id'] ) ? absint( $_GET['post_id'] ) : 0;
- $right_handle_at = ! empty( $_GET['right_handle_at'] ) ? (int) $_GET['right_handle_at'] : 0;
- $left_handle_at = ! empty( $_GET['left_handle_at'] ) ? (int) $_GET['left_handle_at'] : 0;
- $single_revision_id = ! empty( $_GET['single_revision_id'] ) ? absint( $_GET['single_revision_id'] ) : 0;
- $compare_two_mode = (bool) $post_id;
+ // check_ajax_referer( 'revisions-ajax-nonce', 'nonce' );
- $all_the_revisions = array();
- if ( ! $post_id )
- $post_id = $compare_to;
+ if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
+ wp_send_json_error();
- if ( ! current_user_can( 'read_post', $post_id ) )
- continue;
+ if ( ! current_user_can( 'read_post', $post->ID ) )
+ wp_send_json_error();
- if ( ! $revisions = wp_get_post_revisions( $post_id ) )
- return;
+ // Really just pre-loading the cache here.
+ if ( ! $revisions = wp_get_post_revisions( $post->ID ) )
+ wp_send_json_error();
- $left_revision = get_post( $compare_to );
+ $return = array();
+ @set_time_limit( count( $_REQUEST['compare'] ) );
- // single model fetch mode
- // return the diff of a single revision comparison
- if ( $single_revision_id ) {
- $right_revision = get_post( $single_revision_id );
+ foreach ( $_REQUEST['compare'] as $compare_key ) {
+ list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
- if ( ! $compare_to )
- $left_revision = get_post( $post_id );
-
- // make sure the right revision is the most recent, except on oldest revision
- if ( $compare_to && $right_revision->post_date < $left_revision->post_date ) {
- $temp = $left_revision;
- $left_revision = $right_revision;
- $right_revision = $temp;
- }
-
- $lines_added = $lines_deleted = 0;
- $content = '';
- // compare from left to right, passed from application
- foreach ( _wp_post_revision_fields() as $field => $field_value ) {
- $left_content = apply_filters( "_wp_post_revision_field_$field", $left_revision->$field, $field, $left_revision, 'left' );
- $right_content = apply_filters( "_wp_post_revision_field_$field", $right_revision->$field, $field, $right_revision, 'right' );
-
- add_filter( "_wp_post_revision_field_$field", 'htmlspecialchars' );
-
- $args = array();
-
- if ( $show_split_view )
- $args = array( 'show_split_view' => true );
-
- // compare_to == 0 means first revision, so compare to a blank field to show whats changed
- $diff = wp_text_diff_with_count( ( 0 == $compare_to ) ? '' : $left_content, $right_content, $args );
-
- if ( isset( $diff[ 'html' ] ) ) {
- $content .= sprintf( '
%s
', $field_value );
- $content .= $diff[ 'html' ];
- }
-
- if ( isset( $diff[ 'lines_added' ] ) )
- $lines_added = $lines_added + $diff[ 'lines_added' ];
-
- if ( isset( $diff[ 'lines_deleted' ] ) )
- $lines_deleted = $lines_deleted + $diff[ 'lines_deleted' ];
- }
- $content = '' == $content ? __( 'No difference' ) : $content;
-
- $all_the_revisions = array (
- 'diff' => $content,
- 'linesDeleted' => $lines_deleted,
- 'linesAdded' => $lines_added
+ $return[] = array(
+ 'id' => $compare_key,
+ 'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
);
-
- echo json_encode( $all_the_revisions );
- exit();
- } // end single model fetch
-
- $count = -1;
-
- // reverse the list to start with oldest revision
- $revisions = array_reverse( $revisions );
-
- $previous_revision_id = 0;
-
- /* translators: revision date format, see http://php.net/date */
- $datef = _x( 'j F, Y @ G:i:s', 'revision date format');
-
- foreach ( $revisions as $revision ) :
- if ( ! $show_autosaves && wp_is_post_autosave( $revision ) )
- continue;
-
- $revision_from_date_author = '';
- $is_current_revision = false;
- $count++;
-
- /**
- * return blank data for diffs to the left of the left handle (for right handel model)
- * or to the right of the right handle (for left handel model)
- * and visa versa in RTL mode
- */
- if( ! is_rtl() ) {
- if ( ( ( 0 != $left_handle_at && $count < $left_handle_at ) ||
- ( 0 != $right_handle_at && $count > ( $right_handle_at - 2 ) ) ) ) {
- $all_the_revisions[] = array (
- 'ID' => $revision->ID,
- );
- continue;
- }
- } else { // is_rtl
- if ( ( 0 != $left_handle_at && $count > ( $left_handle_at - 1 ) ||
- ( 0 != $left_handle_at && $count < $right_handle_at ) ) ) {
- $all_the_revisions[] = array (
- 'ID' => $revision->ID,
- );
- continue;
- }
- }
-
- if ( $compare_two_mode ) {
- $compare_to_gravatar = get_avatar( $left_revision->post_author, 24 );
- $compare_to_author = get_the_author_meta( 'display_name', $left_revision->post_author );
- $compare_to_date = date_i18n( $datef, strtotime( $left_revision->post_modified ) );
-
- $revision_from_date_author = sprintf(
- /* translators: post revision title: 1: author avatar, 2: author name, 3: time ago, 4: date */
- _x( '%1$s %2$s, %3$s ago (%4$s)', 'post revision title' ),
- $compare_to_gravatar,
- $compare_to_author,
- human_time_diff( strtotime( $left_revision->post_modified ), current_time( 'timestamp' ) ),
- $compare_to_date
- );
- }
-
- $gravatar = get_avatar( $revision->post_author, 24 );
- $author = get_the_author_meta( 'display_name', $revision->post_author );
- $date = date_i18n( $datef, strtotime( $revision->post_modified ) );
- $revision_date_author = sprintf(
- /* translators: post revision title: 1: author avatar, 2: author name, 3: time ago, 4: date */
- _x( '%1$s %2$s, %3$s ago (%4$s)', 'post revision title' ),
- $gravatar,
- $author,
- human_time_diff( strtotime( $revision->post_modified ), current_time( 'timestamp' ) ),
- $date
- );
-
- /* translators: 1: date */
- $autosavef = _x( '%1$s [Autosave]', 'post revision title extra' );
- /* translators: 1: date */
- $currentf = _x( '%1$s [Current Revision]', 'post revision title extra' );
-
- if ( ! $post = get_post( $post_id ) )
- continue;
-
- if ( $left_revision->post_modified === $post->post_modified )
- $revision_from_date_author = sprintf( $currentf, $revision_from_date_author );
- elseif ( wp_is_post_autosave( $left_revision ) )
- $revision_from_date_author = sprintf( $autosavef, $revision_from_date_author );
-
- if ( $revision->post_modified === $post->post_modified ) {
- $revision_date_author = sprintf( $currentf, $revision_date_author );
- $is_current_revision = true;
- } elseif ( wp_is_post_autosave( $revision ) ) {
- $revision_date_author = sprintf( $autosavef, $revision_date_author );
- }
-
- /* translators: revision date short format, see http://php.net/date */
- $date_short_format = _x( 'j M @ G:i', 'revision date short format');
- $date_short = date_i18n( $date_short_format, strtotime( $revision->post_modified ) );
-
- $revision_date_author_short = sprintf(
- '%s %s
%s',
- $gravatar,
- $author,
- $date_short
- );
-
- $restore_link = wp_nonce_url(
- add_query_arg(
- array( 'revision' => $revision->ID,
- 'action' => 'restore' ),
- admin_url( 'revision.php' )
- ),
- "restore-post_{$revision->ID}"
- );
-
- // if this is a left handled calculation swap data
- if ( 0 != $right_handle_at ) {
- $tmp = $revision_from_date_author;
- $revision_from_date_author = $revision_date_author;
- $revision_date_author = $tmp;
- }
-
- if ( ( $compare_two_mode || -1 !== $previous_revision_id ) ) {
- $all_the_revisions[] = array (
- 'ID' => $revision->ID,
- 'titleTo' => $revision_date_author,
- 'titleFrom' => $revision_from_date_author,
- 'titleTooltip' => $revision_date_author_short,
- 'restoreLink' => urldecode( $restore_link ),
- 'previousID' => $previous_revision_id,
- 'isCurrent' => $is_current_revision,
- );
- }
- $previous_revision_id = $revision->ID;
-
- endforeach;
-
- // in RTL + single handle mode, reverse the revision direction
- if ( is_rtl() && $compare_two_mode )
- $all_the_revisions = array_reverse( $all_the_revisions );
-
- echo json_encode( $all_the_revisions );
- exit();
+ }
+ wp_send_json_success( $return );
}
diff --git a/wp-admin/includes/revision.php b/wp-admin/includes/revision.php
new file mode 100644
index 0000000000..8510924112
--- /dev/null
+++ b/wp-admin/includes/revision.php
@@ -0,0 +1,100 @@
+post_parent !== $post->ID )
+ return false;
+ if ( $compare_to->post_parent !== $post->ID )
+ return false;
+
+ if ( $compare_from && strtotime( $compare_from->post_date_gmt ) > strtotime( $compare_to->post_date_gmt ) ) {
+ $temp = $compare_from;
+ $compare_from = $compare_to;
+ $compare_to = $temp;
+ }
+
+ $return = array();
+
+ foreach ( _wp_post_revision_fields() as $field => $name ) {
+ $content_from = $compare_from ? apply_filters( "_wp_post_revision_field_$field", $compare_from->$field, $field, $compare_from, 'left' ) : '';
+ $content_to = apply_filters( "_wp_post_revision_field_$field", $compare_to->$field, $field, $compare_to, 'right' );
+
+ $diff = wp_text_diff( $content_from, $content_to, array( 'show_split_view' => true ) );
+
+ if ( ! $diff && 'post_title' === $field ) {
+ // It's a better user experience to still show the Title, even if it didn't change.
+ // No, you didn't see this.
+ $diff = "";
+ $diff .= '' . esc_html( $compare_from->post_title ) . ' | | ' . esc_html( $compare_to->post_title ) . ' | ';
+ $diff .= '
';
+ $diff .= '
';
+ }
+
+ if ( $diff ) {
+ $return[] = array(
+ 'id' => $field,
+ 'name' => $name,
+ 'diff' => $diff,
+ );
+ }
+ }
+ return $return;
+}
+
+function wp_prepare_revisions_for_js( $post, $selected_revision_id ) {
+ $post = get_post( $post );
+ $revisions = array();
+ $current = current_time( 'timestamp' );
+
+ $revisions = wp_get_post_revisions( $post->ID );
+
+ cache_users( wp_list_pluck( $revisions, 'post_author' ) );
+
+ foreach ( $revisions as $revision ) {
+ $modified_gmt = strtotime( $revision->post_modified_gmt );
+ $restore_link = wp_nonce_url(
+ add_query_arg(
+ array( 'revision' => $revision->ID,
+ 'action' => 'restore' ),
+ admin_url( 'revision.php' )
+ ),
+ "restore-post_{$revision->ID}"
+ );
+ $revisions[ $revision->ID ] = array(
+ 'id' => $revision->ID,
+ 'title' => get_the_title( $post->ID ),
+ 'author' => array(
+ 'id' => (int) $revision->post_author,
+ 'avatar' => get_avatar( $revision->post_author, 24 ),
+ 'name' => get_the_author_meta( 'display_name', $revision->post_author ),
+ ),
+ 'date' => date_i18n( __( 'M j, Y @ G:i' ), $modified_gmt ),
+ 'dateShort' => date_i18n( _x( 'j M @ G:i', 'revision date short format' ), $modified_gmt ),
+ 'timeAgo' => human_time_diff( $modified_gmt, $current ),
+ 'autosave' => wp_is_post_autosave( $revision ),
+ 'current' => $revision->post_modified_gmt === $post->post_modified_gmt,
+ 'restoreUrl' => urldecode( $restore_link ),
+ );
+ }
+
+ return array(
+ 'postId' => $post->ID,
+ 'nonce' => wp_create_nonce( 'revisions-ajax-nonce' ),
+ 'revisionData' => array_values( $revisions ),
+ 'selectedRevision' => $selected_revision_id,
+ );
+}
\ No newline at end of file
diff --git a/wp-admin/js/revisions.js b/wp-admin/js/revisions.js
index ce3f098244..67f7f80b8d 100644
--- a/wp-admin/js/revisions.js
+++ b/wp-admin/js/revisions.js
@@ -1,283 +1,207 @@
window.wp = window.wp || {};
(function($) {
- var Revision, Revisions, Diff, revisions;
+ var revisions;
- revisions = wp.revisions = function() {
- Diff = revisions.Diff = new Diff();
- };
-
- _.extend( revisions, { model: {}, view: {}, controller: {} } );
+ revisions = wp.revisions = { model: {}, view: {}, controller: {} };
// Link settings.
- revisions.model.settings = typeof wpRevisionsSettings === 'undefined' ? {} : wpRevisionsSettings;
+ revisions.settings = typeof _wpRevisionsSettings === 'undefined' ? {} : _wpRevisionsSettings;
/**
* ========================================================================
- * CONTROLLERS
+ * MODELS
* ========================================================================
*/
+ revisions.model.Slider = Backbone.Model.extend({
+ defaults: {
+ value: 0,
+ min: 0,
+ max: 1,
+ step: 1
+ }
+ });
- /**
- * wp.revisions.controller.Diff
- *
- * Controlls the diff
- */
- Diff = revisions.controller.Diff = Backbone.Model.extend( {
- rightDiff: 1,
- leftDiff: 1,
- revisions: null,
- leftHandleRevisions: null,
- rightHandleRevisions: null,
- revisionsInteractions: null,
- autosaves: true,
- showSplitView: true,
- singleRevision: true,
- leftModelLoading: false, // keep track of model loads
- rightModelLoading: false, // disallow slider interaction, also repeat loads, while loading
- tickmarkView: null, // the slider tickmarks
- slider: null, // the slider instance
+ revisions.model.Revision = Backbone.Model.extend({});
- constructor: function() {
- var self = this;
- this.slider = new revisions.view.Slider();
+ revisions.model.Revisions = Backbone.Collection.extend({
+ model: revisions.model.Revision,
- if ( null === this.revisions ) {
- this.revisions = new Revisions(); // set up collection
- this.startRightModelLoading();
+ comparator: function( revision ) {
+ return revision.id;
+ },
+ });
- this.revisions.fetch({ // load revision data
- success: function() {
- self.stopRightModelLoading();
- self.completeApplicationSetup();
+ revisions.model.Field = Backbone.Model.extend({});
+
+ revisions.model.Fields = Backbone.Collection.extend({
+ model: revisions.model.Field
+ });
+
+ revisions.model.Diff = Backbone.Model.extend({
+ initialize: function(attributes, options) {
+ var fields = this.get('fields');
+ this.unset('fields');
+
+ this.fields = new revisions.model.Fields( fields );
+ }
+ });
+
+ revisions.model.Diffs = Backbone.Collection.extend({
+ initialize: function(models, options) {
+ this.revisions = options.revisions;
+ this.requests = {};
+ },
+
+ model: revisions.model.Diff,
+
+ ensure: function( id, context ) {
+ var diff = this.get( id );
+ var request = this.requests[ id ];
+ var deferred = $.Deferred();
+ var ids = {};
+
+ if ( diff ) {
+ deferred.resolveWith( context, [ diff ] );
+ } else {
+ this.trigger( 'ensure:load', ids );
+ _.each( ids, _.bind( function(id) {
+ // Remove anything that has an ongoing request
+ if ( this.requests[ id ] )
+ delete ids[ id ];
+ }, this ) );
+ if ( ! request ) {
+ // Always include the ID that started this ensure
+ ids[ id ] = true;
+ request = this.load( _.keys( ids ) );
+ }
+
+ request.done( _.bind( function() {
+ deferred.resolveWith( context, [ this.get( id ) ] );
+ }, this ) );
+ }
+
+ return deferred.promise();
+ },
+
+ loadNew: function( comparisons ) {
+ comparisons = _.object( comparisons, comparisons );
+ _.each( comparisons, _.bind( function( id ) {
+ // Exists
+ if ( this.get( id ) )
+ delete comparisons[ id ];
+ }, this ) );
+ comparisons = _.toArray( comparisons );
+ return this.load( comparisons );
+ },
+
+ load: function( comparisons ) {
+ // Our collection should only ever grow, never shrink, so remove: false
+ return this.fetch({ data: { compare: comparisons }, remove: false });
+ },
+
+/**/
+ loadLast: function( num ) {
+ num = num || 1;
+ var ids = this.getProximalDiffIds();
+ ids = _.last( ids, num );
+
+ if ( ids.length ) {
+ return this.loadNew( ids );
+ }
+ },
+
+ loadLastUnloaded: function( num ) {
+ num = num || 1;
+ var ids = this.getUnloadedProximalDiffIds();
+ ids = _.last( ids, num );
+
+ if ( ids.length ) {
+ return this.loadNew( ids );
+ }
+ },
+
+ getProximalDiffIds: function() {
+ var previous = 0, ids = [];
+ this.revisions.each( _.bind( function(revision) {
+ ids.push( previous + ':' + revision.id );
+ previous = revision.id;
+ }, this ) );
+ return ids;
+ },
+
+ getUnloadedProximalDiffIds: function() {
+ var comparisons = this.getProximalDiffIds();
+ comparisons = _.object( comparisons, comparisons );
+ _.each( comparisons, _.bind( function( id ) {
+ // Exists
+ if ( this.get( id ) )
+ delete comparisons[ id ];
+ }, this ) );
+ return _.toArray( comparisons );
+ },
+
+ loadAllBy: function( chunkSize ) {
+ chunkSize = chunkSize || 20;
+ var unloaded = this.getUnloadedProximalDiffIds();
+ if ( unloaded.length ) {
+ return this.loadLastUnloaded( chunkSize ).always( _.bind( function() {
+ this.loadAllBy( chunkSize );
+ }, this ) );
+ }
+ },
+
+ sync: function( method, model, options ) {
+ if ( 'read' === method ) {
+ options = options || {};
+ options.context = this;
+ options.data = _.extend( options.data || {}, {
+ action: 'get-revision-diffs',
+ post_id: revisions.settings.postId
+ });
+
+ var deferred = wp.xhr.send( options );
+ var requests = this.requests;
+
+ // Record that we're requesting each diff.
+ if ( options.data.compare ) {
+ _.each( options.data.compare, function( id ) {
+ requests[ id ] = deferred;
+ });
+ }
+
+ // When the request completes, clear the stored request.
+ deferred.always( function() {
+ if ( options.data.compare ) {
+ _.each( options.data.compare, function( id ) {
+ delete requests[ id ];
+ });
}
});
- }
- },
- loadDiffs: function( models ) {
- var self = this,
- revisionsToLoad = models.where( { completed: false } ),
- delay = 0,
- totalChanges;
+ return deferred;
- // match slider to passed revision_id
- _.each( revisionsToLoad, function( revision ) {
- if ( revision.get( 'ID' ) == revisions.model.settings.revision_id )
- self.rightDiff = self.revisions.indexOf( revision ) + 1;
- });
-
- _.each( revisionsToLoad, function( revision ) {
- _.delay( function() {
- revision.fetch( {
- update: true,
- add: false,
- remove: false,
- success: function( model ) {
- model.set( 'completed', true );
-
- // stop spinner when all models are loaded
- if ( 0 === models.where( { completed: false } ).length )
- self.stopModelLoadingSpinner();
-
- totalChanges = model.get( 'linesAdded' ) + model.get( 'linesDeleted' ),
- scopeOfChanges = 'vsmall';
-
- // Note: hard coded scope of changes
- // TODO change to dynamic based on range of values
- if ( totalChanges > 1 && totalChanges <= 3 ) {
- scopeOfChanges = 'small';
- } else if ( totalChanges > 3 && totalChanges <= 5 ) {
- scopeOfChanges = 'med';
- } else if ( totalChanges > 5 && totalChanges <= 10 ) {
- scopeOfChanges = 'large';
- } else if ( totalChanges > 10 ) {
- scopeOfChanges = 'vlarge';
- }
- model.set( 'scopeOfChanges', scopeOfChanges );
- if ( 0 !== self.rightDiff &&
- model.get( 'ID' ) === self.revisions.at( self.rightDiff - 1 ).get( 'ID' ) ) {
- // reload if current model refreshed
- self.revisionView.render();
- }
- self.tickmarkView.render();
- }
- } );
- }, delay ) ;
- delay = delay + 150; // stagger model loads to avoid hammering server with requests
- }
- );
- },
-
- startLeftModelLoading: function() {
- this.leftModelLoading = true;
- $('#revision-diff-container').addClass('left-model-loading');
- },
-
- stopLeftModelLoading: function() {
- this.leftModelLoading = false;
- },
-
- startRightModelLoading: function() {
- this.rightModelLoading = true;
- $('#revision-diff-container').addClass('right-model-loading');
- },
-
- stopRightModelLoading: function() {
- this.rightModelLoading = false;
- },
-
- stopModelLoadingSpinner: function() {
- $('#revision-diff-container').removeClass('right-model-loading');
- $('#revision-diff-container').removeClass('left-model-loading');
- },
-
- reloadModel: function() {
- if ( this.singleRevision ) {
- this.reloadModelSingle();
+ // Otherwise, fall back to `Backbone.sync()`.
} else {
- this.reloadLeftRight();
+ return Backbone.Model.prototype.sync.apply( this, arguments );
}
+ }
+ });
+
+
+ revisions.model.FrameState = Backbone.Model.extend({
+ initialize: function( attributes, options ) {
+ this.revisions = options.revisions;
+ this.diffs = new revisions.model.Diffs( [], {revisions: this.revisions} );
+
+ this.listenTo( this, 'change:from change:to', this.updateDiffId );
},
- // load the models for the single handle mode
- reloadModelSingle: function() {
- var self = this;
-
- self.startRightModelLoading();
-
- self.revisions.reload({
- options: {
- 'showAutosaves': self.autosaves,
- 'showSplitView': self.showSplitView
- },
-
- success: function() {
- var revisionCount = self.revisions.length;
- self.revisionView.model = self.revisions;
- self.revisionView.render();
- self.loadDiffs( self.revisions );
- self.tickmarkView.model = self.revisions;
- self.tickmarkView.render();
- self.slider.refresh({
- 'max': revisionCount - 1, // slider starts at 0 in single handle mode
- 'value': self.rightDiff - 1 // slider starts at 0 in single handle mode
- }, true);
- },
-
- error: function() {
- self.stopRightModelLoading();
- }
- });
- },
-
- // load the models for the left handle (the right handler has moved)
- reloadLeft: function() {
- var self = this;
- self.startLeftModelLoading();
- self.leftHandleRevisions = new Revisions( {}, {
- 'compareTo': self.revisions.at( self.rightDiff - 1 ).get( 'ID' ), // diff and model count off by 1
- 'showAutosaves': self.autosaves,
- 'showSplitView': self.showSplitView,
- 'rightHandleAt': self.rightDiff
- });
-
- self.leftHandleRevisions.fetch({
- success: function(){
- self.stopLeftModelLoading();
- self.loadDiffs( self.leftHandleRevisions );
- self.tickmarkView.model = self.leftHandleRevisions;
- self.slider.refresh({
- 'max': self.revisions.length
- });
- // ensure right handle not beyond length
- if ( self.rightDiff > self.revisions.length )
- self.rightDiff = self.revisions.length;
- },
-
- error: function() {
- self.stopLeftModelLoading();
- }
- });
- },
-
- // load the models for the right handle (the left handle has moved)
- reloadRight: function() {
- var self = this;
- self.startRightModelLoading();
- self.rightHandleRevisions = new Revisions( {}, {
- 'compareTo': self.revisions.at( self.leftDiff - 1 ).get( 'ID' ), // diff and model count off by 1
- 'showAutosaves': self.autosaves,
- 'showSplitView': self.showSplitView,
- 'leftHandleAt': self.leftDiff
- });
-
- self.rightHandleRevisions.fetch({
- success: function(){
- self.stopRightModelLoading();
- self.loadDiffs( self.rightHandleRevisions );
- self.tickmarkView.model = self.rightHandleRevisions;
- self.slider.refresh({
- 'max': self.revisions.length
- }, true);
- },
-
- error: function( response ) {
- self.stopRightModelLoading();
- }
- });
-
- },
-
- /**
- * reloadLeftRight reload models for both the left and right handles
- */
- reloadLeftRight: function() {
- this.startRightModelLoading();
- this.startLeftModelLoading();
- this.reloadLeft();
- this.reloadRight();
- },
-
- disabledButtonCheck: function( val ) {
- var maxVal = this.revisions.length - 1,
- next = ! isRtl ? $( '#next' ) : $( '#previous' ),
- prev = ! isRtl ? $( '#previous' ) : $( '#next' );
-
- // Disable "Next" button if you're on the last node
- if ( maxVal === val )
- next.prop( 'disabled', true );
- else
- next.prop( 'disabled', false );
-
- // Disable "Previous" button if you're on the 0 node
- if ( 0 === val )
- prev.prop( 'disabled', true );
- else
- prev.prop( 'disabled', false );
- },
-
- /**
- * completeApplicationSetup finishes loading all views once the initial model load is complete
- */
- completeApplicationSetup: function() {
- this.revisionView = new revisions.view.Diff({
- model: this.revisions
- });
- this.revisionView.render(); // render the revision view
-
- this.loadDiffs( this.revisions ); // get the actual revisions data
-
- this.revisionsInteractions = new revisions.view.Interact({
- model: this.revisions
- });
- this.revisionsInteractions.render(); // render the interaction view
-
- this.tickmarkView = new revisions.view.Tickmarks({
- model: this.revisions
- });
- this.tickmarkView.render(); // render the tickmark view
+ updateDiffId: function() {
+ var from = this.get( 'from' );
+ var to = this.get( 'to' );
+ this.set( 'diffId', (from ? from.id : '0') + ':' + to.id );
}
});
@@ -288,534 +212,286 @@ window.wp = window.wp || {};
* ========================================================================
*/
- /**
- * wp.revisions.view.Slider
- *
- * The slider
- */
- revisions.view.Slider = Backbone.View.extend({
- el: $( '#diff-slider' ),
- singleRevision: true,
+ // The frame view. This contains the entire page.
+ revisions.view.Frame = wp.Backbone.View.extend({
+ tagName: 'div',
+ className: 'revisions',
+ template: wp.template('revisions-frame'),
- initialize: function( options ) {
- this.options = _.defaults( options || {}, {
- value: 0,
- min: 0,
- max: 1,
- step: 1
+ initialize: function() {
+ this.model = new revisions.model.FrameState({}, {
+ revisions: this.collection
});
- },
- /**
- * respond to slider slide events
- * Note: in one handle mode, jQuery UI reports leftmost position as 0
- * in two handle mode, jQuery UI Slider reports leftmost position as 1
- */
- slide: function( event, ui ) {
- if ( this.singleRevision ) {
- Diff.rightDiff = ( ui.value + 1 );
- Diff.revisionView.render();
- Diff.disabledButtonCheck( ui.value );
- } else {
- if ( ui.values[0] === ui.values[1] ) // prevent compare to self
- return false;
+ this.listenTo( this.model, 'change:diffId', this.updateDiff );
- if ( $( ui.handle ).hasClass( 'left-handle' ) ) {
- // Left handler
- if ( Diff.leftModelLoading ) // left model still loading, prevent sliding left handle
- return false;
+ this.views.set( '.revisions-control-frame', new revisions.view.Controls({
+ model: this.model
+ }) );
- Diff.leftDiff = isRtl ? ui.values[1] : ui.values[0]; // handles are reversed in RTL mode
- } else {
- // Right handler
- if ( Diff.rightModelLoading ) // right model still loading, prevent sliding right handle
- return false;
+ if ( this.model.revisions.length ) {
+ var last = this.model.revisions.last(2);
+ var attributes = { to: last.pop() };
- Diff.rightDiff = isRtl ? ui.values[0] : ui.values[1]; // handles are reversed in RTL mode
- }
+ if ( last.length )
+ attributes.from = last.pop();
- Diff.revisionView.render();
+ this.model.set( attributes );
+
+ // Load the rest: first 10, then the rest by 50
+ this.model.diffs.loadLastUnloaded( 10 ).always( _.bind( function() {
+ this.model.diffs.loadAllBy( 50 );
+ }, this ) );
}
},
- /**
- * responds to slider start sliding events
- * in two handle mode stores start position, so if unchanged at stop event no need to reload diffs
- * also swaps in the appropriate models - left handled or right handled
- */
- start: function( event, ui ) {
- // Not needed in one mode
- if ( this.singleRevision )
- return;
-
- if ( $( ui.handle ).hasClass( 'left-handle' ) ) {
- // Left handler
- if ( Diff.leftModelLoading ) // left model still loading, prevent sliding left handle
- return false;
-
- Diff.revisionView.draggingLeft = true;
-
- if ( Diff.revisionView.model !== Diff.leftHandleRevisions &&
- null !== Diff.leftHandleRevisions ) {
- Diff.revisionView.model = Diff.leftHandleRevisions; // use the left handle models
- Diff.tickmarkView.model = Diff.leftHandleRevisions;
- Diff.tickmarkView.render();
- }
-
- Diff.leftDiffStart = isRtl ? ui.values[1] : ui.values[0]; // in RTL mode the 'left handle' is the second in the slider, 'right' is first
-
- } else {
- // Right handler
- if ( Diff.rightModelLoading || 0 === Diff.rightHandleRevisions.length) // right model still loading, prevent sliding right handle
- return false;
-
- if ( Diff.revisionView.model !== Diff.rightHandleRevisions &&
- null !== Diff.rightHandleRevisions ) {
- Diff.revisionView.model = Diff.rightHandleRevisions; // use the right handle models
- Diff.tickmarkView.model = Diff.rightHandleRevisions;
- Diff.tickmarkView.render();
- }
-
- Diff.revisionView.draggingLeft = false;
- Diff.rightDiffStart = isRtl ? ui.values[0] : ui.values[1]; // in RTL mode the 'left handle' is the second in the slider, 'right' is first
- }
- },
-
- /**
- * responds to slider stop events
- * in two handled mode, if the handle that stopped has moved, reload the diffs for the other handle
- * the other handle compares to this handle's position, so if it changes they need to be recalculated
- */
- stop: function( event, ui ) {
- // Not needed in one mode
- if ( this.singleRevision )
- return;
-
- // calculate and generate a diff for comparing to the left handle
- // and the right handle, swap out when dragging
- if ( $( ui.handle ).hasClass( 'left-handle' ) ) {
- // Left handler
- if ( Diff.leftDiffStart !== isRtl ? ui.values[1] : ui.values[0] ) // in RTL mode the 'left handle' is the second in the slider, 'right' is first
- Diff.reloadRight();
- } else {
- // Right handler
- if ( Diff.rightDiffStart !== isRtl ? ui.values[0] : ui.values[1] ) // in RTL mode the 'left handle' is the second in the slider, 'right' is first
- Diff.reloadLeft();
- }
- },
-
- addTooltip: function( handle, message ) {
- handle.find( '.ui-slider-tooltip' ).html( message );
- },
-
- width: function() {
- return $( '#diff-slider' ).width();
- },
-
- setWidth: function( width ) {
- $( '#diff-slider' ).width( width );
- },
-
- refresh: function( options, slide ) {
- $( '#diff-slider' ).slider( 'option', options );
-
- // Triggers the slide event
- if ( slide )
- $( '#diff-slider' ).trigger( 'slide' );
-
- Diff.disabledButtonCheck( options.value );
- },
-
- option: function( key ) {
- return $( '#diff-slider' ).slider( 'option', key );
- },
-
render: function() {
- var self = this;
- // this.$el doesn't work, why?
- $( '#diff-slider' ).slider( {
- slide: $.proxy( self.slide, self ),
- start: $.proxy( self.start, self ),
- stop: $.proxy( self.stop, self )
- } );
+ wp.Backbone.View.prototype.render.apply( this, arguments );
- // Set options
- this.refresh( this.options );
- }
- });
-
- /**
- * wp.revisions.view.Tickmarks
- *
- * The slider tickmarks.
- */
- revisions.view.Tickmarks = Backbone.View.extend({
- el: $('#diff-slider-ticks'),
- template: wp.template('revision-ticks'),
- model: Revision,
-
- resetTicks: function() {
- var sliderMax, sliderWidth, adjustMax, tickWidth, tickCount = 0, aTickWidth, tickMargin, self = this, firstTick, lastTick;
- sliderMax = Diff.slider.option( 'max' );
- sliderWidth = Diff.slider.width();
- adjustMax = Diff.singleRevision ? 0 : 1;
- tickWidth = Math.floor( sliderWidth / ( sliderMax - adjustMax ) );
- tickWidth = ( tickWidth > 50 ) ? 50 : tickWidth; // set minimum and maximum widths for tick marks
- tickWidth = ( tickWidth < 6 ) ? 6 : tickWidth;
- sliderWidth = tickWidth * ( sliderMax - adjustMax ); // calculate the slider width
- aTickWidth = $( '.revision-tick' ).width();
-
- if ( tickWidth !== aTickWidth ) { // is the width already set correctly?
- $( '.revision-tick' ).each( function() {
- tickMargin = Math.floor( ( tickWidth - $( this ).width() ) / 2 ) + 1;
- $( this ).css( 'border-left', tickMargin + 'px solid #f7f7f7'); // space the ticks out using margins
- $( this ).css( 'border-right', ( tickWidth - tickMargin - $( this ).width() ) + 'px solid #f7f7f7'); // space the ticks out using margins
- });
- firstTick = $( '.revision-tick' ).first(); //cache selectors for optimization
- lastTick = $( '.revision-tick' ).last();
-
- sliderWidth = sliderWidth + Math.ceil( ( tickWidth - ( lastTick.outerWidth() - lastTick.innerWidth() ) ) / 2 ); // room for the last tick
- sliderWidth = sliderWidth + Math.ceil( ( tickWidth - ( firstTick.outerWidth() - firstTick.innerWidth() ) ) / 2 ); // room for the first tick
- firstTick.css( 'border-left', 'none' ); // first tick gets no left border
- lastTick.css( 'border-right', 'none' ); // last tick gets no right border
- }
-
- /**
- * reset the slider width
- */
- Diff.slider.setWidth( sliderWidth );
- $( '.diff-slider-ticks-wrapper' ).width( sliderWidth );
- $( '#diff-slider-ticks' ).width( sliderWidth );
-
- /**
- * go through all ticks, add hover and click interactions
- */
- $( '.revision-tick' ).each( function() {
- Diff.slider.addTooltip ( $( this ), Diff.revisions.at( tickCount++ ).get( 'titleTooltip' ) );
- $( this ).hover(
- function() {
- $( this ).find( '.ui-slider-tooltip' ).show().append('');
- },
- function() {
- $( this ).find( '.ui-slider-tooltip' ).hide().find( '.arrow' ).remove();
- }
- );
-
- /**
- * move the slider handle when the tick marks are clicked
- */
- $( this ).on( 'click',
- { tickCount: tickCount }, // pass the tick through so we know where to move the handle
- function( event ) {
- if ( Diff.slider.singleRevision ) { // single handle mode
- Diff.rightDiff = event.data.tickCount; // reposition the right handle
- Diff.slider.refresh({
- value: Diff.rightDiff - 1
- } );
- } else { //compare two mode
- if ( isRtl ) {
- if ( event.data.tickCount < Diff.leftDiff ) { // click was on the 'left' side
- Diff.rightDiff = event.data.tickCount; // set the 'right' handle location
- Diff.reloadLeft(); // reload the left handle comparison models
- } else { // middle or 'right' clicks
- Diff.leftDiff = event.data.tickCount; // set the 'left' handle location
- Diff.reloadRight(); // reload right handle models
- }
- } else {
- if ( event.data.tickCount < Diff.leftDiff ) { // click was on the 'left' side
- Diff.leftDiff = event.data.tickCount; // set the left handle location
- Diff.reloadRight(); // reload the right handle comparison models
- } else { // middle or 'right' clicks
- Diff.rightDiff = event.data.tickCount; // set the right handle location
- Diff.reloadLeft(); // reload left handle models
- }
- }
- Diff.slider.refresh( { // set the slider handle positions
- values: [ isRtl ? Diff.rightDiff : Diff.leftDiff, isRtl ? Diff.leftDiff : Diff.rightDiff ]
- } );
- }
- Diff.revisionView.render(); // render the main view
- } );
- } );
- },
-
- // render the tick mark view
- render: function() {
- var self = this, addHtml;
-
- if ( null !== self.model ) {
- addHtml = "";
- _.each ( self.model.models, function( theModel ) {
- addHtml = addHtml + self.template ( theModel.toJSON() );
- });
- self.$el.html( addHtml );
-
- }
- self.resetTicks();
- return self;
- }
- } );
-
- /**
- * wp.revisions.view.Interact
- *
- * Next/Prev buttons and the slider
- */
- revisions.view.Interact = Backbone.View.extend({
- el: $( '#revision-interact' ),
- template: wp.template( 'revision-interact' ),
-
- // next and previous buttons, only available in compare one mode
- events: {
- 'click #next': ! isRtl ? 'nextRevision' : 'previousRevision',
- 'click #previous': ! isRtl ? 'previousRevision' : 'nextRevision'
- },
-
- render: function() {
- var modelcount;
- this.$el.html( this.template );
-
- modelcount = Diff.revisions.length;
-
- Diff.slider.singleRevision = Diff.singleRevision;
- Diff.slider.render();
-
- if ( Diff.singleRevision ) {
- Diff.slider.refresh({
- value: Diff.rightDiff - 1, // rightDiff value is off model index by 1
- min: 0,
- max: modelcount - 1
- });
-
- $( '#revision-diff-container' ).removeClass( 'comparing-two-revisions' );
-
- } else {
- Diff.slider.refresh({
- // in RTL mode the 'left handle' is the second in the slider, 'right' is first
- values: [ isRtl ? Diff.rightDiff : Diff.leftDiff, isRtl ? Diff.leftDiff : Diff.rightDiff ],
- min: 1,
- max: modelcount + 1,
- range: true
- });
-
- $( '#revision-diff-container' ).addClass( 'comparing-two-revisions' );
- // in RTL mode the 'left handle' is the second in the slider, 'right' is first
- $( '#diff-slider a.ui-slider-handle' ).first().addClass( isRtl ? 'right-handle' : 'left-handle' );
- $( '#diff-slider a.ui-slider-handle' ).last().addClass( isRtl ? 'left-handle' : 'right-handle' );
-
- }
+ $('#wpbody-content .wrap').append( this.el );
+ this.views.ready();
return this;
},
- // go to the next revision
+ updateDiff: function() {
+ this.model.diffs.ensure( this.model.get('diffId'), this ).done( function( diff ) {
+ if ( this.model.get('diffId') !== diff.id )
+ return;
+ this.views.set( '.revisions-diff-frame', new revisions.view.Diff({
+ model: diff
+ }));
+ });
+ }
+ });
+
+ // The control view.
+ // This contains the revision slider, previous/next buttons, and the compare checkbox.
+ revisions.view.Controls = wp.Backbone.View.extend({
+ tagName: 'div',
+ className: 'revisions-controls',
+
+ initialize: function() {
+ // Add the button view
+ this.views.add( new revisions.view.Buttons({
+ model: this.model
+ }));
+
+ // Add the Slider view
+ this.views.add( new revisions.view.Slider({
+ model: this.model
+ }) );
+
+ // Add the Meta view
+ this.views.add( new revisions.view.Meta({
+ model: this.model
+ }) );
+ }
+ });
+
+ // The meta view.
+ // This contains the revision meta, and the restore button.
+ revisions.view.Meta = wp.Backbone.View.extend({
+ tagName: 'div',
+ className: 'revisions-meta',
+ template: wp.template('revisions-meta'),
+
+ initialize: function() {
+ this.listenTo( this.model, 'change:diffId', this.updateMeta );
+ },
+
+ events: {
+ 'click #restore-revision': 'restoreRevision'
+ },
+
+ restoreRevision: function() {
+ var restoreUrl = this.model.get('to').attributes.restoreUrl.replace(/&/g, '&');
+ document.location = restoreUrl;
+ },
+
+ updateMeta: function() {
+ this.$el.html( this.template( this.model.toJSON() ) );
+ if( this.model.get( 'to' ).attributes.current ) {
+ $( '#restore-revision' ).prop( 'disabled', true);
+ } else {
+ $( '#restore-revision' ).prop( 'disabled', false)
+ }
+ }
+ });
+
+
+ // The buttons view.
+ // Encapsulates all of the configuration for the previous/next buttons, and the compare checkbox.
+ revisions.view.Buttons = wp.Backbone.View.extend({
+ tagName: 'div',
+ className: 'revisions-buttons',
+ template: wp.template('revisions-controls'),
+
+ initialize: function() {
+ this.$el.html( this.template() )
+ },
+
+ events: {
+ 'click #next': 'nextRevision',
+ 'click #previous': 'previousRevision'
+ },
+
+ gotoModel: function( toIndex ) {
+ var attributes = {
+ to: this.model.revisions.at( isRtl ? this.model.revisions.length - toIndex - 1 : toIndex ) // Reverse directions for Rtl
+ };
+ // 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 );
+ else
+ this.model.unset('from', { silent: true });
+
+ this.model.set( attributes );
+ },
+
nextRevision: function() {
- if ( Diff.rightDiff < this.model.length ) // unless at right boundry
- Diff.rightDiff = Diff.rightDiff + 1 ;
-
- Diff.revisionView.render();
-
- Diff.slider.refresh({
- value: Diff.rightDiff - 1
- }, true );
+ var toIndex = this.model.revisions.indexOf( this.model.get( 'to' ) );
+ toIndex = isRtl ? toIndex - 1 : toIndex + 1;
+ this.gotoModel( toIndex );
},
-
- // go to the previous revision
+
previousRevision: function() {
- if ( Diff.rightDiff > 1 ) // unless at left boundry
- Diff.rightDiff = Diff.rightDiff - 1 ;
+ var toIndex = this.model.revisions.indexOf( this.model.get('to') );
+ toIndex = isRtl ? toIndex + 1 : toIndex - 1;
+ this.gotoModel( toIndex );
+ },
+
+ ready: function() {
+ this.listenTo( this.model, 'change:diffId', this.disabledButtonCheck );
+ },
+
+ // Check to see if the Previous or Next buttons need to be disabled or enabled
+ disabledButtonCheck: function() {
+ var maxVal = isRtl ? 0 : this.model.revisions.length - 1,
+ minVal = isRtl ? this.model.revisions.length - 1 : 0,
+ next = $( '.revisions-next .button' ),
+ previous = $( '.revisions-previous .button' ),
+ val = this.model.revisions.indexOf( this.model.get( 'to' ) );
+
+ // Disable "Next" button if you're on the last node
+ if ( maxVal === val )
+ next.prop( 'disabled', true );
+ else
+ next.prop( 'disabled', false );
+
+ // Disable "Previous" button if you're on the first node
+ if ( minVal === val )
+ previous.prop( 'disabled', true );
+ else
+ previous.prop( 'disabled', false );
+ },
- Diff.revisionView.render();
- Diff.slider.refresh({
- value: Diff.rightDiff - 1
- }, true );
- }
});
- /**
- * wp.revisions.view.Diff
- *
- * Diff, compare two checkbox and restore button
- */
- revisions.view.Diff = Backbone.View.extend({
- el: $( '#revisions-diff' ),
- template: wp.template( 'revisions-diff' ),
- draggingLeft: false,
+ // The slider view.
+ // Encapsulates all of the configuration for the jQuery UI slider into a view.
+ revisions.view.Slider = wp.Backbone.View.extend({
+ tagName: 'div',
+ className: 'wp-slider',
- // the compare two button is in this view, add the interaction here
- events: {
- 'click #compare-two-revisions': 'compareTwo',
- 'click #restore-revision': 'restore'
+ initialize: function() {
+ _.bindAll( this, 'start', 'slide', 'stop' );
+
+ // Create the slider model from the provided collection data.
+ // TODO: This should actually pull from the model's `to` key.
+ var latestRevisionIndex = this.model.revisions.length - 1;
+
+ // Find the initially selected revision
+ var initiallySelectedRevisionIndex =
+ this.model.revisions.indexOf(
+ this.model.revisions.findWhere( { id: Number( revisions.settings.selectedRevision ) } ) );
+
+ this.settings = new revisions.model.Slider({
+ max: latestRevisionIndex,
+ value: initiallySelectedRevisionIndex,
+ start: this.start,
+ slide: this.slide,
+ stop: this.stop
+ });
},
- // render the revisions
- render: function() {
- var addHtml = '', thediff;
+ ready: function() {
+ this.$el.slider( this.settings.toJSON() );
+ this.settings.on( 'change', function( model, options ) {
+ // Apply changes to slider settings here.
+ this.$el.slider( { value: this.model.revisions.indexOf( this.model.get( 'to' ) ) } ); // Set handle to current to model
+ }, this );
+ // Reset to the initially selected revision
+ this.slide( '', this.settings.attributes );
- // compare two revisions mode?
- if ( ! Diff.singleRevision ) {
- if ( this.draggingLeft ) {
- thediff = Diff.leftDiff - 1; //leftDiff value is off model index by 1
- if ( this.model.at( thediff ) ) {
- addHtml = this.template( this.model.at( thediff ).toJSON() );
- }
- } else { // dragging right handle
- thediff = Diff.rightDiff - 1; // rightDiff value is off model index by 1
- if ( this.model.at( thediff ) ) {
- addHtml = this.template( this.model.at( thediff ).toJSON() );
- }
- }
- } else { // end compare two revisions mode, eg only one slider handle
- if ( this.model.at( Diff.rightDiff - 1 ) ) { // rightDiff value is off model index by 1
- addHtml = this.template( this.model.at( Diff.rightDiff - 1 ).toJSON() );
- }
- }
- this.$el.html( addHtml );
+ // Listen for changes in the diffId
+ this.listenTo( this.model, 'change:diffId', this.diffIdChanged );
- if ( this.model.length < 2 ) {
- $( '#diff-slider' ).hide(); // don't allow compare two if fewer than three revisions
- $( '.diff-slider-ticks-wrapper' ).hide();
- }
-
- this.toggleCompareTwoCheckbox();
-
- // hide the restore button when on the last sport/current post data
- $( '#restore-revision' ).toggle( ! Diff.revisions.at( Diff.rightDiff - 1 ).get( 'isCurrent' ) );
-
- return this;
},
- toggleCompareTwoCheckbox: function() {
- // don't allow compare two if fewer than three revisions
- if ( this.model.length < 3 )
- $( '#toggle-revision-compare-mode' ).hide();
-
- $( '#compare-two-revisions' ).prop( 'checked', ! Diff.singleRevision );
+ diffIdChanged: function() {
+ // Reset the view settings when diffId is changed
+ this.settings.set( { 'value': this.model.revisions.indexOf( this.model.get( 'to' ) ) } );
},
- // turn on/off the compare two mode
- compareTwo: function() {
- if ( $( '#compare-two-revisions' ).is( ':checked' ) ) { // compare 2 mode
- Diff.singleRevision = false ;
+ start: function( event, ui ) {
+ // Track the mouse position to enable smooth dragging, overrides default jquery ui step behaviour
+ $( window ).mousemove( function( e ) {
+ var sliderLeft = $( '.wp-slider' ).offset().left,
+ sliderRight = sliderLeft + $( '.wp-slider' ).width();
- // in RTL mode handles are swapped, so boundary checks are different;
- if ( isRtl ){
- Diff.leftDiff = Diff.revisions.length; // put the left handle at the rightmost position, representing current revision
-
- if ( Diff.revisions.length === Diff.rightDiff ) // make sure 'left' handle not in rightmost slot
- Diff.rightDiff = Diff.rightDiff - 1;
+ // Follow mouse movements, as long as handle remains inside slider
+ if ( e.clientX < sliderLeft ) {
+ $( ui.handle ).css( 'left', 0 ); // Mouse to left of slider
+ } else if ( e.clientX > sliderRight ) {
+ $( ui.handle ).css( 'left', sliderRight - sliderLeft); // Mouse to right of slider
} else {
- if ( 1 === Diff.rightDiff ) // make sure right handle not in leftmost slot
- Diff.rightDiff = 2;
+ $( ui.handle ).css( 'left', e.clientX - sliderLeft ); // Mouse in slider
}
-
- Diff.revisionView.draggingLeft = false;
-
- revisions.model.settings.revision_id = ''; // reset passed revision id so switching back to one handle mode doesn't re-select revision
- Diff.reloadLeftRight(); // load diffs for left and right handles
- Diff.revisionView.model = Diff.rightHandleRevisions;
-
- } else { // compare one mode
- Diff.singleRevision = true;
- Diff.revisionView.draggingLeft = false;
- Diff.reloadModelSingle();
- }
- Diff.revisionsInteractions.render();
- Diff.tickmarkView.render();
+ } ); // End mousemove
},
- restore: function() {
- document.location = $( '#restore-revision' ).data( 'restoreLink' );
+ slide: function( event, ui ) {
+ var attributes = {
+ to: this.model.revisions.at( isRtl ? this.model.revisions.length - ui.value - 1 : ui.value ) // Reverse directions for Rtl
+ };
+
+ // If we're at the first revision, unset 'from'.
+ if ( isRtl ? this.model.revisions.length - ui.value - 1 : ui.value ) // Reverse directions for Rtl
+ attributes.from = this.model.revisions.at( isRtl ? this.model.revisions.length - ui.value - 2 : ui.value - 1 );
+ else
+ this.model.unset('from', { silent: true });
+
+ this.model.set( attributes );
+ },
+
+ stop: function( event, ui ) {
+ $( window ).unbind( 'mousemove' ); // Stop tracking the mouse
+ // Reset settings pops handle back to the step position
+ this.settings.trigger( 'change' );
}
});
+ // The diff view.
+ // This is the view for the current active diff.
+ revisions.view.Diff = wp.Backbone.View.extend({
+ tagName: 'div',
+ className: 'revisions-diff',
+ template: wp.template('revisions-diff'),
- /**
- * ========================================================================
- * MODELS
- * ========================================================================
- */
-
- /**
- * wp.revisions.Revision
- */
- Revision = revisions.model.Revision = Backbone.Model.extend({
- idAttribute: 'ID',
-
- defaults: {
- ID: 0,
- titleTo: '',
- titleTooltip: '',
- titleFrom: '',
- diff: '',
- restoreLink: '',
- completed: false,
- linesAdded: 0,
- linesDeleted: 0,
- scopeOfChanges: 'none',
- previousID: 0,
- isCurrent: false
- },
-
- url: function() {
- if ( Diff.singleRevision ) {
- return ajaxurl +
- '?action=revisions-data' +
- '&show_autosaves=true' +
- '&show_split_view=true' +
- '&nonce=' + revisions.model.settings.nonce +
- '&single_revision_id=' + this.id +
- '&compare_to=' + this.get( 'previousID' ) +
- '&post_id=' + revisions.model.settings.post_id;
- } else {
- return this.collection.url() + '&single_revision_id=' + this.id;
- }
-
+ // Generate the options to be passed to the template.
+ prepare: function() {
+ return _.extend({ fields: this.model.fields.toJSON() }, this.options );
}
});
- /**
- * wp.revisions.Revisions
- */
- Revisions = revisions.Revisions = Backbone.Collection.extend({
- model: Revision,
-
- initialize: function( models, options ) {
- this.options = _.defaults( options || {}, {
- 'compareTo': revisions.model.settings.post_id,
- 'post_id': revisions.model.settings.post_id,
- 'showAutosaves': true,
- 'showSplitView': true,
- 'rightHandleAt': 0,
- 'leftHandleAt': 0,
- 'nonce': revisions.model.settings.nonce
- });
- },
-
- url: function() {
- return ajaxurl +
- '?action=revisions-data' +
- '&compare_to=' + this.options.compareTo + // revision are we comparing to
- '&post_id=' + this.options.post_id + // the post id
- '&show_autosaves=' + this.options.showAutosaves + // show or hide autosaves
- '&show_split_view=' + this.options.showSplitView + // show in split view or single column view
- '&right_handle_at=' + this.options.rightHandleAt + // mark point for comparison list
- '&left_handle_at=' + this.options.leftHandleAt + // mark point for comparison list
- '&nonce=' + this.options.nonce;
- },
-
- reload: function( options ) {
- this.options = _.defaults( options.options || {}, this.options );
-
- this.fetch({
- success: options.success || null,
- error: options.error || null
- });
- }
-
- } );
-
- $( wp.revisions );
+ // Initialize the revisions UI.
+ revisions.init = function() {
+ revisions.view.frame = new revisions.view.Frame({
+ collection: new revisions.model.Revisions( revisions.settings.revisionData )
+ }).render();
+ };
+ $( revisions.init );
}(jQuery));
diff --git a/wp-admin/revision.php b/wp-admin/revision.php
index ec7ee51ca0..7c390989c6 100644
--- a/wp-admin/revision.php
+++ b/wp-admin/revision.php
@@ -8,6 +8,12 @@
/** WordPress Administration Bootstrap */
require_once('./admin.php');
+
+require ABSPATH . 'wp-admin/includes/revision.php';
+
+// wp_get_revision_ui_diff( $post, $compare_from, $compare_to )
+// wp_prepare_revisions_for_js( $post )
+
wp_reset_vars( array( 'revision', 'action' ) );
$revision_id = absint( $revision );
@@ -21,7 +27,6 @@ case 'restore' :
if ( ! current_user_can( 'edit_post', $revision->post_parent ) )
break;
-
if ( ! $post = get_post( $revision->post_parent ) )
break;
@@ -77,15 +82,7 @@ else
$parent_file = $submenu_file = 'edit.php';
wp_enqueue_script( 'revisions' );
-
-
-$settings = array(
- 'post_id' => $post->ID,
- 'nonce' => wp_create_nonce( 'revisions-ajax-nonce' ),
- 'revision_id' => $revision_id
-);
-
-wp_localize_script( 'revisions', 'wpRevisionsSettings', $settings );
+wp_localize_script( 'revisions', '_wpRevisionsSettings', wp_prepare_revisions_for_js( $post, $revision_id ) );
/* Revisions Help Tab */
@@ -114,24 +111,73 @@ require_once( './admin-header.php' );
+
+
+
+
+
+
+
+
+
-