<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[24520] trunk: Cleanup of the revisions screen, both on the PHP API side, and the JS.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://core.trac.wordpress.org/changeset/24520">24520</a></dd>
<dt>Author</dt> <dd>markjaquith</dd>
<dt>Date</dt> <dd>2013-06-26 21:06:50 +0000 (Wed, 26 Jun 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Cleanup of the revisions screen, both on the PHP API side, and the JS.

* Much simpler PHP API
* Cleaner and more Backbone-y JS API
* Consequently, does batch queries; this now scales up to hundreds of revisions

Currently missing, but much easier considering the cleaned up base:

* Compare two mode
* RTL

props koopersmith, nacin, adamsilverstein, ocean90. see <a href="http://core.trac.wordpress.org/ticket/24425">#24425</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkwpadminadminajaxphp">trunk/wp-admin/admin-ajax.php</a></li>
<li><a href="#trunkwpadmincsswpadmincss">trunk/wp-admin/css/wp-admin.css</a></li>
<li><a href="#trunkwpadminincludesajaxactionsphp">trunk/wp-admin/includes/ajax-actions.php</a></li>
<li><a href="#trunkwpadminjsrevisionsjs">trunk/wp-admin/js/revisions.js</a></li>
<li><a href="#trunkwpadminrevisionphp">trunk/wp-admin/revision.php</a></li>
<li><a href="#trunkwpincludesrevisionphp">trunk/wp-includes/revision.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkwpadminincludesrevisionphp">trunk/wp-admin/includes/revision.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkwpadminadminajaxphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/admin-ajax.php (24519 => 24520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/admin-ajax.php    2013-06-26 20:42:01 UTC (rev 24519)
+++ trunk/wp-admin/admin-ajax.php       2013-06-26 21:06:50 UTC (rev 24520)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx"> 
</span><span class="cx"> $core_actions_get = array(
</span><span class="cx">  'fetch-list', 'ajax-tag-search', 'wp-compression-test', 'imgedit-preview', 'oembed-cache',
</span><del>-       'autocomplete-user', 'dashboard-widgets', 'logged-in', 'revisions-data'
</del><ins>+        'autocomplete-user', 'dashboard-widgets', 'logged-in',
</ins><span class="cx"> );
</span><span class="cx"> 
</span><span class="cx"> $core_actions_post = array(
</span><span class="lines">@@ -56,7 +56,7 @@
</span><span class="cx">  'save-widget', 'set-post-thumbnail', 'date_format', 'time_format', 'wp-fullscreen-save-post',
</span><span class="cx">  'wp-remove-post-lock', 'dismiss-wp-pointer', 'upload-attachment', 'get-attachment',
</span><span class="cx">  'query-attachments', 'save-attachment', 'save-attachment-compat', 'send-link-to-editor',
</span><del>-       'send-attachment-to-editor', 'save-attachment-order', 'heartbeat',
</del><ins>+        'send-attachment-to-editor', 'save-attachment-order', 'heartbeat', 'get-revision-diffs',
</ins><span class="cx"> );
</span><span class="cx"> 
</span><span class="cx"> // Register core Ajax calls.
</span></span></pre></div>
<a id="trunkwpadmincsswpadmincss"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/css/wp-admin.css (24519 => 24520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/css/wp-admin.css  2013-06-26 20:42:01 UTC (rev 24519)
+++ trunk/wp-admin/css/wp-admin.css     2013-06-26 21:06:50 UTC (rev 24520)
</span><span class="lines">@@ -3481,7 +3481,49 @@
</span><span class="cx"> /*------------------------------------------------------------------------------
</span><span class="cx">   11.2 - Post Revisions
</span><span class="cx"> ------------------------------------------------------------------------------*/
</span><ins>+.revisions .spinner {
+       float: none;
+       margin: 100px auto;
+}
</ins><span class="cx"> 
</span><ins>+.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;
+}
+
</ins><span class="cx"> /* Revision meta box */
</span><span class="cx"> .post-revisions li img,
</span><span class="cx"> #revisions-meta-restored img {
</span><span class="lines">@@ -3527,13 +3569,6 @@
</span><span class="cx">  position: relative;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-#toggle-revision-compare-mode {
-       position: absolute;
-       top: 0;
-       right: 0;
-       padding: 9px 9px 0 0;
-}
-
</del><span class="cx"> #loading-status {
</span><span class="cx">  display: none;
</span><span class="cx">  position: absolute;
</span><span class="lines">@@ -3551,24 +3586,6 @@
</span><span class="cx">  padding: 20px 0;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-#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;
-}
-
</del><span class="cx"> .comparetwo #diff-slider {
</span><span class="cx">  width: 95%;
</span><span class="cx"> }
</span><span class="lines">@@ -3588,7 +3605,7 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #diff-title-to strong {
</span><del>-       display: none;
</del><ins>+        display: inline;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .comparing-two-revisions #diff-title-to strong {
</span><span class="lines">@@ -3605,6 +3622,7 @@
</span><span class="cx">  -webkit-border-radius: 3px;
</span><span class="cx">  border-radius: 3px;
</span><span class="cx">  padding: 5px 200px 5px 5px;
</span><ins>+       clear: both;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .diff-header {
</span></span></pre></div>
<a id="trunkwpadminincludesajaxactionsphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/includes/ajax-actions.php (24519 => 24520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/includes/ajax-actions.php 2013-06-26 20:42:01 UTC (rev 24519)
+++ trunk/wp-admin/includes/ajax-actions.php    2013-06-26 21:06:50 UTC (rev 24520)
</span><span class="lines">@@ -2082,219 +2082,31 @@
</span><span class="cx">  wp_send_json($response);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-function wp_ajax_revisions_data() {
-       check_ajax_referer( 'revisions-ajax-nonce', 'nonce' );
</del><ins>+function wp_ajax_get_revision_diffs() {
+       require ABSPATH . 'wp-admin/includes/revision.php';
</ins><span class="cx"> 
</span><del>-       $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;
</del><ins>+        // check_ajax_referer( 'revisions-ajax-nonce', 'nonce' );
</ins><span class="cx"> 
</span><del>-       $all_the_revisions = array();
-       if ( ! $post_id )
-               $post_id = $compare_to;
</del><ins>+        if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
+               wp_send_json_error();
</ins><span class="cx"> 
</span><del>-       if ( ! current_user_can( 'read_post', $post_id ) )
-               continue;
</del><ins>+        if ( ! current_user_can( 'read_post', $post->ID ) )
+               wp_send_json_error();
</ins><span class="cx"> 
</span><del>-       if ( ! $revisions = wp_get_post_revisions( $post_id ) )
-               return;
</del><ins>+        // Really just pre-loading the cache here.
+       if ( ! $revisions = wp_get_post_revisions( $post->ID ) )
+               wp_send_json_error();
</ins><span class="cx"> 
</span><del>-       $left_revision = get_post( $compare_to );
</del><ins>+        $return = array();
+       @set_time_limit( count( $_REQUEST['compare'] ) );
</ins><span class="cx"> 
</span><del>-       // single model fetch mode
-       // return the diff of a single revision comparison
-       if ( $single_revision_id ) {
-               $right_revision = get_post( $single_revision_id );
</del><ins>+        foreach ( $_REQUEST['compare'] as $compare_key ) {
+               list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
</ins><span class="cx"> 
</span><del>-               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( '<div class="diff-label">%s</div>', $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
</del><ins>+                $return[] = array(
+                       'id' => $compare_key,
+                       'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
</ins><span class="cx">           );
</span><del>-
-               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 <strong>%s</strong><br />%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();
</del><ins>+        }
+       wp_send_json_success( $return );
</ins><span class="cx"> }
</span></span></pre></div>
<a id="trunkwpadminincludesrevisionphp"></a>
<div class="addfile"><h4>Added: trunk/wp-admin/includes/revision.php (0 => 24520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/includes/revision.php                             (rev 0)
+++ trunk/wp-admin/includes/revision.php        2013-06-26 21:06:50 UTC (rev 24520)
</span><span class="lines">@@ -0,0 +1,100 @@
</span><ins>+<?php
+
+function wp_get_revision_ui_diff( $post, $compare_from, $compare_to ) {
+       if ( ! $post = get_post( $post ) )
+               return false;
+
+       if ( $compare_from ) {
+               if ( ! $compare_from = get_post( $compare_from ) )
+                       return false;
+       } else {
+               // If we're dealing with the first revision...
+               $compare_from = false;
+       }
+
+       if ( ! $compare_to = get_post( $compare_to ) )
+               return false;
+
+       // If comparing revisions, make sure we're dealing with the right post parent.
+       if ( $compare_from && $compare_from->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 = "<table class='diff'><col class='ltype' /><col class='content' /><col class='ltype' /><col class='content' /><tbody><tr>";
+                       $diff .= '<td>' . esc_html( $compare_from->post_title ) . '</td><td></td><td>' . esc_html( $compare_to->post_title ) . '</td>';
+                       $diff .= '</tr></tbody>';
+                       $diff .= '</table>';
+               }
+
+               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,
+       );
+}
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkwpadminjsrevisionsjs"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/js/revisions.js (24519 => 24520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/js/revisions.js   2013-06-26 20:42:01 UTC (rev 24519)
+++ trunk/wp-admin/js/revisions.js      2013-06-26 21:06:50 UTC (rev 24520)
</span><span class="lines">@@ -1,283 +1,207 @@
</span><span class="cx"> window.wp = window.wp || {};
</span><span class="cx"> 
</span><span class="cx"> (function($) {
</span><del>-       var Revision, Revisions, Diff, revisions;
</del><ins>+        var revisions;
</ins><span class="cx"> 
</span><del>-       revisions = wp.revisions = function() {
-               Diff = revisions.Diff = new Diff();
-       };
</del><ins>+        revisions = wp.revisions = { model: {}, view: {}, controller: {} };
</ins><span class="cx"> 
</span><del>-       _.extend( revisions, { model: {}, view: {}, controller: {} } );
-
</del><span class="cx">   // Link settings.
</span><del>-       revisions.model.settings = typeof wpRevisionsSettings === 'undefined' ? {} : wpRevisionsSettings;
</del><ins>+        revisions.settings = typeof _wpRevisionsSettings === 'undefined' ? {} : _wpRevisionsSettings;
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">  /**
</span><span class="cx">   * ========================================================================
</span><del>-        * CONTROLLERS
</del><ins>+         * MODELS
</ins><span class="cx">    * ========================================================================
</span><span class="cx">   */
</span><ins>+       revisions.model.Slider = Backbone.Model.extend({
+               defaults: {
+                       value: 0,
+                       min: 0,
+                       max: 1,
+                       step: 1
+               }
+       });
</ins><span class="cx"> 
</span><del>-       /**
-        * 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
</del><ins>+        revisions.model.Revision = Backbone.Model.extend({});
</ins><span class="cx"> 
</span><del>-               constructor: function() {
-                       var self    = this;
-                       this.slider = new revisions.view.Slider();
</del><ins>+        revisions.model.Revisions = Backbone.Collection.extend({
+               model: revisions.model.Revision,
</ins><span class="cx"> 
</span><del>-                       if ( null === this.revisions ) {
-                               this.revisions = new Revisions(); // set up collection
-                               this.startRightModelLoading();
-
-                               this.revisions.fetch({ // load revision data
-                                       success: function() {
-                                               self.stopRightModelLoading();
-                                               self.completeApplicationSetup();
-                                       }
-                               });
-                       }
</del><ins>+                comparator: function( revision ) {
+                       return revision.id;
</ins><span class="cx">           },
</span><ins>+       });
</ins><span class="cx"> 
</span><del>-               loadDiffs: function( models ) {
-                       var self = this,
-                               revisionsToLoad = models.where( { completed: false } ),
-                               delay = 0,
-                               totalChanges;
</del><ins>+        revisions.model.Field = Backbone.Model.extend({});
</ins><span class="cx"> 
</span><del>-                       // 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;
-                       });
</del><ins>+        revisions.model.Fields = Backbone.Collection.extend({
+               model: revisions.model.Field
+       });
</ins><span class="cx"> 
</span><del>-                       _.each( revisionsToLoad, function( revision ) {
-                                       _.delay( function() {
-                                               revision.fetch( {
-                                                       update: true,
-                                                       add: false,
-                                                       remove: false,
-                                                       success: function( model ) {
-                                                               model.set( 'completed', true );
</del><ins>+        revisions.model.Diff = Backbone.Model.extend({
+               initialize: function(attributes, options) {
+                       var fields = this.get('fields');
+                       this.unset('fields');
</ins><span class="cx"> 
</span><del>-                                                               // stop spinner when all models are loaded
-                                                               if ( 0 === models.where( { completed: false } ).length )
-                                                                       self.stopModelLoadingSpinner();
</del><ins>+                        this.fields = new revisions.model.Fields( fields );
+               }
+       });
</ins><span class="cx"> 
</span><del>-                                                               totalChanges = model.get( 'linesAdded' ) + model.get( 'linesDeleted' ),
-                                                                       scopeOfChanges = 'vsmall';
</del><ins>+        revisions.model.Diffs = Backbone.Collection.extend({
+               initialize: function(models, options) {
+                       this.revisions = options.revisions;
+                       this.requests  = {};
+               },
</ins><span class="cx"> 
</span><del>-                                                               // 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
</del><ins>+                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 ) );
</ins><span class="cx">                           }
</span><del>-                       );
-               },
</del><span class="cx"> 
</span><del>-               startLeftModelLoading: function() {
-                       this.leftModelLoading = true;
-                       $('#revision-diff-container').addClass('left-model-loading');
-               },
</del><ins>+                                request.done( _.bind( function() {
+                                       deferred.resolveWith( context, [ this.get( id ) ] );
+                               }, this ) );
+                       }
</ins><span class="cx"> 
</span><del>-               stopLeftModelLoading: function() {
-                       this.leftModelLoading = false;
</del><ins>+                        return deferred.promise();
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               startRightModelLoading: function() {
-                       this.rightModelLoading = true;
-                       $('#revision-diff-container').addClass('right-model-loading');
</del><ins>+                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 );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               stopRightModelLoading: function() {
-                       this.rightModelLoading = false;
</del><ins>+                load: function( comparisons ) {
+                       // Our collection should only ever grow, never shrink, so remove: false
+                       return this.fetch({ data: { compare: comparisons }, remove: false });
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               stopModelLoadingSpinner: function() {
-                       $('#revision-diff-container').removeClass('right-model-loading');
-                       $('#revision-diff-container').removeClass('left-model-loading');
-               },
</del><ins>+/**/
+               loadLast: function( num ) {
+                       num     = num || 1;
+                       var ids = this.getProximalDiffIds();
+                       ids     = _.last( ids, num );
</ins><span class="cx"> 
</span><del>-               reloadModel: function() {
-                       if ( this.singleRevision ) {
-                               this.reloadModelSingle();
-                       } else {
-                               this.reloadLeftRight();
</del><ins>+                        if ( ids.length ) {
+                               return this.loadNew( ids );
</ins><span class="cx">                   }
</span><span class="cx">          },
</span><span class="cx"> 
</span><del>-               // load the models for the single handle mode
-               reloadModelSingle: function() {
-                       var self = this;
</del><ins>+                loadLastUnloaded: function( num ) {
+                       num     = num || 1;
+                       var ids = this.getUnloadedProximalDiffIds();
+                       ids     = _.last( ids, num );
</ins><span class="cx"> 
</span><del>-                       self.startRightModelLoading();
</del><ins>+                        if ( ids.length ) {
+                               return this.loadNew( ids );
+                       }
+               },
</ins><span class="cx"> 
</span><del>-                       self.revisions.reload({
-                               options: {
-                               'showAutosaves': self.autosaves,
-                               'showSplitView': self.showSplitView
-                               },
</del><ins>+                getProximalDiffIds: function() {
+                       var previous = 0, ids = [];
+                       this.revisions.each( _.bind( function(revision) {
+                               ids.push( previous + ':' + revision.id );
+                               previous = revision.id;
+                       }, this ) );
+                       return ids;
+               },
</ins><span class="cx"> 
</span><del>-                               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);
-                               },
</del><ins>+                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 );
+               },
</ins><span class="cx"> 
</span><del>-                               error: function() {
-                                       self.stopRightModelLoading();
-                               }
-                       });
</del><ins>+                loadAllBy: function( chunkSize ) {
+                       chunkSize    = chunkSize || 20;
+                       var unloaded = this.getUnloadedProximalDiffIds();
+                       if ( unloaded.length ) {
+                               return this.loadLastUnloaded( chunkSize ).always( _.bind( function() {
+                                       this.loadAllBy( chunkSize );
+                               }, this ) );
+                       }
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               // 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
-                       });
</del><ins>+                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
+                               });
</ins><span class="cx"> 
</span><del>-                       self.leftHandleRevisions.fetch({
-                               success: function(){
-                                       self.stopLeftModelLoading();
-                                       self.loadDiffs( self.leftHandleRevisions );
-                                       self.tickmarkView.model = self.leftHandleRevisions;
-                                       self.slider.refresh({
-                                               'max': self.revisions.length
</del><ins>+                                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;
</ins><span class="cx">                                   });
</span><del>-                                       // ensure right handle not beyond length
-                                       if ( self.rightDiff > self.revisions.length )
-                                               self.rightDiff = self.revisions.length;
-                                       },
-
-                               error: function() {
-                                       self.stopLeftModelLoading();
</del><span class="cx">                           }
</span><del>-                       });
-               },
</del><span class="cx"> 
</span><del>-               // 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
-                       });
</del><ins>+                                // When the request completes, clear the stored request.
+                               deferred.always( function() {
+                                       if ( options.data.compare ) {
+                                               _.each( options.data.compare, function( id ) {
+                                                       delete requests[ id ];
+                                               });
+                                       }
+                               });
</ins><span class="cx"> 
</span><del>-                       self.rightHandleRevisions.fetch({
-                               success: function(){
-                                       self.stopRightModelLoading();
-                                       self.loadDiffs( self.rightHandleRevisions );
-                                       self.tickmarkView.model = self.rightHandleRevisions;
-                                       self.slider.refresh({
-                                               'max': self.revisions.length
-                                       }, true);
-                               },
</del><ins>+                                return deferred;
</ins><span class="cx"> 
</span><del>-                               error: function( response ) {
-                                       self.stopRightModelLoading();
-                               }
-                       });
</del><ins>+                        // Otherwise, fall back to `Backbone.sync()`.
+                       } else {
+                               return Backbone.Model.prototype.sync.apply( this, arguments );
+                       }
+               }
+       });
</ins><span class="cx"> 
</span><del>-               },
</del><span class="cx"> 
</span><del>-               /**
-                * reloadLeftRight reload models for both the left and right handles
-                */
-               reloadLeftRight: function() {
-                       this.startRightModelLoading();
-                       this.startLeftModelLoading();
-                       this.reloadLeft();
-                       this.reloadRight();
-               },
</del><ins>+        revisions.model.FrameState = Backbone.Model.extend({
+               initialize: function( attributes, options ) {
+                       this.revisions = options.revisions;
+                       this.diffs     = new revisions.model.Diffs( [], {revisions: this.revisions} );
</ins><span class="cx"> 
</span><del>-               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 );
</del><ins>+                        this.listenTo( this, 'change:from change:to', this.updateDiffId );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               /**
-                * 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
</del><ins>+                updateDiffId: function() {
+                       var from = this.get( 'from' );
+                       var to   = this.get( 'to' );
+                       this.set( 'diffId', (from ? from.id : '0') + ':' + to.id );
</ins><span class="cx">           }
</span><span class="cx">  });
</span><span class="cx"> 
</span><span class="lines">@@ -288,534 +212,286 @@
</span><span class="cx">   * ========================================================================
</span><span class="cx">   */
</span><span class="cx"> 
</span><del>-       /**
-        * wp.revisions.view.Slider
-        *
-        * The slider
-        */
-       revisions.view.Slider = Backbone.View.extend({
-               el: $( '#diff-slider' ),
-               singleRevision: true,
</del><ins>+        // The frame view. This contains the entire page.
+       revisions.view.Frame = wp.Backbone.View.extend({
+               tagName: 'div',
+               className: 'revisions',
+               template: wp.template('revisions-frame'),
</ins><span class="cx"> 
</span><del>-               initialize: function( options ) {
-                       this.options = _.defaults( options || {}, {
-                               value: 0,
-                               min: 0,
-                               max: 1,
-                               step: 1
</del><ins>+                initialize: function() {
+                       this.model = new revisions.model.FrameState({}, {
+                               revisions: this.collection
</ins><span class="cx">                   });
</span><del>-               },
</del><span class="cx"> 
</span><del>-               /**
-                * 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;
</del><ins>+                        this.listenTo( this.model, 'change:diffId', this.updateDiff );
</ins><span class="cx"> 
</span><del>-                               if ( $( ui.handle ).hasClass( 'left-handle' ) ) {
-                                       // Left handler
-                                       if ( Diff.leftModelLoading ) // left model still loading, prevent sliding left handle
-                                               return false;
</del><ins>+                        this.views.set( '.revisions-control-frame', new revisions.view.Controls({
+                               model: this.model
+                       }) );
</ins><span class="cx"> 
</span><del>-                                       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;
</del><ins>+                        if ( this.model.revisions.length ) {
+                               var last = this.model.revisions.last(2);
+                               var attributes = { to: last.pop() };
</ins><span class="cx"> 
</span><del>-                                       Diff.rightDiff = isRtl ? ui.values[0] : ui.values[1]; // handles are reversed in RTL mode
-                               }
</del><ins>+                                if ( last.length )
+                                       attributes.from = last.pop();
</ins><span class="cx"> 
</span><del>-                               Diff.revisionView.render();
</del><ins>+                                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 ) );
</ins><span class="cx">                   }
</span><span class="cx">          },
</span><span class="cx"> 
</span><del>-               /**
-                * 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;
</del><ins>+                render: function() {
+                       wp.Backbone.View.prototype.render.apply( this, arguments );
</ins><span class="cx"> 
</span><del>-                       if ( $( ui.handle ).hasClass( 'left-handle' ) ) {
-                               // Left handler
-                               if ( Diff.leftModelLoading ) // left model still loading, prevent sliding left handle
-                                       return false;
</del><ins>+                        $('#wpbody-content .wrap').append( this.el );
+                       this.views.ready();
</ins><span class="cx"> 
</span><del>-                               Diff.revisionView.draggingLeft = true;
</del><ins>+                        return this;
+               },
</ins><span class="cx"> 
</span><del>-                               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();
-                               }
</del><ins>+                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
+                               }));
+                       });
+               }
+       });
</ins><span class="cx"> 
</span><del>-                               Diff.leftDiffStart = isRtl ? ui.values[1] : ui.values[0]; // in RTL mode the 'left handle' is the second in the slider, 'right' is first
</del><ins>+        // 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',
</ins><span class="cx"> 
</span><del>-                       } else {
-                               // Right handler
-                               if ( Diff.rightModelLoading || 0 === Diff.rightHandleRevisions.length) // right model still loading, prevent sliding right handle
-                                       return false;
</del><ins>+                initialize: function() {
+                       // Add the button view
+                       this.views.add( new revisions.view.Buttons({ 
+                               model: this.model
+                       }));
</ins><span class="cx"> 
</span><del>-                               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();
-                               }
</del><ins>+                        // Add the Slider view
+                       this.views.add( new revisions.view.Slider({
+                               model: this.model
+                       }) );
</ins><span class="cx"> 
</span><del>-                               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
-                       }
-               },
</del><ins>+                        // Add the Meta view
+                       this.views.add( new revisions.view.Meta({
+                               model: this.model
+                       }) );
+               }
+       });
</ins><span class="cx"> 
</span><del>-               /**
-                * 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;
</del><ins>+        // 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'),
</ins><span class="cx"> 
</span><del>-                       // 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();
-                       }
</del><ins>+                initialize: function() {
+                       this.listenTo( this.model, 'change:diffId', this.updateMeta );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               addTooltip: function( handle, message ) {
-                       handle.find( '.ui-slider-tooltip' ).html( message );
</del><ins>+                events: {
+                       'click #restore-revision': 'restoreRevision'
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               width: function() {
-                       return $( '#diff-slider' ).width();
</del><ins>+                restoreRevision: function() {
+                       var restoreUrl    = this.model.get('to').attributes.restoreUrl.replace(/&amp;/g, '&');
+                       document.location = restoreUrl;
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               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 )
-                       } );
-
-                       // Set options
-                       this.refresh( this.options );
</del><ins>+                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)
+                       }
</ins><span class="cx">           }
</span><span class="cx">  });
</span><span class="cx"> 
</span><del>-       /**
-        * wp.revisions.view.Tickmarks
-        *
-        * The slider tickmarks.
-        */
-       revisions.view.Tickmarks = Backbone.View.extend({
-               el: $('#diff-slider-ticks'),
-               template: wp.template('revision-ticks'),
-               model: Revision,
</del><span class="cx"> 
</span><del>-               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();
</del><ins>+        // 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'),
</ins><span class="cx"> 
</span><del>-                       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('<div class="arrow"></div>');
-                                       },
-                                       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
-                                       } );
-                       } );
</del><ins>+                initialize: function() {
+                       this.$el.html( this.template() )
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               // 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
</del><span class="cx">           events: {
</span><del>-                       'click #next':     ! isRtl ? 'nextRevision' : 'previousRevision',
-                       'click #previous': ! isRtl ? 'previousRevision' : 'nextRevision'
</del><ins>+                        'click #next': 'nextRevision',
+                       'click #previous': 'previousRevision'
</ins><span class="cx">           },
</span><ins>+               
+               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 });
</ins><span class="cx"> 
</span><del>-               render: function() {
-                       var modelcount;
-                       this.$el.html( this.template );
</del><ins>+                        this.model.set( attributes );
+               },
</ins><span class="cx"> 
</span><del>-                       modelcount = Diff.revisions.length;
</del><ins>+                nextRevision: function() {
+                       var toIndex = this.model.revisions.indexOf( this.model.get( 'to' ) );
+                       toIndex     = isRtl ? toIndex - 1 : toIndex + 1;
+                       this.gotoModel( toIndex );
+               },
+               
+               previousRevision: function() {
+                       var toIndex = this.model.revisions.indexOf( this.model.get('to') );
+                       toIndex     = isRtl ? toIndex + 1 : toIndex - 1;
+                       this.gotoModel( toIndex );
+               },
</ins><span class="cx"> 
</span><del>-                       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' );
-
-                       }
-
-                       return this;
</del><ins>+                ready: function() {
+                       this.listenTo( this.model, 'change:diffId', this.disabledButtonCheck );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               // go to the next revision
-               nextRevision: function() {
-                       if ( Diff.rightDiff < this.model.length ) // unless at right boundry
-                               Diff.rightDiff = Diff.rightDiff + 1 ;
</del><ins>+                // 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' ) );
</ins><span class="cx"> 
</span><del>-                       Diff.revisionView.render();
</del><ins>+                        // Disable "Next" button if you're on the last node
+                       if ( maxVal === val )
+                               next.prop( 'disabled', true );
+                       else
+                               next.prop( 'disabled', false );
</ins><span class="cx"> 
</span><del>-                       Diff.slider.refresh({
-                               value: Diff.rightDiff - 1
-                       }, true );
</del><ins>+                        // Disable "Previous" button if you're on the first node
+                       if ( minVal === val )
+                               previous.prop( 'disabled', true );
+                       else
+                               previous.prop( 'disabled', false );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               // go to the previous revision
-               previousRevision: function() {
-                       if ( Diff.rightDiff > 1 ) // unless at left boundry
-                               Diff.rightDiff = Diff.rightDiff - 1 ;
</del><span class="cx"> 
</span><del>-                       Diff.revisionView.render();
-
-                       Diff.slider.refresh({
-                               value: Diff.rightDiff - 1
-                       }, true );
-               }
</del><span class="cx">   });
</span><span class="cx"> 
</span><del>-       /**
-        * 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,
</del><ins>+        // 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',
</ins><span class="cx"> 
</span><del>-               // the compare two button is in this view, add the interaction here
-               events: {
-                       'click #compare-two-revisions': 'compareTwo',
-                       'click #restore-revision':      'restore'
-               },
</del><ins>+                initialize: function() {
+                       _.bindAll( this, 'start', 'slide', 'stop' );
</ins><span class="cx"> 
</span><del>-               // render the revisions
-               render: function() {
-                       var addHtml = '', thediff;
</del><ins>+                        // 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;
</ins><span class="cx"> 
</span><del>-                       // 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 );
</del><ins>+                        // Find the initially selected revision
+                       var initiallySelectedRevisionIndex =
+                               this.model.revisions.indexOf( 
+                                       this.model.revisions.findWhere(  { id: Number( revisions.settings.selectedRevision ) } ) );
</ins><span class="cx"> 
</span><del>-                       if ( this.model.length < 2 ) {
-                               $( '#diff-slider' ).hide(); // don't allow compare two if fewer than three revisions
-                               $( '.diff-slider-ticks-wrapper' ).hide();
-                       }
</del><ins>+                        this.settings = new revisions.model.Slider({
+                               max:   latestRevisionIndex,
+                               value: initiallySelectedRevisionIndex,
+                               start: this.start,
+                               slide: this.slide,
+                               stop:  this.stop
+                       });
+               },
</ins><span class="cx"> 
</span><del>-                       this.toggleCompareTwoCheckbox();
</del><ins>+                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 );
</ins><span class="cx"> 
</span><del>-                       // hide the restore button when on the last sport/current post data
-                       $( '#restore-revision' ).toggle( ! Diff.revisions.at( Diff.rightDiff - 1 ).get( 'isCurrent' ) );
</del><ins>+                        // Listen for changes in the diffId
+                       this.listenTo( this.model, 'change:diffId', this.diffIdChanged );
</ins><span class="cx"> 
</span><del>-                       return this;
</del><span class="cx">           },
</span><span class="cx"> 
</span><del>-               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 );
</del><ins>+                diffIdChanged: function() {
+                       // Reset the view settings when diffId is changed
+                       this.settings.set( { 'value': this.model.revisions.indexOf( this.model.get( 'to' ) ) } );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               // turn on/off the compare two mode
-               compareTwo: function() {
-                       if ( $( '#compare-two-revisions' ).is( ':checked' ) ) { // compare 2 mode
-                               Diff.singleRevision = false ;
</del><ins>+                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();
</ins><span class="cx"> 
</span><del>-                               // 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;
</del><ins>+                                // 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
</ins><span class="cx">                           } else {
</span><del>-                                       if ( 1 === Diff.rightDiff ) // make sure right handle not in leftmost slot
-                                               Diff.rightDiff = 2;
</del><ins>+                                        $( ui.handle ).css( 'left', e.clientX - sliderLeft ); // Mouse in slider
</ins><span class="cx">                           }
</span><ins>+                       } ); // End mousemove 
+               },
</ins><span class="cx"> 
</span><del>-                               Diff.revisionView.draggingLeft = false;
</del><ins>+                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
+                       };
</ins><span class="cx"> 
</span><del>-                               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;
</del><ins>+                        // 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 });
</ins><span class="cx"> 
</span><del>-                       } else { // compare one mode
-                               Diff.singleRevision = true;
-                               Diff.revisionView.draggingLeft = false;
-                               Diff.reloadModelSingle();
-                       }
-                       Diff.revisionsInteractions.render();
-                       Diff.tickmarkView.render();
</del><ins>+                        this.model.set( attributes );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><del>-               restore: function() {
-                       document.location = $( '#restore-revision' ).data( 'restoreLink' );
</del><ins>+                stop: function( event, ui ) {
+                       $( window ).unbind( 'mousemove' ); // Stop tracking the mouse
+                       // Reset settings pops handle back to the step position
+                       this.settings.trigger( 'change' );
</ins><span class="cx">           }
</span><span class="cx">  });
</span><span class="cx"> 
</span><ins>+       // 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'),
</ins><span class="cx"> 
</span><del>-       /**
-        * ========================================================================
-        * MODELS
-        * ========================================================================
-        */
-
-       /**
-        * wp.revisions.Revision
-        */
-       Revision = revisions.model.Revision = Backbone.Model.extend({
-               idAttribute: 'ID',
-
-               defaults: {
-                       ID: 0,
-                       titleTo: '',
-                       titleTooltip: '',
-                       titleFrom: '',
-                       diff: '<div class="diff-loading"><div class="spinner"></div></div>',
-                       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;
-                       }
-
</del><ins>+                // Generate the options to be passed to the template.
+               prepare: function() {
+                       return _.extend({ fields: this.model.fields.toJSON() }, this.options );
</ins><span class="cx">           }
</span><span class="cx">  });
</span><span class="cx"> 
</span><del>-       /**
-        * wp.revisions.Revisions
-        */
-       Revisions = revisions.Revisions = Backbone.Collection.extend({
-               model: Revision,
</del><ins>+        // Initialize the revisions UI.
+       revisions.init = function() {
+               revisions.view.frame = new revisions.view.Frame({
+                       collection: new revisions.model.Revisions( revisions.settings.revisionData )
+               }).render();
+       };
</ins><span class="cx"> 
</span><del>-               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 );
-
</del><ins>+        $( revisions.init );
</ins><span class="cx"> }(jQuery));
</span></span></pre></div>
<a id="trunkwpadminrevisionphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/revision.php (24519 => 24520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/revision.php      2013-06-26 20:42:01 UTC (rev 24519)
+++ trunk/wp-admin/revision.php 2013-06-26 21:06:50 UTC (rev 24520)
</span><span class="lines">@@ -8,6 +8,12 @@
</span><span class="cx"> 
</span><span class="cx"> /** WordPress Administration Bootstrap */
</span><span class="cx"> require_once('./admin.php');
</span><ins>+
+require ABSPATH . 'wp-admin/includes/revision.php';
+
+// wp_get_revision_ui_diff( $post, $compare_from, $compare_to )
+// wp_prepare_revisions_for_js( $post )
+
</ins><span class="cx"> wp_reset_vars( array( 'revision', 'action' ) );
</span><span class="cx"> 
</span><span class="cx"> $revision_id = absint( $revision );
</span><span class="lines">@@ -21,7 +27,6 @@
</span><span class="cx">  if ( ! current_user_can( 'edit_post', $revision->post_parent ) )
</span><span class="cx">          break;
</span><span class="cx"> 
</span><del>-
</del><span class="cx">   if ( ! $post = get_post( $revision->post_parent ) )
</span><span class="cx">          break;
</span><span class="cx"> 
</span><span class="lines">@@ -77,16 +82,8 @@
</span><span class="cx">  $parent_file = $submenu_file = 'edit.php';
</span><span class="cx"> 
</span><span class="cx"> wp_enqueue_script( 'revisions' );
</span><ins>+wp_localize_script( 'revisions', '_wpRevisionsSettings', wp_prepare_revisions_for_js( $post, $revision_id ) );
</ins><span class="cx"> 
</span><del>-
-$settings = array(
-       'post_id'     => $post->ID,
-       'nonce'       => wp_create_nonce( 'revisions-ajax-nonce' ),
-       'revision_id' => $revision_id
-);
-
-wp_localize_script( 'revisions', 'wpRevisionsSettings', $settings );
-
</del><span class="cx"> /* Revisions Help Tab */
</span><span class="cx"> 
</span><span class="cx"> $revisions_overview  = '<p>' . __( 'This screen is used for managing your content revisions.' ) . '</p>';
</span><span class="lines">@@ -114,24 +111,73 @@
</span><span class="cx"> 
</span><span class="cx"> <div class="wrap">
</span><span class="cx">  <?php screen_icon(); ?>
</span><del>-       <div id="revision-diff-container" class="current-version right-model-loading">
-               <h2 class="long-header"><?php echo $h2; ?></h2>
</del><ins>+        <h2 class="long-header"><?php echo $h2; ?></h2>
+</div>
</ins><span class="cx"> 
</span><del>-               <div id="loading-status" class="updated message">
-                       <p><span class="spinner" ></span></p>
</del><ins>+<script id="tmpl-revisions-frame" type="text/html">
+       <span class="spinner"></span>
+       <div class="revisions-control-frame"></div>
+       <div class="revisions-diff-frame"></div>
+</script>
+
+<script id="tmpl-revisions-controls" type="text/html">
+
+       <div class="revision-toggle-compare-mode">
+               <label>
+                       <input type="checkbox" class="compare-two-revisions" />
+                       <?php esc_attr_e( 'Compare two revisions' ); ?>
+               </label>
+       </div>
+
+       <div class="revisions-previous">
+               <input class="button" type="button" id="previous" value="<?php echo esc_attr_x( 'Previous', 'Button label for a previous revision' ); ?>" />
+       </div>
+
+       <div class="revisions-next">
+               <input class="button" type="button" id="next" value="<?php echo esc_attr_x( 'Next', 'Button label for a next revision' ); ?>" />
+       </div>
+</script>
+
+
+<script id="tmpl-revisions-meta" type="text/html">
+       <div id="diff-header">
+               <div id="diff-header-from" class="diff-header">
+                       <div id="diff-title-from" class="diff-title">
+                               <strong>
+                               <?php _ex( 'From:', 'Followed by post revision info' ); ?></strong>
+                                       <# if ( 'undefined' !== typeof data.from ) { #>
+                                               {{{ data.from.attributes.author.avatar }}} {{{ data.from.attributes.author.name }}},
+                                               {{{ data.from.attributes.timeAgo }}} <?php _e( 'ago' ); ?>
+                                               ({{{ data.from.attributes.dateShort }}})
+                                       <# } #>
+
+                       </div>
+                       <div class="clear"></div>
</ins><span class="cx">           </div>
</span><span class="cx"> 
</span><del>-               <div class="diff-slider-ticks-wrapper">
-                       <div id="diff-slider-ticks"></div>
</del><ins>+                <div id="diff-header-to" class="diff-header">
+                       <div id="diff-title-to" class="diff-title">
+                               <strong><?php _ex( 'To:', 'Followed by post revision info' ); ?></strong>
+                                       <# if ( 'undefined' !== typeof data.to ) { #>
+                                               {{{ data.to.attributes.author.avatar }}} {{{ data.to.attributes.author.name }}},
+                                               {{{ data.to.attributes.timeAgo }}} <?php _e( 'ago' ); ?>
+                                               ({{{ data.to.attributes.dateShort }}})
+                                       <# } #>
</ins><span class="cx">           </div>
</span><span class="cx"> 
</span><del>-               <div id="revision-interact"></div>
-
-               <div id="revisions-diff"></div>
</del><ins>+                        <input type="button" id="restore-revision" class="button button-primary" data-restore-link="{{{ data.restoreLink }}}" value="<?php esc_attr_e( 'Restore This Revision' )?>" />
+               </div>
</ins><span class="cx">   </div>
</span><del>-</div>
</del><ins>+</script>
</ins><span class="cx"> 
</span><span class="cx"> <script id="tmpl-revisions-diff" type="text/html">
</span><ins>+       <# _.each( data.fields, function( field ) { #>
+               <h3>{{{ field.name }}}</h3>
+               {{{ field.diff }}}
+       <# }); #>
+</script>
+
+<script id="tmpl-revisions-diff-old" type="text/html">
</ins><span class="cx">   <div id="toggle-revision-compare-mode">
</span><span class="cx">          <label>
</span><span class="cx">                  <input type="checkbox" id="compare-two-revisions" />
</span><span class="lines">@@ -157,12 +203,10 @@
</span><span class="cx">          </div>
</span><span class="cx">  </div>
</span><span class="cx"> 
</span><del>-       </div>
-
</del><span class="cx">   <div id="diff-table">{{{ data.diff }}}</div>
</span><span class="cx"> </script>
</span><span class="cx"> 
</span><del>-<script id="tmpl-revision-interact" type="text/html">
</del><ins>+<script id="tmpl-revision-interact-old" type="text/html">
</ins><span class="cx">   <div id="diff-previous-revision">
</span><span class="cx">          <input class="button" type="button" id="previous" value="<?php echo esc_attr_x( 'Previous', 'Button label for a previous revision' ); ?>" />
</span><span class="cx">  </div>
</span><span class="lines">@@ -171,7 +215,6 @@
</span><span class="cx">          <input class="button" type="button" id="next" value="<?php echo esc_attr_x( 'Next', 'Button label for a next revision' ); ?>" />
</span><span class="cx">  </div>
</span><span class="cx"> 
</span><del>-       <div id="diff-slider" class="wp-slider"></div>
</del><span class="cx"> </script>
</span><span class="cx"> 
</span><span class="cx"> <script id="tmpl-revision-ticks" type="text/html">
</span></span></pre></div>
<a id="trunkwpincludesrevisionphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/revision.php (24519 => 24520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/revision.php   2013-06-26 20:42:01 UTC (rev 24519)
+++ trunk/wp-includes/revision.php      2013-06-26 21:06:50 UTC (rev 24520)
</span><span class="lines">@@ -597,68 +597,3 @@
</span><span class="cx"> 
</span><span class="cx">  return true;
</span><span class="cx"> }
</span><del>-
-/**
- * Displays a human readable HTML representation of the difference between two strings.
- * similar to wp_text_diff, but tracks and returns could of lines added and removed
- *
- * @since 3.6.0
- *
- * @see wp_parse_args() Used to change defaults to user defined settings.
- * @uses Text_Diff
- * @uses WP_Text_Diff_Renderer_Table
- *
- * @param string $left_string "old" (left) version of string
- * @param string $right_string "new" (right) version of string
- * @param string|array $args Optional. Change 'title', 'title_left', and 'title_right' defaults.
- * @return array contains html, linesadded & linesdeletd, empty string if strings are equivalent.
- */
-function wp_text_diff_with_count( $left_string, $right_string, $args = null ) {
-       $defaults = array( 'title' => '', 'title_left' => '', 'title_right' => '' );
-       $args = wp_parse_args( $args, $defaults );
-
-       if ( ! class_exists( 'WP_Text_Diff_Renderer_Table' ) )
-                       require( ABSPATH . WPINC . '/wp-diff.php' );
-
-       $left_string  = normalize_whitespace( $left_string );
-       $right_string = normalize_whitespace( $right_string );
-
-       $left_lines  = explode( "\n", $left_string );
-       $right_lines = explode( "\n", $right_string) ;
-
-       $text_diff = new Text_Diff($left_lines, $right_lines  );
-       $lines_added = $text_diff->countAddedLines();
-       $lines_deleted = $text_diff->countDeletedLines();
-
-       $renderer  = new WP_Text_Diff_Renderer_Table();
-       $diff = $renderer->render( $text_diff );
-
-       if ( !$diff )
-                       return '';
-
-               $r  = "<table class='diff'>\n";
-
-       if ( ! empty( $args[ 'show_split_view' ] ) ) {
-               $r .= "<col class='content diffsplit left' /><col class='content diffsplit middle' /><col class='content diffsplit right' />";
-       } else {
-               $r .= "<col class='content' />";
-       }
-
-       if ( $args['title'] || $args['title_left'] || $args['title_right'] )
-               $r .= "<thead>";
-       if ( $args['title'] )
-               $r .= "<tr class='diff-title'><th colspan='4'>$args[title]</th></tr>\n";
-       if ( $args['title_left'] || $args['title_right'] ) {
-               $r .= "<tr class='diff-sub-title'>\n";
-               $r .= "\t<td></td><th>$args[title_left]</th>\n";
-               $r .= "\t<td></td><th>$args[title_right]</th>\n";
-               $r .= "</tr>\n";
-       }
-       if ( $args['title'] || $args['title_left'] || $args['title_right'] )
-               $r .= "</thead>\n";
-
-       $r .= "<tbody>\n$diff\n</tbody>\n";
-       $r .= "</table>";
-
-       return array( 'html' => $r, 'lines_added' => $lines_added, 'lines_deleted' => $lines_deleted );
-}
</del></span></pre>
</div>
</div>

</body>
</html>