new file mode 100644
---wp-admin/includes/revision.php (revision 0)n+++wp-admin/includes/revision.php (revision 0)
@@ -0,0 +1,92 @@
+<?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 ) {
+ $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 );
+ $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,
+ 'restoreNonce' => wp_create_nonce( 'restore-post_' . $revision->ID ),
+ );
+ }
+
+ return array(
+ 'postId' => $post->ID,
+ 'nonce' => wp_create_nonce( 'revisions-ajax-nonce' ),
+ 'restoreUrl' => admin_url( 'revision.php?action=restore' ), // &revision=$ID&_wpnonce=$restoreNonce
+ 'revisionData' => array_values( $revisions ),
+ );
+}
|
|
|
|
| 1 | 1 | window.wp = window.wp || {}; |
| 2 | 2 | |
| 3 | 3 | (function($) { |
| 4 | | var Revision, Revisions, Diff, revisions; |
| | 4 | var revisions; |
| 5 | 5 | |
| 6 | | revisions = wp.revisions = function() { |
| 7 | | Diff = revisions.Diff = new Diff(); |
| 8 | | }; |
| 9 | | |
| 10 | | _.extend( revisions, { model: {}, view: {}, controller: {} } ); |
| | 6 | revisions = wp.revisions = { model: {}, view: {}, controller: {} }; |
| 11 | 7 | |
| 12 | 8 | // Link settings. |
| 13 | | revisions.model.settings = typeof wpRevisionsSettings === 'undefined' ? {} : wpRevisionsSettings; |
| | 9 | revisions.settings = typeof _wpRevisionsSettings === 'undefined' ? {} : _wpRevisionsSettings; |
| 14 | 10 | |
| 15 | 11 | |
| 16 | 12 | /** |
| 17 | 13 | * ======================================================================== |
| 18 | | * CONTROLLERS |
| | 14 | * MODELS |
| 19 | 15 | * ======================================================================== |
| 20 | 16 | */ |
| | 17 | revisions.model.Slider = Backbone.Model.extend({ |
| | 18 | defaults: { |
| | 19 | value: 0, |
| | 20 | min: 0, |
| | 21 | max: 1, |
| | 22 | step: 1 |
| | 23 | } |
| | 24 | }); |
| 21 | 25 | |
| 22 | | /** |
| 23 | | * wp.revisions.controller.Diff |
| 24 | | * |
| 25 | | * Controlls the diff |
| 26 | | */ |
| 27 | | Diff = revisions.controller.Diff = Backbone.Model.extend( { |
| 28 | | rightDiff: 1, |
| 29 | | leftDiff: 1, |
| 30 | | revisions: null, |
| 31 | | leftHandleRevisions: null, |
| 32 | | rightHandleRevisions: null, |
| 33 | | revisionsInteractions: null, |
| 34 | | autosaves: true, |
| 35 | | showSplitView: true, |
| 36 | | singleRevision: true, |
| 37 | | leftModelLoading: false, // keep track of model loads |
| 38 | | rightModelLoading: false, // disallow slider interaction, also repeat loads, while loading |
| 39 | | tickmarkView: null, // the slider tickmarks |
| 40 | | slider: null, // the slider instance |
| 41 | | |
| 42 | | constructor: function() { |
| 43 | | var self = this; |
| 44 | | this.slider = new revisions.view.Slider(); |
| 45 | | |
| 46 | | if ( null === this.revisions ) { |
| 47 | | this.revisions = new Revisions(); // set up collection |
| 48 | | this.startRightModelLoading(); |
| 49 | | |
| 50 | | this.revisions.fetch({ // load revision data |
| 51 | | success: function() { |
| 52 | | self.stopRightModelLoading(); |
| 53 | | self.completeApplicationSetup(); |
| 54 | | } |
| 55 | | }); |
| 56 | | } |
| 57 | | }, |
| 58 | | |
| 59 | | loadDiffs: function( models ) { |
| 60 | | var self = this, |
| 61 | | revisionsToLoad = models.where( { completed: false } ), |
| 62 | | delay = 0, |
| 63 | | totalChanges; |
| 64 | | |
| 65 | | // match slider to passed revision_id |
| 66 | | _.each( revisionsToLoad, function( revision ) { |
| 67 | | if ( revision.get( 'ID' ) == revisions.model.settings.revision_id ) |
| 68 | | self.rightDiff = self.revisions.indexOf( revision ) + 1; |
| 69 | | }); |
| | 26 | revisions.model.Revision = Backbone.Model.extend({}); |
| 70 | 27 | |
| 71 | | _.each( revisionsToLoad, function( revision ) { |
| 72 | | _.delay( function() { |
| 73 | | revision.fetch( { |
| 74 | | update: true, |
| 75 | | add: false, |
| 76 | | remove: false, |
| 77 | | success: function( model ) { |
| 78 | | model.set( 'completed', true ); |
| 79 | | |
| 80 | | // stop spinner when all models are loaded |
| 81 | | if ( 0 === models.where( { completed: false } ).length ) |
| 82 | | self.stopModelLoadingSpinner(); |
| 83 | | |
| 84 | | totalChanges = model.get( 'linesAdded' ) + model.get( 'linesDeleted' ), |
| 85 | | scopeOfChanges = 'vsmall'; |
| 86 | | |
| 87 | | // Note: hard coded scope of changes |
| 88 | | // TODO change to dynamic based on range of values |
| 89 | | if ( totalChanges > 1 && totalChanges <= 3 ) { |
| 90 | | scopeOfChanges = 'small'; |
| 91 | | } else if ( totalChanges > 3 && totalChanges <= 5 ) { |
| 92 | | scopeOfChanges = 'med'; |
| 93 | | } else if ( totalChanges > 5 && totalChanges <= 10 ) { |
| 94 | | scopeOfChanges = 'large'; |
| 95 | | } else if ( totalChanges > 10 ) { |
| 96 | | scopeOfChanges = 'vlarge'; |
| 97 | | } |
| 98 | | model.set( 'scopeOfChanges', scopeOfChanges ); |
| 99 | | if ( 0 !== self.rightDiff && |
| 100 | | model.get( 'ID' ) === self.revisions.at( self.rightDiff - 1 ).get( 'ID' ) ) { |
| 101 | | // reload if current model refreshed |
| 102 | | self.revisionView.render(); |
| 103 | | } |
| 104 | | self.tickmarkView.render(); |
| 105 | | } |
| 106 | | } ); |
| 107 | | }, delay ) ; |
| 108 | | delay = delay + 150; // stagger model loads to avoid hammering server with requests |
| 109 | | } |
| 110 | | ); |
| 111 | | }, |
| | 28 | revisions.model.Revisions = Backbone.Collection.extend({ |
| | 29 | model: revisions.model.Revision, |
| 112 | 30 | |
| 113 | | startLeftModelLoading: function() { |
| 114 | | this.leftModelLoading = true; |
| 115 | | $('#revision-diff-container').addClass('left-model-loading'); |
| | 31 | comparator: function( revision ) { |
| | 32 | return revision.id; |
| 116 | 33 | }, |
| | 34 | }); |
| 117 | 35 | |
| 118 | | stopLeftModelLoading: function() { |
| 119 | | this.leftModelLoading = false; |
| 120 | | }, |
| | 36 | revisions.model.Field = Backbone.Model.extend({}); |
| 121 | 37 | |
| 122 | | startRightModelLoading: function() { |
| 123 | | this.rightModelLoading = true; |
| 124 | | $('#revision-diff-container').addClass('right-model-loading'); |
| 125 | | }, |
| 126 | | |
| 127 | | stopRightModelLoading: function() { |
| 128 | | this.rightModelLoading = false; |
| 129 | | }, |
| | 38 | revisions.model.Fields = Backbone.Collection.extend({ |
| | 39 | model: revisions.model.Field |
| | 40 | }); |
| 130 | 41 | |
| 131 | | stopModelLoadingSpinner: function() { |
| 132 | | $('#revision-diff-container').removeClass('right-model-loading'); |
| 133 | | $('#revision-diff-container').removeClass('left-model-loading'); |
| 134 | | }, |
| | 42 | revisions.model.Diff = Backbone.Model.extend({ |
| | 43 | initialize: function(attributes, options) { |
| | 44 | var fields = this.get('fields'); |
| | 45 | this.unset('fields'); |
| 135 | 46 | |
| 136 | | reloadModel: function() { |
| 137 | | if ( this.singleRevision ) { |
| 138 | | this.reloadModelSingle(); |
| 139 | | } else { |
| 140 | | this.reloadLeftRight(); |
| 141 | | } |
| 142 | | }, |
| | 47 | this.fields = new revisions.model.Fields( fields ); |
| | 48 | } |
| | 49 | }); |
| 143 | 50 | |
| 144 | | // load the models for the single handle mode |
| 145 | | reloadModelSingle: function() { |
| 146 | | var self = this; |
| 147 | | |
| 148 | | self.startRightModelLoading(); |
| 149 | | |
| 150 | | self.revisions.reload({ |
| 151 | | options: { |
| 152 | | 'showAutosaves': self.autosaves, |
| 153 | | 'showSplitView': self.showSplitView |
| 154 | | }, |
| 155 | | |
| 156 | | success: function() { |
| 157 | | var revisionCount = self.revisions.length; |
| 158 | | self.revisionView.model = self.revisions; |
| 159 | | self.revisionView.render(); |
| 160 | | self.loadDiffs( self.revisions ); |
| 161 | | self.tickmarkView.model = self.revisions; |
| 162 | | self.tickmarkView.render(); |
| 163 | | self.slider.refresh({ |
| 164 | | 'max': revisionCount - 1, // slider starts at 0 in single handle mode |
| 165 | | 'value': self.rightDiff - 1 // slider starts at 0 in single handle mode |
| 166 | | }, true); |
| 167 | | }, |
| 168 | | |
| 169 | | error: function() { |
| 170 | | self.stopRightModelLoading(); |
| 171 | | } |
| 172 | | }); |
| | 51 | revisions.model.Diffs = Backbone.Collection.extend({ |
| | 52 | initialize: function(models, options) { |
| | 53 | this.revisions = options.revisions; |
| | 54 | this.requests = {}; |
| 173 | 55 | }, |
| 174 | 56 | |
| 175 | | // load the models for the left handle (the right handler has moved) |
| 176 | | reloadLeft: function() { |
| 177 | | var self = this; |
| 178 | | self.startLeftModelLoading(); |
| 179 | | self.leftHandleRevisions = new Revisions( {}, { |
| 180 | | 'compareTo': self.revisions.at( self.rightDiff - 1 ).get( 'ID' ), // diff and model count off by 1 |
| 181 | | 'showAutosaves': self.autosaves, |
| 182 | | 'showSplitView': self.showSplitView, |
| 183 | | 'rightHandleAt': self.rightDiff |
| 184 | | }); |
| | 57 | model: revisions.model.Diff, |
| 185 | 58 | |
| 186 | | self.leftHandleRevisions.fetch({ |
| 187 | | success: function(){ |
| 188 | | self.stopLeftModelLoading(); |
| 189 | | self.loadDiffs( self.leftHandleRevisions ); |
| 190 | | self.tickmarkView.model = self.leftHandleRevisions; |
| 191 | | self.slider.refresh({ |
| 192 | | 'max': self.revisions.length |
| 193 | | }); |
| 194 | | // ensure right handle not beyond length |
| 195 | | if ( self.rightDiff > self.revisions.length ) |
| 196 | | self.rightDiff = self.revisions.length; |
| 197 | | }, |
| | 59 | ensure: function( id, context ) { |
| | 60 | var diff = this.get( id ); |
| | 61 | var request = this.requests[ id ]; |
| | 62 | var deferred = $.Deferred(); |
| | 63 | var ids = {}; |
| 198 | 64 | |
| 199 | | error: function() { |
| 200 | | self.stopLeftModelLoading(); |
| | 65 | if ( diff ) { |
| | 66 | deferred.resolveWith( context, [ diff ] ); |
| | 67 | } else { |
| | 68 | this.trigger( 'ensure:load', ids ); |
| | 69 | _.each( ids, _.bind( function(id) { |
| | 70 | // Remove anything that has an ongoing request |
| | 71 | if ( this.requests[ id ] ) |
| | 72 | delete ids[ id ]; |
| | 73 | }, this ) ); |
| | 74 | if ( ! request ) { |
| | 75 | // Always include the ID that started this ensure |
| | 76 | ids[ id ] = true; |
| | 77 | request = this.load( _.keys( ids ) ); |
| 201 | 78 | } |
| 202 | | }); |
| 203 | | }, |
| 204 | 79 | |
| 205 | | // load the models for the right handle (the left handle has moved) |
| 206 | | reloadRight: function() { |
| 207 | | var self = this; |
| 208 | | self.startRightModelLoading(); |
| 209 | | self.rightHandleRevisions = new Revisions( {}, { |
| 210 | | 'compareTo': self.revisions.at( self.leftDiff - 1 ).get( 'ID' ), // diff and model count off by 1 |
| 211 | | 'showAutosaves': self.autosaves, |
| 212 | | 'showSplitView': self.showSplitView, |
| 213 | | 'leftHandleAt': self.leftDiff |
| 214 | | }); |
| 215 | | |
| 216 | | self.rightHandleRevisions.fetch({ |
| 217 | | success: function(){ |
| 218 | | self.stopRightModelLoading(); |
| 219 | | self.loadDiffs( self.rightHandleRevisions ); |
| 220 | | self.tickmarkView.model = self.rightHandleRevisions; |
| 221 | | self.slider.refresh({ |
| 222 | | 'max': self.revisions.length |
| 223 | | }, true); |
| 224 | | }, |
| 225 | | |
| 226 | | error: function( response ) { |
| 227 | | self.stopRightModelLoading(); |
| 228 | | } |
| 229 | | }); |
| | 80 | request.done( _.bind( function() { |
| | 81 | deferred.resolveWith( context, [ this.get( id ) ] ); |
| | 82 | }, this ) ); |
| | 83 | } |
| 230 | 84 | |
| | 85 | return deferred.promise(); |
| 231 | 86 | }, |
| 232 | 87 | |
| 233 | | /** |
| 234 | | * reloadLeftRight reload models for both the left and right handles |
| 235 | | */ |
| 236 | | reloadLeftRight: function() { |
| 237 | | this.startRightModelLoading(); |
| 238 | | this.startLeftModelLoading(); |
| 239 | | this.reloadLeft(); |
| 240 | | this.reloadRight(); |
| | 88 | loadNew: function( comparisons ) { |
| | 89 | comparisons = _.object( comparisons, comparisons ); |
| | 90 | _.each( comparisons, _.bind( function( id ) { |
| | 91 | // Exists |
| | 92 | if ( this.get( id ) ) |
| | 93 | delete comparisons[ id ]; |
| | 94 | }, this ) ); |
| | 95 | comparisons = _.toArray( comparisons ); |
| | 96 | console.log( 'Loading', comparisons ); |
| | 97 | return this.load( comparisons ); |
| 241 | 98 | }, |
| 242 | 99 | |
| 243 | | disabledButtonCheck: function( val ) { |
| 244 | | var maxVal = this.revisions.length - 1, |
| 245 | | next = ! isRtl ? $( '#next' ) : $( '#previous' ), |
| 246 | | prev = ! isRtl ? $( '#previous' ) : $( '#next' ); |
| 247 | | |
| 248 | | // Disable "Next" button if you're on the last node |
| 249 | | if ( maxVal === val ) |
| 250 | | next.prop( 'disabled', true ); |
| 251 | | else |
| 252 | | next.prop( 'disabled', false ); |
| 253 | | |
| 254 | | // Disable "Previous" button if you're on the 0 node |
| 255 | | if ( 0 === val ) |
| 256 | | prev.prop( 'disabled', true ); |
| 257 | | else |
| 258 | | prev.prop( 'disabled', false ); |
| 259 | | }, |
| 260 | | |
| 261 | | /** |
| 262 | | * completeApplicationSetup finishes loading all views once the initial model load is complete |
| 263 | | */ |
| 264 | | completeApplicationSetup: function() { |
| 265 | | this.revisionView = new revisions.view.Diff({ |
| 266 | | model: this.revisions |
| 267 | | }); |
| 268 | | this.revisionView.render(); // render the revision view |
| 269 | | |
| 270 | | this.loadDiffs( this.revisions ); // get the actual revisions data |
| 271 | | |
| 272 | | this.revisionsInteractions = new revisions.view.Interact({ |
| 273 | | model: this.revisions |
| 274 | | }); |
| 275 | | this.revisionsInteractions.render(); // render the interaction view |
| 276 | | |
| 277 | | this.tickmarkView = new revisions.view.Tickmarks({ |
| 278 | | model: this.revisions |
| 279 | | }); |
| 280 | | this.tickmarkView.render(); // render the tickmark view |
| 281 | | } |
| 282 | | }); |
| 283 | | |
| 284 | | |
| 285 | | /** |
| 286 | | * ======================================================================== |
| 287 | | * VIEWS |
| 288 | | * ======================================================================== |
| 289 | | */ |
| 290 | | |
| 291 | | /** |
| 292 | | * wp.revisions.view.Slider |
| 293 | | * |
| 294 | | * The slider |
| 295 | | */ |
| 296 | | revisions.view.Slider = Backbone.View.extend({ |
| 297 | | el: $( '#diff-slider' ), |
| 298 | | singleRevision: true, |
| 299 | | |
| 300 | | initialize: function( options ) { |
| 301 | | this.options = _.defaults( options || {}, { |
| 302 | | value: 0, |
| 303 | | min: 0, |
| 304 | | max: 1, |
| 305 | | step: 1 |
| 306 | | }); |
| | 100 | load: function( comparisons ) { |
| | 101 | // Our collection should only ever grow, never shrink, so remove: false |
| | 102 | return this.fetch({ data: { compare: comparisons }, remove: false }); |
| 307 | 103 | }, |
| 308 | 104 | |
| 309 | | /** |
| 310 | | * respond to slider slide events |
| 311 | | * Note: in one handle mode, jQuery UI reports leftmost position as 0 |
| 312 | | * in two handle mode, jQuery UI Slider reports leftmost position as 1 |
| 313 | | */ |
| 314 | | slide: function( event, ui ) { |
| 315 | | if ( this.singleRevision ) { |
| 316 | | Diff.rightDiff = ( ui.value + 1 ); |
| 317 | | Diff.revisionView.render(); |
| 318 | | Diff.disabledButtonCheck( ui.value ); |
| 319 | | } else { |
| 320 | | if ( ui.values[0] === ui.values[1] ) // prevent compare to self |
| 321 | | return false; |
| 322 | | |
| 323 | | if ( $( ui.handle ).hasClass( 'left-handle' ) ) { |
| 324 | | // Left handler |
| 325 | | if ( Diff.leftModelLoading ) // left model still loading, prevent sliding left handle |
| 326 | | return false; |
| 327 | | |
| 328 | | Diff.leftDiff = isRtl ? ui.values[1] : ui.values[0]; // handles are reversed in RTL mode |
| 329 | | } else { |
| 330 | | // Right handler |
| 331 | | if ( Diff.rightModelLoading ) // right model still loading, prevent sliding right handle |
| 332 | | return false; |
| 333 | | |
| 334 | | Diff.rightDiff = isRtl ? ui.values[0] : ui.values[1]; // handles are reversed in RTL mode |
| 335 | | } |
| 336 | | |
| 337 | | Diff.revisionView.render(); |
| | 105 | /**/ |
| | 106 | loadLast: function( num ) { |
| | 107 | num = num || 1; |
| | 108 | var ids = this.getProximalDiffIds(); |
| | 109 | ids = _.last( ids, num ); |
| | 110 | if ( ids.length ) { |
| | 111 | console.log( 'Last ' + num, ids ); |
| | 112 | return this.loadNew( ids ); |
| 338 | 113 | } |
| 339 | 114 | }, |
| 340 | 115 | |
| 341 | | /** |
| 342 | | * responds to slider start sliding events |
| 343 | | * in two handle mode stores start position, so if unchanged at stop event no need to reload diffs |
| 344 | | * also swaps in the appropriate models - left handled or right handled |
| 345 | | */ |
| 346 | | start: function( event, ui ) { |
| 347 | | // Not needed in one mode |
| 348 | | if ( this.singleRevision ) |
| 349 | | return; |
| 350 | | |
| 351 | | if ( $( ui.handle ).hasClass( 'left-handle' ) ) { |
| 352 | | // Left handler |
| 353 | | if ( Diff.leftModelLoading ) // left model still loading, prevent sliding left handle |
| 354 | | return false; |
| 355 | | |
| 356 | | Diff.revisionView.draggingLeft = true; |
| 357 | | |
| 358 | | if ( Diff.revisionView.model !== Diff.leftHandleRevisions && |
| 359 | | null !== Diff.leftHandleRevisions ) { |
| 360 | | Diff.revisionView.model = Diff.leftHandleRevisions; // use the left handle models |
| 361 | | Diff.tickmarkView.model = Diff.leftHandleRevisions; |
| 362 | | Diff.tickmarkView.render(); |
| 363 | | } |
| 364 | | |
| 365 | | Diff.leftDiffStart = isRtl ? ui.values[1] : ui.values[0]; // in RTL mode the 'left handle' is the second in the slider, 'right' is first |
| 366 | | |
| 367 | | } else { |
| 368 | | // Right handler |
| 369 | | if ( Diff.rightModelLoading || 0 === Diff.rightHandleRevisions.length) // right model still loading, prevent sliding right handle |
| 370 | | return false; |
| 371 | | |
| 372 | | if ( Diff.revisionView.model !== Diff.rightHandleRevisions && |
| 373 | | null !== Diff.rightHandleRevisions ) { |
| 374 | | Diff.revisionView.model = Diff.rightHandleRevisions; // use the right handle models |
| 375 | | Diff.tickmarkView.model = Diff.rightHandleRevisions; |
| 376 | | Diff.tickmarkView.render(); |
| 377 | | } |
| 378 | | |
| 379 | | Diff.revisionView.draggingLeft = false; |
| 380 | | Diff.rightDiffStart = isRtl ? ui.values[0] : ui.values[1]; // in RTL mode the 'left handle' is the second in the slider, 'right' is first |
| | 116 | loadLastUnloaded: function( num ) { |
| | 117 | num = num || 1; |
| | 118 | var ids = this.getUnloadedProximalDiffIds(); |
| | 119 | ids = _.last( ids, num ); |
| | 120 | if ( ids.length ) { |
| | 121 | console.log( 'Loading last ' + num ); |
| | 122 | return this.loadNew( ids ); |
| 381 | 123 | } |
| 382 | 124 | }, |
| 383 | 125 | |
| 384 | | /** |
| 385 | | * responds to slider stop events |
| 386 | | * in two handled mode, if the handle that stopped has moved, reload the diffs for the other handle |
| 387 | | * the other handle compares to this handle's position, so if it changes they need to be recalculated |
| 388 | | */ |
| 389 | | stop: function( event, ui ) { |
| 390 | | // Not needed in one mode |
| 391 | | if ( this.singleRevision ) |
| 392 | | return; |
| 393 | | |
| 394 | | // calculate and generate a diff for comparing to the left handle |
| 395 | | // and the right handle, swap out when dragging |
| 396 | | if ( $( ui.handle ).hasClass( 'left-handle' ) ) { |
| 397 | | // Left handler |
| 398 | | 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 |
| 399 | | Diff.reloadRight(); |
| 400 | | } else { |
| 401 | | // Right handler |
| 402 | | 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 |
| 403 | | Diff.reloadLeft(); |
| | 126 | getProximalDiffIds: function() { |
| | 127 | var previous = 0, ids = []; |
| | 128 | this.revisions.each( _.bind( function(revision) { |
| | 129 | ids.push( previous + ':' + revision.id ); |
| | 130 | previous = revision.id; |
| | 131 | }, this ) ); |
| | 132 | return ids; |
| | 133 | }, |
| | 134 | |
| | 135 | getUnloadedProximalDiffIds: function() { |
| | 136 | var comparisons = this.getProximalDiffIds(); |
| | 137 | comparisons = _.object( comparisons, comparisons ); |
| | 138 | _.each( comparisons, _.bind( function( id ) { |
| | 139 | // Exists |
| | 140 | if ( this.get( id ) ) |
| | 141 | delete comparisons[ id ]; |
| | 142 | }, this ) ); |
| | 143 | return _.toArray( comparisons ); |
| | 144 | }, |
| | 145 | |
| | 146 | loadAllBy: function( chunkSize ) { |
| | 147 | chunkSize = chunkSize || 20; |
| | 148 | var unloaded = this.getUnloadedProximalDiffIds(); |
| | 149 | if ( unloaded.length ) { |
| | 150 | return this.loadLastUnloaded( chunkSize ).always( _.bind( function() { |
| | 151 | this.loadAllBy( chunkSize ); |
| | 152 | }, this ) ); |
| 404 | 153 | } |
| 405 | 154 | }, |
| 406 | 155 | |
| 407 | | addTooltip: function( handle, message ) { |
| 408 | | handle.find( '.ui-slider-tooltip' ).html( message ); |
| 409 | | }, |
| 410 | | |
| 411 | | width: function() { |
| 412 | | return $( '#diff-slider' ).width(); |
| 413 | | }, |
| | 156 | /**/ |
| 414 | 157 | |
| 415 | | setWidth: function( width ) { |
| 416 | | $( '#diff-slider' ).width( width ); |
| 417 | | }, |
| | 158 | sync: function( method, model, options ) { |
| | 159 | if ( 'read' === method ) { |
| | 160 | options = options || {}; |
| | 161 | options.context = this; |
| | 162 | options.data = _.extend( options.data || {}, { |
| | 163 | action: 'get-revision-diffs', |
| | 164 | post_id: revisions.settings.postId |
| | 165 | }); |
| 418 | 166 | |
| 419 | | refresh: function( options, slide ) { |
| 420 | | $( '#diff-slider' ).slider( 'option', options ); |
| | 167 | var deferred = wp.xhr.send( options ); |
| | 168 | var requests = this.requests; |
| 421 | 169 | |
| 422 | | // Triggers the slide event |
| 423 | | if ( slide ) |
| 424 | | $( '#diff-slider' ).trigger( 'slide' ); |
| | 170 | // Record that we're requesting each diff. |
| | 171 | if ( options.data.compare ) { |
| | 172 | _.each( options.data.compare, function( id ) { |
| | 173 | requests[ id ] = deferred; |
| | 174 | }); |
| | 175 | } |
| 425 | 176 | |
| 426 | | Diff.disabledButtonCheck( options.value ); |
| 427 | | }, |
| | 177 | // When the request completes, clear the stored request. |
| | 178 | deferred.always( function() { |
| | 179 | if ( options.data.compare ) { |
| | 180 | _.each( options.data.compare, function( id ) { |
| | 181 | delete requests[ id ]; |
| | 182 | }); |
| | 183 | } |
| | 184 | }); |
| 428 | 185 | |
| 429 | | option: function( key ) { |
| 430 | | return $( '#diff-slider' ).slider( 'option', key ); |
| 431 | | }, |
| | 186 | return deferred; |
| 432 | 187 | |
| 433 | | render: function() { |
| 434 | | var self = this; |
| 435 | | // this.$el doesn't work, why? |
| 436 | | $( '#diff-slider' ).slider( { |
| 437 | | slide: $.proxy( self.slide, self ), |
| 438 | | start: $.proxy( self.start, self ), |
| 439 | | stop: $.proxy( self.stop, self ) |
| 440 | | } ); |
| 441 | | |
| 442 | | // Set options |
| 443 | | this.refresh( this.options ); |
| | 188 | // Otherwise, fall back to `Backbone.sync()`. |
| | 189 | } else { |
| | 190 | return Backbone.Model.prototype.sync.apply( this, arguments ); |
| | 191 | } |
| 444 | 192 | } |
| 445 | 193 | }); |
| 446 | 194 | |
| 447 | | /** |
| 448 | | * wp.revisions.view.Tickmarks |
| 449 | | * |
| 450 | | * The slider tickmarks. |
| 451 | | */ |
| 452 | | revisions.view.Tickmarks = Backbone.View.extend({ |
| 453 | | el: $('#diff-slider-ticks'), |
| 454 | | template: wp.template('revision-ticks'), |
| 455 | | model: Revision, |
| 456 | | |
| 457 | | resetTicks: function() { |
| 458 | | var sliderMax, sliderWidth, adjustMax, tickWidth, tickCount = 0, aTickWidth, tickMargin, self = this, firstTick, lastTick; |
| 459 | | sliderMax = Diff.slider.option( 'max' ); |
| 460 | | sliderWidth = Diff.slider.width(); |
| 461 | | adjustMax = Diff.singleRevision ? 0 : 1; |
| 462 | | tickWidth = Math.floor( sliderWidth / ( sliderMax - adjustMax ) ); |
| 463 | | tickWidth = ( tickWidth > 50 ) ? 50 : tickWidth; // set minimum and maximum widths for tick marks |
| 464 | | tickWidth = ( tickWidth < 6 ) ? 6 : tickWidth; |
| 465 | | sliderWidth = tickWidth * ( sliderMax - adjustMax ); // calculate the slider width |
| 466 | | aTickWidth = $( '.revision-tick' ).width(); |
| 467 | | |
| 468 | | if ( tickWidth !== aTickWidth ) { // is the width already set correctly? |
| 469 | | $( '.revision-tick' ).each( function() { |
| 470 | | tickMargin = Math.floor( ( tickWidth - $( this ).width() ) / 2 ) + 1; |
| 471 | | $( this ).css( 'border-left', tickMargin + 'px solid #f7f7f7'); // space the ticks out using margins |
| 472 | | $( this ).css( 'border-right', ( tickWidth - tickMargin - $( this ).width() ) + 'px solid #f7f7f7'); // space the ticks out using margins |
| 473 | | }); |
| 474 | | firstTick = $( '.revision-tick' ).first(); //cache selectors for optimization |
| 475 | | lastTick = $( '.revision-tick' ).last(); |
| 476 | 195 | |
| 477 | | sliderWidth = sliderWidth + Math.ceil( ( tickWidth - ( lastTick.outerWidth() - lastTick.innerWidth() ) ) / 2 ); // room for the last tick |
| 478 | | sliderWidth = sliderWidth + Math.ceil( ( tickWidth - ( firstTick.outerWidth() - firstTick.innerWidth() ) ) / 2 ); // room for the first tick |
| 479 | | firstTick.css( 'border-left', 'none' ); // first tick gets no left border |
| 480 | | lastTick.css( 'border-right', 'none' ); // last tick gets no right border |
| 481 | | } |
| | 196 | revisions.model.FrameState = Backbone.Model.extend({ |
| | 197 | initialize: function( attributes, options ) { |
| | 198 | this.revisions = options.revisions; |
| | 199 | this.diffs = new revisions.model.Diffs( [], {revisions: this.revisions} ); |
| 482 | 200 | |
| 483 | | /** |
| 484 | | * reset the slider width |
| 485 | | */ |
| 486 | | Diff.slider.setWidth( sliderWidth ); |
| 487 | | $( '.diff-slider-ticks-wrapper' ).width( sliderWidth ); |
| 488 | | $( '#diff-slider-ticks' ).width( sliderWidth ); |
| 489 | | |
| 490 | | /** |
| 491 | | * go through all ticks, add hover and click interactions |
| 492 | | */ |
| 493 | | $( '.revision-tick' ).each( function() { |
| 494 | | Diff.slider.addTooltip ( $( this ), Diff.revisions.at( tickCount++ ).get( 'titleTooltip' ) ); |
| 495 | | $( this ).hover( |
| 496 | | function() { |
| 497 | | $( this ).find( '.ui-slider-tooltip' ).show().append('<div class="arrow"></div>'); |
| 498 | | }, |
| 499 | | function() { |
| 500 | | $( this ).find( '.ui-slider-tooltip' ).hide().find( '.arrow' ).remove(); |
| 501 | | } |
| 502 | | ); |
| 503 | | |
| 504 | | /** |
| 505 | | * move the slider handle when the tick marks are clicked |
| 506 | | */ |
| 507 | | $( this ).on( 'click', |
| 508 | | { tickCount: tickCount }, // pass the tick through so we know where to move the handle |
| 509 | | function( event ) { |
| 510 | | if ( Diff.slider.singleRevision ) { // single handle mode |
| 511 | | Diff.rightDiff = event.data.tickCount; // reposition the right handle |
| 512 | | Diff.slider.refresh({ |
| 513 | | value: Diff.rightDiff - 1 |
| 514 | | } ); |
| 515 | | } else { //compare two mode |
| 516 | | if ( isRtl ) { |
| 517 | | if ( event.data.tickCount < Diff.leftDiff ) { // click was on the 'left' side |
| 518 | | Diff.rightDiff = event.data.tickCount; // set the 'right' handle location |
| 519 | | Diff.reloadLeft(); // reload the left handle comparison models |
| 520 | | } else { // middle or 'right' clicks |
| 521 | | Diff.leftDiff = event.data.tickCount; // set the 'left' handle location |
| 522 | | Diff.reloadRight(); // reload right handle models |
| 523 | | } |
| 524 | | } else { |
| 525 | | if ( event.data.tickCount < Diff.leftDiff ) { // click was on the 'left' side |
| 526 | | Diff.leftDiff = event.data.tickCount; // set the left handle location |
| 527 | | Diff.reloadRight(); // reload the right handle comparison models |
| 528 | | } else { // middle or 'right' clicks |
| 529 | | Diff.rightDiff = event.data.tickCount; // set the right handle location |
| 530 | | Diff.reloadLeft(); // reload left handle models |
| 531 | | } |
| 532 | | } |
| 533 | | Diff.slider.refresh( { // set the slider handle positions |
| 534 | | values: [ isRtl ? Diff.rightDiff : Diff.leftDiff, isRtl ? Diff.leftDiff : Diff.rightDiff ] |
| 535 | | } ); |
| 536 | | } |
| 537 | | Diff.revisionView.render(); // render the main view |
| 538 | | } ); |
| 539 | | } ); |
| | 201 | this.listenTo( this, 'change:from change:to', this.updateDiffId ); |
| 540 | 202 | }, |
| 541 | 203 | |
| 542 | | // render the tick mark view |
| 543 | | render: function() { |
| 544 | | var self = this, addHtml; |
| 545 | | |
| 546 | | if ( null !== self.model ) { |
| 547 | | addHtml = ""; |
| 548 | | _.each ( self.model.models, function( theModel ) { |
| 549 | | addHtml = addHtml + self.template ( theModel.toJSON() ); |
| 550 | | }); |
| 551 | | self.$el.html( addHtml ); |
| 552 | | |
| 553 | | } |
| 554 | | self.resetTicks(); |
| 555 | | return self; |
| | 204 | updateDiffId: function() { |
| | 205 | var from = this.get('from'); |
| | 206 | var to = this.get('to'); |
| | 207 | this.set( 'diffId', (from ? from.id : '0') + ':' + to.id ); |
| 556 | 208 | } |
| 557 | | } ); |
| | 209 | }); |
| | 210 | |
| 558 | 211 | |
| 559 | 212 | /** |
| 560 | | * wp.revisions.view.Interact |
| 561 | | * |
| 562 | | * Next/Prev buttons and the slider |
| | 213 | * ======================================================================== |
| | 214 | * VIEWS |
| | 215 | * ======================================================================== |
| 563 | 216 | */ |
| 564 | | revisions.view.Interact = Backbone.View.extend({ |
| 565 | | el: $( '#revision-interact' ), |
| 566 | | template: wp.template( 'revision-interact' ), |
| 567 | | |
| 568 | | // next and previous buttons, only available in compare one mode |
| 569 | | events: { |
| 570 | | 'click #next': ! isRtl ? 'nextRevision' : 'previousRevision', |
| 571 | | 'click #previous': ! isRtl ? 'previousRevision' : 'nextRevision' |
| 572 | | }, |
| 573 | 217 | |
| 574 | | render: function() { |
| 575 | | var modelcount; |
| 576 | | this.$el.html( this.template ); |
| | 218 | // The frame view. This contains the entire page. |
| | 219 | revisions.view.Frame = wp.Backbone.View.extend({ |
| | 220 | tagName: 'div', |
| | 221 | className: 'revisions', |
| | 222 | template: wp.template('revisions-frame'), |
| 577 | 223 | |
| 578 | | modelcount = Diff.revisions.length; |
| | 224 | initialize: function() { |
| | 225 | this.model = new revisions.model.FrameState({}, { |
| | 226 | revisions: this.collection |
| | 227 | }); |
| 579 | 228 | |
| 580 | | Diff.slider.singleRevision = Diff.singleRevision; |
| 581 | | Diff.slider.render(); |
| | 229 | this.listenTo( this.model, 'change:diffId', this.updateDiff ); |
| 582 | 230 | |
| 583 | | if ( Diff.singleRevision ) { |
| 584 | | Diff.slider.refresh({ |
| 585 | | value: Diff.rightDiff - 1, // rightDiff value is off model index by 1 |
| 586 | | min: 0, |
| 587 | | max: modelcount - 1 |
| 588 | | }); |
| | 231 | this.views.set( '.revisions-control-frame', new revisions.view.Controls({ |
| | 232 | model: this.model |
| | 233 | }) ); |
| 589 | 234 | |
| 590 | | $( '#revision-diff-container' ).removeClass( 'comparing-two-revisions' ); |
| | 235 | if ( this.model.revisions.length ) { |
| | 236 | var last = this.model.revisions.last(2); |
| | 237 | var attributes = { to: last.pop() }; |
| 591 | 238 | |
| 592 | | } else { |
| 593 | | Diff.slider.refresh({ |
| 594 | | // in RTL mode the 'left handle' is the second in the slider, 'right' is first |
| 595 | | values: [ isRtl ? Diff.rightDiff : Diff.leftDiff, isRtl ? Diff.leftDiff : Diff.rightDiff ], |
| 596 | | min: 1, |
| 597 | | max: modelcount + 1, |
| 598 | | range: true |
| 599 | | }); |
| | 239 | if ( last.length ) |
| | 240 | attributes.from = last.pop(); |
| 600 | 241 | |
| 601 | | $( '#revision-diff-container' ).addClass( 'comparing-two-revisions' ); |
| 602 | | // in RTL mode the 'left handle' is the second in the slider, 'right' is first |
| 603 | | $( '#diff-slider a.ui-slider-handle' ).first().addClass( isRtl ? 'right-handle' : 'left-handle' ); |
| 604 | | $( '#diff-slider a.ui-slider-handle' ).last().addClass( isRtl ? 'left-handle' : 'right-handle' ); |
| | 242 | this.model.set( attributes ); |
| 605 | 243 | |
| | 244 | // Load the rest: first 10, then the rest by 50 |
| | 245 | this.model.diffs.loadLastUnloaded( 10 ).always( _.bind( function() { |
| | 246 | console.log( 'Loading all by 50' ); |
| | 247 | this.model.diffs.loadAllBy( 50 ); |
| | 248 | }, this ) ); |
| 606 | 249 | } |
| 607 | | |
| 608 | | return this; |
| 609 | 250 | }, |
| 610 | 251 | |
| 611 | | // go to the next revision |
| 612 | | nextRevision: function() { |
| 613 | | if ( Diff.rightDiff < this.model.length ) // unless at right boundry |
| 614 | | Diff.rightDiff = Diff.rightDiff + 1 ; |
| | 252 | render: function() { |
| | 253 | wp.Backbone.View.prototype.render.apply( this, arguments ); |
| 615 | 254 | |
| 616 | | Diff.revisionView.render(); |
| | 255 | $('#wpbody-content .wrap').append( this.el ); |
| | 256 | this.views.ready(); |
| 617 | 257 | |
| 618 | | Diff.slider.refresh({ |
| 619 | | value: Diff.rightDiff - 1 |
| 620 | | }, true ); |
| | 258 | return this; |
| 621 | 259 | }, |
| 622 | 260 | |
| 623 | | // go to the previous revision |
| 624 | | previousRevision: function() { |
| 625 | | if ( Diff.rightDiff > 1 ) // unless at left boundry |
| 626 | | Diff.rightDiff = Diff.rightDiff - 1 ; |
| 627 | | |
| 628 | | Diff.revisionView.render(); |
| | 261 | updateDiff: function() { |
| | 262 | this.model.diffs.ensure( this.model.get('diffId'), this ).done( function( diff ) { |
| | 263 | if ( this.model.get('diffId') !== diff.id ) |
| | 264 | return; |
| | 265 | this.views.set( '.revisions-diff-frame', new revisions.view.Diff({ |
| | 266 | model: diff |
| | 267 | })); |
| | 268 | }); |
| | 269 | } |
| | 270 | }); |
| 629 | 271 | |
| 630 | | Diff.slider.refresh({ |
| 631 | | value: Diff.rightDiff - 1 |
| 632 | | }, true ); |
| | 272 | // The control view. |
| | 273 | // This contains the revision slider, previous/next buttons, and the compare checkbox. |
| | 274 | revisions.view.Controls = wp.Backbone.View.extend({ |
| | 275 | tagName: 'div', |
| | 276 | className: 'revisions-controls', |
| | 277 | template: wp.template('revisions-controls'), |
| | 278 | |
| | 279 | initialize: function() { |
| | 280 | this.views.set( new revisions.view.Slider({ |
| | 281 | model: this.model |
| | 282 | }) ); |
| 633 | 283 | } |
| 634 | 284 | }); |
| 635 | 285 | |
| 636 | | /** |
| 637 | | * wp.revisions.view.Diff |
| 638 | | * |
| 639 | | * Diff, compare two checkbox and restore button |
| 640 | | */ |
| 641 | | revisions.view.Diff = Backbone.View.extend({ |
| 642 | | el: $( '#revisions-diff' ), |
| 643 | | template: wp.template( 'revisions-diff' ), |
| 644 | | draggingLeft: false, |
| 645 | | |
| 646 | | // the compare two button is in this view, add the interaction here |
| 647 | | events: { |
| 648 | | 'click #compare-two-revisions': 'compareTwo', |
| 649 | | 'click #restore-revision': 'restore' |
| | 286 | // The slider view. |
| | 287 | // Encapsulates all of the configuration for the jQuery UI slider into a view. |
| | 288 | revisions.view.Slider = wp.Backbone.View.extend({ |
| | 289 | tagName: 'div', |
| | 290 | className: 'wp-slider', |
| | 291 | |
| | 292 | initialize: function() { |
| | 293 | _.bindAll( this, 'start', 'slide', 'stop' ); |
| | 294 | |
| | 295 | // Create the slider model from the provided collection data. |
| | 296 | // TODO: This should actually pull from the model's `to` key. |
| | 297 | var latestRevisionIndex = this.model.revisions.length - 1; |
| | 298 | |
| | 299 | this.settings = new revisions.model.Slider({ |
| | 300 | max: latestRevisionIndex, |
| | 301 | value: latestRevisionIndex, |
| | 302 | start: this.start, |
| | 303 | slide: this.slide, |
| | 304 | stop: this.stop |
| | 305 | }); |
| 650 | 306 | }, |
| 651 | 307 | |
| 652 | | // render the revisions |
| 653 | | render: function() { |
| 654 | | var addHtml = '', thediff; |
| 655 | | |
| 656 | | // compare two revisions mode? |
| 657 | | if ( ! Diff.singleRevision ) { |
| 658 | | if ( this.draggingLeft ) { |
| 659 | | thediff = Diff.leftDiff - 1; //leftDiff value is off model index by 1 |
| 660 | | if ( this.model.at( thediff ) ) { |
| 661 | | addHtml = this.template( this.model.at( thediff ).toJSON() ); |
| 662 | | } |
| 663 | | } else { // dragging right handle |
| 664 | | thediff = Diff.rightDiff - 1; // rightDiff value is off model index by 1 |
| 665 | | if ( this.model.at( thediff ) ) { |
| 666 | | addHtml = this.template( this.model.at( thediff ).toJSON() ); |
| 667 | | } |
| 668 | | } |
| 669 | | } else { // end compare two revisions mode, eg only one slider handle |
| 670 | | if ( this.model.at( Diff.rightDiff - 1 ) ) { // rightDiff value is off model index by 1 |
| 671 | | addHtml = this.template( this.model.at( Diff.rightDiff - 1 ).toJSON() ); |
| 672 | | } |
| 673 | | } |
| 674 | | this.$el.html( addHtml ); |
| 675 | | |
| 676 | | if ( this.model.length < 2 ) { |
| 677 | | $( '#diff-slider' ).hide(); // don't allow compare two if fewer than three revisions |
| 678 | | $( '.diff-slider-ticks-wrapper' ).hide(); |
| 679 | | } |
| 680 | | |
| 681 | | this.toggleCompareTwoCheckbox(); |
| 682 | | |
| 683 | | // hide the restore button when on the last sport/current post data |
| 684 | | $( '#restore-revision' ).toggle( ! Diff.revisions.at( Diff.rightDiff - 1 ).get( 'isCurrent' ) ); |
| 685 | | |
| 686 | | return this; |
| | 308 | ready: function() { |
| | 309 | this.$el.slider( this.settings.toJSON() ); |
| | 310 | this.settings.on( 'change', function( model, options ) { |
| | 311 | // Apply changes to slider settings here. |
| | 312 | }, this ); |
| 687 | 313 | }, |
| 688 | 314 | |
| 689 | | toggleCompareTwoCheckbox: function() { |
| 690 | | // don't allow compare two if fewer than three revisions |
| 691 | | if ( this.model.length < 3 ) |
| 692 | | $( '#toggle-revision-compare-mode' ).hide(); |
| | 315 | start: function() { |
| 693 | 316 | |
| 694 | | $( '#compare-two-revisions' ).prop( 'checked', ! Diff.singleRevision ); |
| 695 | 317 | }, |
| 696 | 318 | |
| 697 | | // turn on/off the compare two mode |
| 698 | | compareTwo: function() { |
| 699 | | if ( $( '#compare-two-revisions' ).is( ':checked' ) ) { // compare 2 mode |
| 700 | | Diff.singleRevision = false ; |
| 701 | | |
| 702 | | // in RTL mode handles are swapped, so boundary checks are different; |
| 703 | | if ( isRtl ){ |
| 704 | | Diff.leftDiff = Diff.revisions.length; // put the left handle at the rightmost position, representing current revision |
| 705 | | |
| 706 | | if ( Diff.revisions.length === Diff.rightDiff ) // make sure 'left' handle not in rightmost slot |
| 707 | | Diff.rightDiff = Diff.rightDiff - 1; |
| 708 | | } else { |
| 709 | | if ( 1 === Diff.rightDiff ) // make sure right handle not in leftmost slot |
| 710 | | Diff.rightDiff = 2; |
| 711 | | } |
| 712 | | |
| 713 | | Diff.revisionView.draggingLeft = false; |
| | 319 | slide: function( event, ui ) { |
| | 320 | var attributes = { |
| | 321 | to: this.model.revisions.at( ui.value ) |
| | 322 | }; |
| 714 | 323 | |
| 715 | | revisions.model.settings.revision_id = ''; // reset passed revision id so switching back to one handle mode doesn't re-select revision |
| 716 | | Diff.reloadLeftRight(); // load diffs for left and right handles |
| 717 | | Diff.revisionView.model = Diff.rightHandleRevisions; |
| | 324 | // If we're at the first revision, unset 'from'. |
| | 325 | if ( ui.value ) |
| | 326 | attributes.from = this.model.revisions.at( ui.value - 1 ); |
| | 327 | else |
| | 328 | this.model.unset('from', { silent: true }); |
| 718 | 329 | |
| 719 | | } else { // compare one mode |
| 720 | | Diff.singleRevision = true; |
| 721 | | Diff.revisionView.draggingLeft = false; |
| 722 | | Diff.reloadModelSingle(); |
| 723 | | } |
| 724 | | Diff.revisionsInteractions.render(); |
| 725 | | Diff.tickmarkView.render(); |
| | 330 | this.model.set( attributes ); |
| 726 | 331 | }, |
| 727 | 332 | |
| 728 | | restore: function() { |
| 729 | | document.location = $( '#restore-revision' ).data( 'restoreLink' ); |
| | 333 | stop: function() { |
| 730 | 334 | } |
| 731 | 335 | }); |
| 732 | 336 | |
| | 337 | // The diff view. |
| | 338 | // This is the view for the current active diff. |
| | 339 | revisions.view.Diff = wp.Backbone.View.extend({ |
| | 340 | tagName: 'div', |
| | 341 | className: 'revisions-diff', |
| | 342 | template: wp.template('revisions-diff'), |
| 733 | 343 | |
| 734 | | /** |
| 735 | | * ======================================================================== |
| 736 | | * MODELS |
| 737 | | * ======================================================================== |
| 738 | | */ |
| 739 | | |
| 740 | | /** |
| 741 | | * wp.revisions.Revision |
| 742 | | */ |
| 743 | | Revision = revisions.model.Revision = Backbone.Model.extend({ |
| 744 | | idAttribute: 'ID', |
| 745 | | |
| 746 | | defaults: { |
| 747 | | ID: 0, |
| 748 | | titleTo: '', |
| 749 | | titleTooltip: '', |
| 750 | | titleFrom: '', |
| 751 | | diff: '<div class="diff-loading"><div class="spinner"></div></div>', |
| 752 | | restoreLink: '', |
| 753 | | completed: false, |
| 754 | | linesAdded: 0, |
| 755 | | linesDeleted: 0, |
| 756 | | scopeOfChanges: 'none', |
| 757 | | previousID: 0, |
| 758 | | isCurrent: false |
| 759 | | }, |
| 760 | | |
| 761 | | url: function() { |
| 762 | | if ( Diff.singleRevision ) { |
| 763 | | return ajaxurl + |
| 764 | | '?action=revisions-data' + |
| 765 | | '&show_autosaves=true' + |
| 766 | | '&show_split_view=true' + |
| 767 | | '&nonce=' + revisions.model.settings.nonce + |
| 768 | | '&single_revision_id=' + this.id + |
| 769 | | '&compare_to=' + this.get( 'previousID' ) + |
| 770 | | '&post_id=' + revisions.model.settings.post_id; |
| 771 | | } else { |
| 772 | | return this.collection.url() + '&single_revision_id=' + this.id; |
| 773 | | } |
| 774 | | |
| | 344 | // Generate the options to be passed to the template. |
| | 345 | prepare: function() { |
| | 346 | return _.extend({ fields: this.model.fields.toJSON() }, this.options ); |
| 775 | 347 | } |
| 776 | 348 | }); |
| 777 | 349 | |
| 778 | | /** |
| 779 | | * wp.revisions.Revisions |
| 780 | | */ |
| 781 | | Revisions = revisions.Revisions = Backbone.Collection.extend({ |
| 782 | | model: Revision, |
| 783 | | |
| 784 | | initialize: function( models, options ) { |
| 785 | | this.options = _.defaults( options || {}, { |
| 786 | | 'compareTo': revisions.model.settings.post_id, |
| 787 | | 'post_id': revisions.model.settings.post_id, |
| 788 | | 'showAutosaves': true, |
| 789 | | 'showSplitView': true, |
| 790 | | 'rightHandleAt': 0, |
| 791 | | 'leftHandleAt': 0, |
| 792 | | 'nonce': revisions.model.settings.nonce |
| 793 | | }); |
| 794 | | }, |
| 795 | | |
| 796 | | url: function() { |
| 797 | | return ajaxurl + |
| 798 | | '?action=revisions-data' + |
| 799 | | '&compare_to=' + this.options.compareTo + // revision are we comparing to |
| 800 | | '&post_id=' + this.options.post_id + // the post id |
| 801 | | '&show_autosaves=' + this.options.showAutosaves + // show or hide autosaves |
| 802 | | '&show_split_view=' + this.options.showSplitView + // show in split view or single column view |
| 803 | | '&right_handle_at=' + this.options.rightHandleAt + // mark point for comparison list |
| 804 | | '&left_handle_at=' + this.options.leftHandleAt + // mark point for comparison list |
| 805 | | '&nonce=' + this.options.nonce; |
| 806 | | }, |
| 807 | | |
| 808 | | reload: function( options ) { |
| 809 | | this.options = _.defaults( options.options || {}, this.options ); |
| 810 | | |
| 811 | | this.fetch({ |
| 812 | | success: options.success || null, |
| 813 | | error: options.error || null |
| 814 | | }); |
| 815 | | } |
| 816 | | |
| 817 | | } ); |
| 818 | | |
| 819 | | $( wp.revisions ); |
| | 350 | // Initialize the revisions UI. |
| | 351 | revisions.init = function() { |
| | 352 | revisions.view.frame = new revisions.view.Frame({ |
| | 353 | collection: new revisions.model.Revisions( revisions.settings.revisionData ) |
| | 354 | }).render(); |
| | 355 | }; |
| 820 | 356 | |
| | 357 | $( revisions.init ); |
| 821 | 358 | }(jQuery)); |