Ticket #28965: 28965.2.diff
File 28965.2.diff, 29.7 KB (added by , 10 years ago) |
---|
-
src/wp-includes/css/media-views.css
diff --git a/src/wp-includes/css/media-views.css b/src/wp-includes/css/media-views.css index 907f252..120ffdd 100644
a b 912 912 } 913 913 914 914 .attachment.details, 915 .media- grid-view.selected.attachment {915 .media-frame.mode-grid .selected.attachment { 916 916 -webkit-box-shadow: 0 0 0 1px #fff, 917 917 0 0 0 5px #1e8cbe; 918 918 box-shadow: 0 0 0 1px #fff, … … 921 921 922 922 .attachment.details .check, 923 923 .attachment.selected .check:focus, 924 .media- grid-view.attachment.selected .check {924 .media-frame.mode-grid .attachment.selected .check { 925 925 background-color: #1e8cbe; 926 926 -webkit-box-shadow: 0 0 0 1px #fff, 927 927 0 0 0 2px #1e8cbe; … … 929 929 0 0 0 2px #1e8cbe; 930 930 } 931 931 932 .media- grid-view.attachment .check {932 .media-frame.mode-grid .attachment .check { 933 933 display: block; 934 934 } 935 935 936 .media- grid-view.attachment .check div {936 .media-frame.mode-grid .attachment .check div { 937 937 background-position: 21px 0; 938 938 } 939 939 940 940 .attachment.details .check div, 941 .media- grid-view.attachment.selected .check div {941 .media-frame.mode-grid .attachment.selected .check div { 942 942 background-position: -21px 0; 943 943 } 944 944 945 945 .attachment.details .check:hover div, 946 946 .attachment.selected .check:focus div, 947 .media- grid-view.attachment.selected .check:hover div {947 .media-frame.mode-grid .attachment.selected .check:hover div { 948 948 background-position: -60px 0; 949 949 } 950 950 … … 1068 1068 display: inline-block; 1069 1069 } 1070 1070 1071 .attachment-preview:hover ~ .inline-toolbar { 1071 .attachment-preview:hover + .inline-toolbar, 1072 .inline-toolbar:hover { 1072 1073 display: block; 1073 1074 } 1074 1075 … … video#inline-media-node { 2596 2597 /** 2597 2598 * Media Grid 2598 2599 */ 2599 .media- grid-view,2600 .media- grid-view.media-frame-content,2601 .media- grid-view.attachments-browser .attachments,2602 .media- grid-view.uploader-inline-content {2600 .media-frame.mode-grid, 2601 .media-frame.mode-grid .media-frame-content, 2602 .media-frame.mode-grid .attachments-browser .attachments, 2603 .media-frame.mode-grid .uploader-inline-content { 2603 2604 position: static; 2604 2605 } 2605 2606 2606 2607 /* Regions we don't use at all */ 2607 .media-grid-view .media-frame-title, 2608 .media-grid-view .media-frame-toolbar, 2609 .media-grid-view .media-frame-menu { 2608 .media-frame.mode-grid .media-frame-title, 2609 .media-frame.mode-grid .media-frame-toolbar, 2610 .media-frame.mode-grid .media-frame-router, 2611 .media-frame.mode-grid .media-frame-menu { 2610 2612 display: none; 2611 2613 } 2612 2614 2613 .media- grid-view.media-frame-content {2615 .media-frame.mode-grid .media-frame-content { 2614 2616 background-color: transparent; 2615 2617 border: none; 2616 2618 } 2617 2619 2618 .media- grid-view.uploader-inline {2620 .media-frame.mode-grid .uploader-inline { 2619 2621 position: relative; 2620 2622 top: auto; 2621 2623 right: auto; … … video#inline-media-node { 2625 2627 margin-top: 0; 2626 2628 } 2627 2629 2628 .media- grid-view.media-toolbar select {2630 .media-frame.mode-grid .media-toolbar select { 2629 2631 margin-top: 1px; 2630 2632 font-size: inherit; 2631 2633 } 2632 2634 2633 .media- grid-view.attachments-browser .bulk-select {2635 .media-frame.mode-grid .attachments-browser .bulk-select { 2634 2636 display: inline-block; 2635 2637 } 2636 2638 … … video#inline-media-node { 2639 2641 * 2640 2642 * This should be OOCSS'd so both use a shared selector. 2641 2643 */ 2642 .media- grid-view.attachments-browser .media-toolbar {2644 .media-frame.mode-grid .attachments-browser .media-toolbar { 2643 2645 background: #fff; 2644 2646 -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); 2645 2647 box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); … … video#inline-media-node { 2657 2659 border: none; 2658 2660 } 2659 2661 2660 .media- grid-viewinput[type="search"] {2662 .media-frame.mode-grid input[type="search"] { 2661 2663 margin: 1px; 2662 2664 padding: 3px 5px; 2663 2665 position: absolute; … … video#inline-media-node { 2669 2671 width: 280px; 2670 2672 } 2671 2673 2672 .media- grid-view.view-switch {2674 .media-frame.mode-grid .view-switch { 2673 2675 display: inline-block; 2674 2676 float: none; 2675 2677 vertical-align: middle; … … video#inline-media-node { 2677 2679 margin: 0 20px 0 0; 2678 2680 } 2679 2681 2680 .media- grid-viewselect {2682 .media-frame.mode-grid select { 2681 2683 margin: 0 10px 0 0; 2682 2684 } 2683 2685 2684 .media- grid-view.spinner {2686 .media-frame.mode-grid .spinner { 2685 2687 margin-top: 15px; 2686 2688 } 2687 2689 2688 .media- grid-view.attachments-browser {2690 .media-frame.mode-grid .attachments-browser { 2689 2691 padding: 0; 2690 2692 } 2691 2693 2692 .media- grid-view.attachments-browser .no-media {2694 .media-frame.mode-grid .attachments-browser .no-media { 2693 2695 color: #999; 2694 2696 font-size: 18px; 2695 2697 font-style: normal; … … video#inline-media-node { 2805 2807 top: 56px; 2806 2808 } 2807 2809 2808 /* Hiding this for the moment instead of removing it from the template. */2809 .edit-attachment-frame h3 {2810 display: none;2811 }2812 2813 2810 .edit-attachment-frame .attachment-details { 2814 2811 position: absolute; 2815 2812 overflow: auto; … … video#inline-media-node { 2950 2947 */ 2951 2948 2952 2949 @media only screen and (max-width: 1120px) { 2953 .media- grid-view.attachments-browser .media-toolbar-primary,2954 .media- grid-view.attachments-browser .media-toolbar-secondary {2950 .media-frame.mode-grid .attachments-browser .media-toolbar-primary, 2951 .media-frame.mode-grid .attachments-browser .media-toolbar-secondary { 2955 2952 float: none; 2956 2953 } 2957 2954 2958 .media- grid-viewinput[type="search"] {2955 .media-frame.mode-grid input[type="search"] { 2959 2956 margin: 20px 0; 2960 2957 position: static; 2961 2958 width: 100%; -
src/wp-includes/js/media-grid.js
diff --git a/src/wp-includes/js/media-grid.js b/src/wp-includes/js/media-grid.js index 67f4dc5..1aee748 100644
a b 1 1 /* global _wpMediaViewsL10n, MediaElementPlayer, _wpMediaGridSettings, confirm */ 2 2 (function($, _, Backbone, wp) { 3 // Local reference to the WordPress media namespace. 3 4 var media = wp.media, l10n; 4 5 5 // Link any localized strings.6 // Link localized strings and settings. 6 7 if ( media.view.l10n ) { 7 8 l10n = media.view.l10n; 8 9 } else { … … 11 12 } 12 13 13 14 /** 15 * wp.media.controller.EditAttachmentMetadata 16 * 14 17 * A state for editing an attachment's metadata. 15 18 * 16 19 * @constructor … … 20 23 media.controller.EditAttachmentMetadata = media.controller.State.extend({ 21 24 defaults: { 22 25 id: 'edit-attachment', 26 // Title string passed to the frame's title region view. 23 27 title: l10n.attachmentDetails, 24 28 // Region mode defaults. 25 menu: false,26 29 content: 'edit-metadata', 27 28 url: '' 29 }, 30 31 _ready: function() {}, 32 33 /** 34 * Override media.controller.State._postActivate, since this state doesn't 35 * include the regions expected there. 36 */ 37 _postActivate: function() { 38 this.frame.on( 'title:render:default', this._renderTitle, this ); 39 40 this._title(); 41 this._content(); 42 }, 43 44 /** 45 * @access private 46 */ 47 _title: function() { 48 this.frame.title.render( this.get('titleMode') || 'default' ); 49 }, 50 /** 51 * @access private 52 */ 53 _renderTitle: function( view ) { 54 view.$el.text( this.get('title') || '' ); 55 }, 56 57 _content: function() { 58 var mode = this.get( 'content' ); 59 if ( mode ) { 60 this.frame.content.render( mode ); 61 } 30 menu: false, 31 toolbar: false, 32 router: false 62 33 } 63 34 }); 64 35 … … 87 58 title: '', 88 59 modal: false, 89 60 selection: [], 90 library: {}, 61 library: {}, // Options hash for the query to the media library. 91 62 multiple: 'add', 92 63 state: 'library', 93 64 uploader: true, 94 mode: [ 'grid' , 'edit']65 mode: [ 'grid' ] 95 66 }); 96 67 97 68 $(document).on( 'click', '.add-new-h2', _.bind( this.addNewClickHandler, this ) ); 98 $(document).on( 'screen:options:open', _.bind( this.screenOptionsOpen, this ) );99 $(document).on( 'screen:options:close', _.bind( this.screenOptionsClose, this ) );100 69 101 70 // Ensure core and media grid view UI is enabled. 102 this.$el.addClass('wp-core-ui media-grid-view');71 this.$el.addClass('wp-core-ui'); 103 72 104 73 // Force the uploader off if the upload limit has been exceeded or 105 74 // if the browser isn't supported. … … 122 91 this.options.uploader = false; 123 92 } 124 93 125 /** 126 * call 'initialize' directly on the parent class 127 */ 94 // Call 'initialize' directly on the parent class. 128 95 media.view.MediaFrame.prototype.initialize.apply( this, arguments ); 129 96 130 // Since we're not using the default modal built into 131 // a media frame, append our $element to the supplied container. 97 // Append the frame view directly the supplied container. 132 98 this.$el.appendTo( this.options.container ); 133 99 134 this.createSelection();135 100 this.createStates(); 136 this.bind Handlers();101 this.bindRegionModeHandlers(); 137 102 this.render(); 138 103 139 104 // Update the URL when entering search string (at most once per second) … … 145 110 self.gridRouter.navigate( self.gridRouter.baseUrl( url ) ); 146 111 }, 1000 ) ); 147 112 113 // This is problematic. 148 114 _.delay( _.bind( this.createRouter, this ), 1000 ); 149 115 }, 150 116 151 screenOptionsOpen: function() {152 this.$el.addClass( 'media-grid-view-options' );153 },154 155 screenOptionsClose: function() {156 this.$el.removeClass( 'media-grid-view-options' );157 },158 159 117 createRouter: function() { 160 118 this.gridRouter = new media.view.MediaFrame.Manage.Router(); 161 119 … … 168 126 } 169 127 }, 170 128 171 createSelection: function() { 172 var selection = this.options.selection; 173 174 if ( ! (selection instanceof media.model.Selection) ) { 175 this.options.selection = new media.model.Selection( selection, { 176 multiple: this.options.multiple 177 }); 178 } 179 180 this._selection = { 181 attachments: new media.model.Attachments(), 182 difference: [] 183 }; 184 }, 185 129 /** 130 * Create the default states for the frame. 131 */ 186 132 createStates: function() { 187 133 var options = this.options; 188 134 … … 196 142 library: media.query( options.library ), 197 143 multiple: options.multiple, 198 144 title: options.title, 199 priority: 20,200 201 router: false,202 145 content: 'browse', 203 146 204 147 filterable: 'mime-types' … … 206 149 ]); 207 150 }, 208 151 209 bindHandlers: function() { 210 this.on( 'content:create:browse', this.browseContent, this ); 211 this.on( 'content:render:edit-image', this.editImageContent, this ); 152 /** 153 * Bind region mode activation events to proper handlers. 154 */ 155 bindRegionModeHandlers: function() { 156 this.on( 'content:create:browse', this.createViewForContentRegionInBrowseMode, this ); 212 157 213 158 // Handle a frame-level event for editing an attachment. 214 this.on( 'edit:attachment', this. editAttachment, this );159 this.on( 'edit:attachment', this.openEditAttachmentModal, this ); 215 160 }, 216 161 162 /** 163 * Click handler for the `Add New` button. 164 */ 217 165 addNewClickHandler: function( event ) { 218 166 event.preventDefault(); 219 167 this.trigger( 'toggle:upload:attachment' ); … … 222 170 /** 223 171 * Open the Edit Attachment modal. 224 172 */ 225 editAttachment: function( model ) {173 openEditAttachmentModal: function( model ) { 226 174 // Create a new EditAttachment frame, passing along the library and the attachment model. 227 175 wp.media( { 228 176 frame: 'edit-attachments', … … 233 181 }, 234 182 235 183 /** 236 * C ontent184 * Create an attachments browser view within the content region. 237 185 * 238 * @param {Object} content 186 * @param {Object} contentRegion Basic object with a `view` property, which 187 * should be set with the proper region view. 239 188 * @this wp.media.controller.Region 240 189 */ 241 browseContent: function( content) {190 createViewForContentRegionInBrowseMode: function( contentRegion ) { 242 191 var state = this.state(); 243 192 244 193 // Browse our library of attachments. 245 content .view = new media.view.AttachmentsBrowser({194 contentRegion.view = new media.view.AttachmentsBrowser({ 246 195 controller: this, 247 196 collection: state.get('library'), 248 197 selection: state.get('selection'), … … 259 208 260 209 AttachmentView: state.get('AttachmentView') 261 210 }); 262 },263 264 editImageContent: function() {265 var image = this.state().get('image'),266 view = new media.view.EditImage( { model: image, controller: this } ).render();267 268 this.content.set( view );269 270 // after creating the wrapper view, load the actual editor via an ajax call271 view.loadEditor();272 273 211 } 274 212 }); 275 213 214 /** 215 * A similar view to media.view.Attachment.Details 216 * for use in the Edit Attachment modal. 217 * 218 * @constructor 219 * @augments wp.media.view.Attachment.Details 220 * @augments wp.media.view.Attachment 221 * @augments wp.media.View 222 * @augments wp.Backbone.View 223 * @augments Backbone.View 224 */ 276 225 media.view.Attachment.Details.TwoColumn = media.view.Attachment.Details.extend({ 277 template: wp.template( 'attachment-details-two-column' ), 278 279 events: { 280 'change [data-setting]': 'updateSetting', 281 'change [data-setting] input': 'updateSetting', 282 'change [data-setting] select': 'updateSetting', 283 'change [data-setting] textarea': 'updateSetting', 284 'click .delete-attachment': 'deleteAttachment', 285 'click .trash-attachment': 'trashAttachment', 286 'click .edit-attachment': 'editAttachment', 287 'click .refresh-attachment': 'refreshAttachment', 288 'click .edit-image': 'handleEditImageClick' 289 }, 290 291 initialize: function() { 292 if ( ! this.model ) { 293 return; 294 } 295 296 this.$el.attr('aria-label', this.model.get( 'title' ) ).attr( 'aria-checked', false ); 297 298 this.model.on( 'change:title', this._syncTitle, this ); 299 this.model.on( 'change:caption', this._syncCaption, this ); 300 this.model.on( 'change:percent', this.progress, this ); 301 this.model.on( 'change:album', this._syncAlbum, this ); 302 this.model.on( 'change:artist', this._syncArtist, this ); 303 304 // Update the selection. 305 this.model.on( 'add', this.select, this ); 306 this.model.on( 'remove', this.deselect, this ); 307 this.model.on( 'sync', this.afterDelete, this ); 308 }, 226 template: media.template( 'attachment-details-two-column' ), 309 227 310 228 preDestroy: function( event ) { 311 229 event.preventDefault(); … … 324 242 media.view.Attachment.Details.prototype.deleteAttachment.apply( this, arguments ); 325 243 }, 326 244 327 handleEditImageClick: function() { 245 editAttachment: function( event ) { 246 event.preventDefault(); 328 247 this.controller.setState( 'edit-image' ); 329 248 }, 330 249 250 /** 251 * Noop this from parent class, doesn't apply here. 252 */ 253 toggleSelectionHandler: function() {}, 254 331 255 afterDelete: function( model ) { 332 256 if ( ! model.destroyed ) { 333 257 return; … … 359 283 }); 360 284 361 285 /** 362 * A router for handling the browser history and application state 286 * A router for handling the browser history and application state. 287 * 288 * @constructor 289 * @augments Backbone.Router 363 290 */ 364 291 media.view.MediaFrame.Manage.Router = Backbone.Router.extend({ 365 292 routes: { 366 'upload.php?item=:slug': 'show item',293 'upload.php?item=:slug': 'showItem', 367 294 'upload.php?search=:query': 'search', 368 295 ':default': 'defaultRoute' 369 296 }, … … 381 308 }, 382 309 383 310 // Show the modal with a specific item 384 show item: function( query ) {311 showItem: function( query ) { 385 312 var library = media.frame.state().get('library'); 386 313 387 314 // Remove existing modal if present … … 410 337 * Opens in a modal by default. 411 338 * 412 339 * Requires an attachment model to be passed in the options hash under `model`. 340 * 341 * @constructor 342 * @augments wp.media.view.Frame 343 * @augments wp.media.View 344 * @augments wp.Backbone.View 345 * @augments Backbone.View 346 * @mixes wp.media.controller.StateMachine 413 347 */ 414 348 media.view.MediaFrame.EditAttachments = media.view.MediaFrame.extend({ 415 349 … … 443 377 444 378 this.createStates(); 445 379 446 this.on( 'content: render:edit-metadata', this.editMetadataContent, this );447 this.on( 'content: render:edit-image', this.editImageContentUgh, this );380 this.on( 'content:create:edit-metadata', this.createViewForContentRegionInEditMetadataMode, this ); 381 this.on( 'content:create:edit-image', this.createViewForContentRegionInEditImageMode, this ); 448 382 this.on( 'close', this.detach ); 449 383 450 384 // Bind default title creation. … … 473 407 self.resetRoute(); 474 408 } ); 475 409 410 // Set this frame as the modal's content. 476 411 this.modal.content( this ); 477 412 this.modal.open(); 478 413 } … … 497 432 /** 498 433 * Content region rendering callback for the `edit-metadata` mode. 499 434 */ 500 editMetadataContent: function() {501 varview = new media.view.Attachment.Details.TwoColumn({435 createViewForContentRegionInEditMetadataMode: function( contentRegion ) { 436 contentRegion.view = new media.view.Attachment.Details.TwoColumn({ 502 437 controller: this, 503 438 model: this.model 504 439 }); 505 this.content.set( view );506 440 // Update browser url when navigating media details 507 441 if ( this.model ) { 508 442 this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) ); 509 } else {510 this.resetRoute();511 443 } 512 444 }, 513 445 514 446 /** 515 * For some reason the view doesn't exist in the DOM yet, don't have the 516 * patience to track this down right now. 447 * Render the EditImage view into the frame's content region. 517 448 */ 518 editImageContentUgh: function() { 519 _.defer( _.bind( this.editImageContent, this ) ); 449 createViewForContentRegionInEditImageMode: function( contentRegion ) { 450 contentRegion.view = new media.view.EditImage( { model: this.model, controller: this } ); 451 // Defer a call to load the editor, which 452 // requires DOM elements to exist. 453 _.defer( _.bind( contentRegion.view.loadEditor, contentRegion.view ) ); 520 454 }, 521 455 522 456 /** 523 * Render the EditImage view into the frame's content region. 457 * Close this modal and immediately open another one. 458 * 459 * Allows for quickly swapping out the attachment being edited. 524 460 */ 525 editImageContent: function() {526 var view = new media.view.EditImage( { model: this.model, controller: this } ).render();527 528 this.content.set( view );529 530 // after creating the wrapper view, load the actual editor via an ajax call531 view.loadEditor();532 },533 534 461 resetContent: function() { 535 462 this.modal.close(); 536 463 wp.media( { … … 603 530 } 604 531 }); 605 532 533 /** 534 * Controller for bulk selection. 535 */ 606 536 media.view.BulkSelection = media.View.extend({ 607 537 className: 'bulk-select', 608 538 … … 628 558 } 629 559 }); 630 560 561 /** 562 * Bulk Selection dropdown view. 563 * 564 * @constructor 565 * @augments wp.media.View 566 * @augments wp.Backbone.View 567 * @augments Backbone.View 568 */ 631 569 media.view.BulkSelectionActionDropdown = media.View.extend({ 632 570 tagName: 'select', 633 571 … … 637 575 this.$el.append( $('<option></option>').val( '' ).html( l10n.bulkActions ) ) 638 576 .append( $('<option></option>').val( 'delete' ).html( l10n.deletePermanently ) ); 639 577 this.$el.prop( 'disabled', true ); 640 this.$el.on( 'change', _.bind( this. toggleChange, this ) );578 this.$el.on( 'change', _.bind( this.changeHandler, this ) ); 641 579 }, 642 580 643 toggleChange: function() { 581 /** 582 * Change handler for the dropdown. 583 * 584 * Sets the bulk selection controller's currentAction. 585 */ 586 changeHandler: function() { 644 587 this.controller.model.set( { 'currentAction': this.$el.val() } ); 645 588 }, 589 590 /** 591 * Enable or disable the dropdown if attachments have been selected. 592 */ 646 593 enabled: function() { 647 594 var disabled = ! this.controller.controller.state().get('selection').length; 648 595 this.$el.prop( 'disabled', disabled ); 649 596 } 650 597 }); 651 598 599 /** 600 * Bulk Selection dropdown view. 601 * 602 * @constructor 603 * 604 * @augments wp.media.view.Button 605 * @augments wp.media.View 606 * @augments wp.Backbone.View 607 * @augments Backbone.View 608 */ 652 609 media.view.BulkSelectionActionButton = media.view.Button.extend({ 653 610 tagName: 'button', 654 611 … … 658 615 this.listenTo( this.controller.model, 'change', this.enabled, this ); 659 616 this.listenTo( this.controller.controller.state().get( 'selection' ), 'add remove reset', _.bind( this.enabled, this ) ); 660 617 }, 661 618 /** 619 * Button click handler. 620 */ 662 621 click: function() { 663 622 var selection = this.controller.controller.state().get('selection'); 664 623 media.view.Button.prototype.click.apply( this, arguments ); … … 672 631 673 632 this.enabled(); 674 633 }, 675 634 /** 635 * Enable or disable the button depending if a bulk action is selected 636 * in the bulk select dropdown, and if attachments have been selected. 637 */ 676 638 enabled: function() { 677 639 var currentAction = this.controller.model.get( 'currentAction' ), 678 640 selection = this.controller.controller.state().get('selection'), … … 681 643 } 682 644 }); 683 645 684 }(jQuery, _, Backbone, wp)); 646 }(jQuery, _, Backbone, wp)); 647 No newline at end of file -
src/wp-includes/js/media-views.js
diff --git a/src/wp-includes/js/media-views.js b/src/wp-includes/js/media-views.js index c57df05..ce8eadf 100644
a b 1685 1685 */ 1686 1686 media.view.Frame = media.View.extend({ 1687 1687 initialize: function() { 1688 _.defaults( this.options, { 1689 mode: [ 'select' ] 1690 }); 1688 1691 this._createRegions(); 1689 1692 this._createStates(); 1693 this._createModes(); 1690 1694 }, 1691 1695 1692 1696 _createRegions: function() { … … 1721 1725 this.states.add( this.options.states ); 1722 1726 } 1723 1727 }, 1728 _createModes: function() { 1729 // Store active "modes" that the frame is in. Unrelated to region modes. 1730 this.activeModes = new Backbone.Collection(); 1731 this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) ); 1732 1733 _.each( this.options.mode, function( mode ) { 1734 this.activateMode( mode ); 1735 }, this ); 1736 }, 1724 1737 /** 1725 1738 * @returns {wp.media.view.Frame} Returns itself to allow chaining 1726 1739 */ 1727 1740 reset: function() { 1728 1741 this.states.invoke( 'trigger', 'reset' ); 1729 1742 return this; 1743 }, 1744 /** 1745 * Map activeMode collection events to the frame. 1746 */ 1747 triggerModeEvents: function( model, collection, options ) { 1748 var collectionEvent, 1749 modeEventMap = { 1750 add: 'activate', 1751 remove: 'deactivate' 1752 }, 1753 eventToTrigger; 1754 // Probably a better way to do this. 1755 _.each( options, function( value, key ) { 1756 if ( value ) { 1757 collectionEvent = key; 1758 } 1759 } ); 1760 1761 if ( ! _.has( modeEventMap, collectionEvent ) ) { 1762 return; 1763 } 1764 1765 eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent]; 1766 this.trigger( eventToTrigger ); 1767 }, 1768 /** 1769 * Activate a mode on the frame. 1770 * 1771 * @param string mode Mode ID. 1772 * @returns {this} Returns itself to allow chaining. 1773 */ 1774 activateMode: function( mode ) { 1775 // Bail if the mode is already active. 1776 if ( this.isModeActive( mode ) ) { 1777 return; 1778 } 1779 this.activeModes.add( [ { id: mode } ] ); 1780 // Add a CSS class to the frame so elements can be styled for the mode. 1781 this.$el.addClass( 'mode-' + mode ); 1782 /** 1783 * Frame mode activation event. 1784 * 1785 * @event this#{mode}:activate 1786 */ 1787 this.trigger( mode + ':activate' ); 1788 1789 return this; 1790 }, 1791 /** 1792 * Deactivate a mode on the frame. 1793 * 1794 * @param string mode Mode ID. 1795 * @returns {this} Returns itself to allow chaining. 1796 */ 1797 deactivateMode: function( mode ) { 1798 // Bail if the mode isn't active. 1799 if ( ! this.isModeActive( mode ) ) { 1800 return; 1801 } 1802 this.activeModes.remove( this.activeModes.where( { id: mode } ) ); 1803 this.$el.removeClass( 'mode-' + mode ); 1804 /** 1805 * Frame mode deactivation event. 1806 * 1807 * @event this#{mode}:deactivate 1808 */ 1809 this.trigger( mode + ':deactivate' ); 1810 1811 return this; 1812 }, 1813 /** 1814 * Check if a mode is enabled on the frame. 1815 * 1816 * @param string mode Mode ID. 1817 * @return bool 1818 */ 1819 isModeActive: function( mode ) { 1820 return Boolean( this.activeModes.where( { id: mode } ).length ); 1730 1821 } 1731 1822 }); 1732 1823 … … 1759 1850 _.defaults( this.options, { 1760 1851 title: '', 1761 1852 modal: true, 1762 uploader: true, 1763 mode: [ 'select' ] 1853 uploader: true 1764 1854 }); 1765 1855 1766 1856 // Ensure core UI is enabled. … … 1776 1866 this.modal.content( this ); 1777 1867 } 1778 1868 1779 // Store active "modes" that the frame is in. Unrelated to region modes.1780 this.activeModes = new Backbone.Collection();1781 this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) );1782 1783 _.each( this.options.mode, function( mode ) {1784 this.activateMode( mode );1785 }, this );1786 1787 1869 // Force the uploader off if the upload limit has been exceeded or 1788 1870 // if the browser isn't supported. 1789 1871 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) { … … 1948 2030 1949 2031 window.tb_remove = this._tb_remove; 1950 2032 delete this._tb_remove; 1951 },1952 1953 /**1954 * Map activeMode collection events to the frame.1955 */1956 triggerModeEvents: function( model, collection, options ) {1957 var collectionEvent,1958 modeEventMap = {1959 add: 'activate',1960 remove: 'deactivate'1961 },1962 eventToTrigger;1963 // Probably a better way to do this.1964 _.each( options, function( value, key ) {1965 if ( value ) {1966 collectionEvent = key;1967 }1968 } );1969 1970 if ( ! _.has( modeEventMap, collectionEvent ) )1971 return;1972 1973 eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent];1974 this.trigger( eventToTrigger );1975 },1976 /**1977 * Activate a mode on the frame.1978 *1979 * @param string mode Mode ID.1980 * @returns {this} Returns itself to allow chaining.1981 */1982 activateMode: function( mode ) {1983 // Bail if the mode is already active.1984 if ( this.isModeActive( mode ) ) {1985 return;1986 }1987 this.activeModes.add( [ { id: mode } ] );1988 // Add a css class to the frame for anything that needs to be styled1989 // for the mode.1990 this.$el.addClass( 'mode-' + mode );1991 /**1992 * Frame mode activation event.1993 *1994 * @event this#{mode}:activate1995 */1996 this.trigger( mode + ':activate' );1997 1998 return this;1999 },2000 /**2001 * Deactivate a mode on the frame.2002 *2003 * @param string mode Mode ID.2004 * @returns {this} Returns itself to allow chaining.2005 */2006 deactivateMode: function( mode ) {2007 // Bail if the mode isn't active.2008 if ( ! this.isModeActive( mode ) ) {2009 return;2010 }2011 this.activeModes.remove( this.activeModes.where( { id: mode } ) );2012 this.$el.removeClass( 'mode-' + mode );2013 /**2014 * Frame mode deactivation event.2015 *2016 * @event this#{mode}:deactivate2017 */2018 this.trigger( mode + ':deactivate' );2019 2020 return this;2021 },2022 /**2023 * Check if a mode is enabled on the frame.2024 *2025 * @param string mode Mode ID.2026 * @return bool2027 */2028 isModeActive: function( mode ) {2029 return Boolean( this.activeModes.where( { id: mode } ).length );2030 2033 } 2031 2034 }); 2032 2035 … … 4631 4634 buttons: {}, 4632 4635 4633 4636 initialize: function() { 4634 var selection = this.options.selection; 4637 var selection = this.options.selection, 4638 options = _.defaults( this.options, { 4639 rerenderOnModelChange: true 4640 } ); 4641 this.$el.attr( 'aria-label', this.model.get( 'title' ) ).attr( 'aria-checked', false ); 4635 4642 4636 this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false); 4637 this.model.on( 'change', this.render, this ); 4643 if ( options.rerenderOnModelChange ) { 4644 this.model.on( 'change', this.render, this ); 4645 } 4638 4646 this.model.on( 'change:title', this._syncTitle, this ); 4639 4647 this.model.on( 'change:caption', this._syncCaption, this ); 4640 4648 this.model.on( 'change:artist', this._syncArtist, this ); … … 4646 4654 this.model.on( 'remove', this.deselect, this ); 4647 4655 if ( selection ) { 4648 4656 selection.on( 'reset', this.updateSelect, this ); 4657 // Update the model's details view. 4658 this.model.on( 'selection:single selection:unsingle', this.details, this ); 4659 this.details( this.model, this.controller.state().get('selection') ); 4649 4660 } 4650 4651 // Update the model's details view.4652 this.model.on( 'selection:single selection:unsingle', this.details, this );4653 this.details( this.model, this.controller.state().get('selection') );4654 4661 }, 4655 4662 /** 4656 4663 * @returns {wp.media.view.Attachment} Returns itself to allow chaining … … 4752 4759 } 4753 4760 4754 4761 // In the grid view, bubble up an edit:attachment event to the controller. 4755 if ( this.controller. activeModes.where( { id: 'edit' } ).length) {4762 if ( this.controller.isModeActive( 'grid' ) ) { 4756 4763 this.controller.trigger( 'edit:attachment', this.model ); 4757 4764 return; 4758 4765 } … … 6318 6325 }, 6319 6326 6320 6327 initialize: function() { 6328 this.options = _.defaults( this.options, { 6329 rerenderOnModelChange: false 6330 }); 6321 6331 /** 6322 6332 * call 'initialize' directly on the parent class 6323 6333 */ 6324 6334 media.view.Attachment.prototype.initialize.apply( this, arguments ); 6325 6335 }, 6326 6336 /** 6327 * @returns {wp.media.view..Attachment.Details} Returns itself to allow chaining6328 */6329 render: function() {6330 /**6331 * call 'render' directly on the parent class6332 */6333 media.view.Attachment.prototype.render.apply( this, arguments );6334 return this;6335 },6336 /**6337 6337 * @param {Object} event 6338 6338 */ 6339 6339 deleteAttachment: function( event ) { … … 6379 6379 this.model.fetch(); 6380 6380 }, 6381 6381 /** 6382 * When reverse tabbing(shift+tab) out of the right details panel, deliver 6383 * the focus to the item in the list that was being edited. 6384 * 6382 6385 * @param {Object} event 6383 6386 */ 6384 6387 toggleSelectionHandler: function( event ) { 6385 // Reverse tabbing out of the right details panel6386 // should take me back to the item in the list that was being edited.6387 6388 if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === $( ':tabbable', this.$el ).filter( ':first' )[0] ) { 6388 6389 $('.attachments-browser .details').focus(); 6389 6390 return false; -
src/wp-includes/media-template.php
diff --git a/src/wp-includes/media-template.php b/src/wp-includes/media-template.php index c71086e..5ccc5ff 100644
a b function wp_print_media_templates() { 263 263 </script> 264 264 265 265 <script type="text/html" id="tmpl-attachment-details-two-column"> 266 <h3>267 <?php _e('Attachment Details'); ?>268 </h3>269 266 <div class="attachment-media-view"> 270 267 <div class="thumbnail thumbnail-{{ data.type }}"> 271 268 <# if ( data.uploading ) { #> … … function wp_print_media_templates() { 294 291 295 292 <div class="attachment-actions"> 296 293 <# if ( 'image' === data.type && ! data.uploading ) { #> 297 <a class="button edit- image" href="#"><?php _e( 'Edit Image' ); ?></a>294 <a class="button edit-attachment" href="#"><?php _e( 'Edit Image' ); ?></a> 298 295 <# } #> 299 296 300 297 <# if ( ! data.uploading && data.can.remove ) { #>