media-grid.js (29533B)
1 /******/ (function(modules) { // webpackBootstrap 2 /******/ // The module cache 3 /******/ var installedModules = {}; 4 /******/ 5 /******/ // The require function 6 /******/ function __webpack_require__(moduleId) { 7 /******/ 8 /******/ // Check if module is in cache 9 /******/ if(installedModules[moduleId]) { 10 /******/ return installedModules[moduleId].exports; 11 /******/ } 12 /******/ // Create a new module (and put it into the cache) 13 /******/ var module = installedModules[moduleId] = { 14 /******/ i: moduleId, 15 /******/ l: false, 16 /******/ exports: {} 17 /******/ }; 18 /******/ 19 /******/ // Execute the module function 20 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 /******/ 22 /******/ // Flag the module as loaded 23 /******/ module.l = true; 24 /******/ 25 /******/ // Return the exports of the module 26 /******/ return module.exports; 27 /******/ } 28 /******/ 29 /******/ 30 /******/ // expose the modules object (__webpack_modules__) 31 /******/ __webpack_require__.m = modules; 32 /******/ 33 /******/ // expose the module cache 34 /******/ __webpack_require__.c = installedModules; 35 /******/ 36 /******/ // define getter function for harmony exports 37 /******/ __webpack_require__.d = function(exports, name, getter) { 38 /******/ if(!__webpack_require__.o(exports, name)) { 39 /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 40 /******/ } 41 /******/ }; 42 /******/ 43 /******/ // define __esModule on exports 44 /******/ __webpack_require__.r = function(exports) { 45 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 46 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 47 /******/ } 48 /******/ Object.defineProperty(exports, '__esModule', { value: true }); 49 /******/ }; 50 /******/ 51 /******/ // create a fake namespace object 52 /******/ // mode & 1: value is a module id, require it 53 /******/ // mode & 2: merge all properties of value into the ns 54 /******/ // mode & 4: return value when already ns object 55 /******/ // mode & 8|1: behave like require 56 /******/ __webpack_require__.t = function(value, mode) { 57 /******/ if(mode & 1) value = __webpack_require__(value); 58 /******/ if(mode & 8) return value; 59 /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 60 /******/ var ns = Object.create(null); 61 /******/ __webpack_require__.r(ns); 62 /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 63 /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 64 /******/ return ns; 65 /******/ }; 66 /******/ 67 /******/ // getDefaultExport function for compatibility with non-harmony modules 68 /******/ __webpack_require__.n = function(module) { 69 /******/ var getter = module && module.__esModule ? 70 /******/ function getDefault() { return module['default']; } : 71 /******/ function getModuleExports() { return module; }; 72 /******/ __webpack_require__.d(getter, 'a', getter); 73 /******/ return getter; 74 /******/ }; 75 /******/ 76 /******/ // Object.prototype.hasOwnProperty.call 77 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 78 /******/ 79 /******/ // __webpack_public_path__ 80 /******/ __webpack_require__.p = ""; 81 /******/ 82 /******/ 83 /******/ // Load entry module and return exports 84 /******/ return __webpack_require__(__webpack_require__.s = 1); 85 /******/ }) 86 /************************************************************************/ 87 /******/ ({ 88 89 /***/ 1: 90 /***/ (function(module, exports, __webpack_require__) { 91 92 module.exports = __webpack_require__("LRQ5"); 93 94 95 /***/ }), 96 97 /***/ "1lLZ": 98 /***/ (function(module, exports) { 99 100 var Button = wp.media.view.Button, 101 DeleteSelected = wp.media.view.DeleteSelectedButton, 102 DeleteSelectedPermanently; 103 104 /** 105 * wp.media.view.DeleteSelectedPermanentlyButton 106 * 107 * When MEDIA_TRASH is true, a button that handles bulk Delete Permanently logic 108 * 109 * @memberOf wp.media.view 110 * 111 * @class 112 * @augments wp.media.view.DeleteSelectedButton 113 * @augments wp.media.view.Button 114 * @augments wp.media.View 115 * @augments wp.Backbone.View 116 * @augments Backbone.View 117 */ 118 DeleteSelectedPermanently = DeleteSelected.extend(/** @lends wp.media.view.DeleteSelectedPermanentlyButton.prototype */{ 119 initialize: function() { 120 DeleteSelected.prototype.initialize.apply( this, arguments ); 121 this.controller.on( 'select:activate', this.selectActivate, this ); 122 this.controller.on( 'select:deactivate', this.selectDeactivate, this ); 123 }, 124 125 filterChange: function( model ) { 126 this.canShow = ( 'trash' === model.get( 'status' ) ); 127 }, 128 129 selectActivate: function() { 130 this.toggleDisabled(); 131 this.$el.toggleClass( 'hidden', ! this.canShow ); 132 }, 133 134 selectDeactivate: function() { 135 this.toggleDisabled(); 136 this.$el.addClass( 'hidden' ); 137 }, 138 139 render: function() { 140 Button.prototype.render.apply( this, arguments ); 141 this.selectActivate(); 142 return this; 143 } 144 }); 145 146 module.exports = DeleteSelectedPermanently; 147 148 149 /***/ }), 150 151 /***/ "FcM5": 152 /***/ (function(module, exports) { 153 154 var Details = wp.media.view.Attachment.Details, 155 TwoColumn; 156 157 /** 158 * wp.media.view.Attachment.Details.TwoColumn 159 * 160 * A similar view to media.view.Attachment.Details 161 * for use in the Edit Attachment modal. 162 * 163 * @memberOf wp.media.view.Attachment.Details 164 * 165 * @class 166 * @augments wp.media.view.Attachment.Details 167 * @augments wp.media.view.Attachment 168 * @augments wp.media.View 169 * @augments wp.Backbone.View 170 * @augments Backbone.View 171 */ 172 TwoColumn = Details.extend(/** @lends wp.media.view.Attachment.Details.TowColumn.prototype */{ 173 template: wp.template( 'attachment-details-two-column' ), 174 175 initialize: function() { 176 this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) ); 177 178 Details.prototype.initialize.apply( this, arguments ); 179 }, 180 181 editAttachment: function( event ) { 182 if ( event ) { 183 event.preventDefault(); 184 } 185 this.controller.content.mode( 'edit-image' ); 186 }, 187 188 /** 189 * Noop this from parent class, doesn't apply here. 190 */ 191 toggleSelectionHandler: function() {} 192 193 }); 194 195 module.exports = TwoColumn; 196 197 198 /***/ }), 199 200 /***/ "Ffsb": 201 /***/ (function(module, exports) { 202 203 204 var Button = wp.media.view.Button, 205 l10n = wp.media.view.l10n, 206 SelectModeToggle; 207 208 /** 209 * wp.media.view.SelectModeToggleButton 210 * 211 * @memberOf wp.media.view 212 * 213 * @class 214 * @augments wp.media.view.Button 215 * @augments wp.media.View 216 * @augments wp.Backbone.View 217 * @augments Backbone.View 218 */ 219 SelectModeToggle = Button.extend(/** @lends wp.media.view.SelectModeToggle.prototype */{ 220 initialize: function() { 221 _.defaults( this.options, { 222 size : '' 223 } ); 224 225 Button.prototype.initialize.apply( this, arguments ); 226 this.controller.on( 'select:activate select:deactivate', this.toggleBulkEditHandler, this ); 227 this.controller.on( 'selection:action:done', this.back, this ); 228 }, 229 230 back: function () { 231 this.controller.deactivateMode( 'select' ).activateMode( 'edit' ); 232 }, 233 234 click: function() { 235 Button.prototype.click.apply( this, arguments ); 236 if ( this.controller.isModeActive( 'select' ) ) { 237 this.back(); 238 } else { 239 this.controller.deactivateMode( 'edit' ).activateMode( 'select' ); 240 } 241 }, 242 243 render: function() { 244 Button.prototype.render.apply( this, arguments ); 245 this.$el.addClass( 'select-mode-toggle-button' ); 246 return this; 247 }, 248 249 toggleBulkEditHandler: function() { 250 var toolbar = this.controller.content.get().toolbar, children; 251 252 children = toolbar.$( '.media-toolbar-secondary > *, .media-toolbar-primary > *' ); 253 254 // @todo The Frame should be doing all of this. 255 if ( this.controller.isModeActive( 'select' ) ) { 256 this.model.set( { 257 size: 'large', 258 text: l10n.cancel 259 } ); 260 children.not( '.spinner, .media-button' ).hide(); 261 this.$el.show(); 262 toolbar.$el.addClass( 'media-toolbar-mode-select' ); 263 toolbar.$( '.delete-selected-button' ).removeClass( 'hidden' ); 264 } else { 265 this.model.set( { 266 size: '', 267 text: l10n.bulkSelect 268 } ); 269 this.controller.content.get().$el.removeClass( 'fixed' ); 270 toolbar.$el.css( 'width', '' ); 271 toolbar.$el.removeClass( 'media-toolbar-mode-select' ); 272 toolbar.$( '.delete-selected-button' ).addClass( 'hidden' ); 273 children.not( '.media-button' ).show(); 274 this.controller.state().get( 'selection' ).reset(); 275 } 276 } 277 }); 278 279 module.exports = SelectModeToggle; 280 281 282 /***/ }), 283 284 /***/ "HUrf": 285 /***/ (function(module, exports) { 286 287 var View = wp.media.View, 288 EditImage = wp.media.view.EditImage, 289 Details; 290 291 /** 292 * wp.media.view.EditImage.Details 293 * 294 * @memberOf wp.media.view.EditImage 295 * 296 * @class 297 * @augments wp.media.view.EditImage 298 * @augments wp.media.View 299 * @augments wp.Backbone.View 300 * @augments Backbone.View 301 */ 302 Details = EditImage.extend(/** @lends wp.media.view.EditImage.Details.prototype */{ 303 initialize: function( options ) { 304 this.editor = window.imageEdit; 305 this.frame = options.frame; 306 this.controller = options.controller; 307 View.prototype.initialize.apply( this, arguments ); 308 }, 309 310 back: function() { 311 this.frame.content.mode( 'edit-metadata' ); 312 }, 313 314 save: function() { 315 this.model.fetch().done( _.bind( function() { 316 this.frame.content.mode( 'edit-metadata' ); 317 }, this ) ); 318 } 319 }); 320 321 module.exports = Details; 322 323 324 /***/ }), 325 326 /***/ "LRQ5": 327 /***/ (function(module, exports, __webpack_require__) { 328 329 /** 330 * @output wp-includes/js/media-grid.js 331 */ 332 333 var media = wp.media; 334 335 media.controller.EditAttachmentMetadata = __webpack_require__( "ZJBI" ); 336 media.view.MediaFrame.Manage = __webpack_require__( "lH8y" ); 337 media.view.Attachment.Details.TwoColumn = __webpack_require__( "FcM5" ); 338 media.view.MediaFrame.Manage.Router = __webpack_require__( "OMfl" ); 339 media.view.EditImage.Details = __webpack_require__( "HUrf" ); 340 media.view.MediaFrame.EditAttachments = __webpack_require__( "wQX5" ); 341 media.view.SelectModeToggleButton = __webpack_require__( "Ffsb" ); 342 media.view.DeleteSelectedButton = __webpack_require__( "nD7t" ); 343 media.view.DeleteSelectedPermanentlyButton = __webpack_require__( "1lLZ" ); 344 345 346 /***/ }), 347 348 /***/ "OMfl": 349 /***/ (function(module, exports) { 350 351 /** 352 * wp.media.view.MediaFrame.Manage.Router 353 * 354 * A router for handling the browser history and application state. 355 * 356 * @memberOf wp.media.view.MediaFrame.Manage 357 * 358 * @class 359 * @augments Backbone.Router 360 */ 361 var Router = Backbone.Router.extend(/** @lends wp.media.view.MediaFrame.Manage.Router.prototype */{ 362 routes: { 363 'upload.php?item=:slug&mode=edit': 'editItem', 364 'upload.php?item=:slug': 'showItem', 365 'upload.php?search=:query': 'search', 366 'upload.php': 'reset' 367 }, 368 369 // Map routes against the page URL. 370 baseUrl: function( url ) { 371 return 'upload.php' + url; 372 }, 373 374 reset: function() { 375 var frame = wp.media.frames.edit; 376 377 if ( frame ) { 378 frame.close(); 379 } 380 }, 381 382 // Respond to the search route by filling the search field and triggering the input event. 383 search: function( query ) { 384 jQuery( '#media-search-input' ).val( query ).trigger( 'input' ); 385 }, 386 387 // Show the modal with a specific item. 388 showItem: function( query ) { 389 var media = wp.media, 390 frame = media.frames.browse, 391 library = frame.state().get('library'), 392 item; 393 394 // Trigger the media frame to open the correct item. 395 item = library.findWhere( { id: parseInt( query, 10 ) } ); 396 397 if ( item ) { 398 item.set( 'skipHistory', true ); 399 frame.trigger( 'edit:attachment', item ); 400 } else { 401 item = media.attachment( query ); 402 frame.listenTo( item, 'change', function( model ) { 403 frame.stopListening( item ); 404 frame.trigger( 'edit:attachment', model ); 405 } ); 406 item.fetch(); 407 } 408 }, 409 410 // Show the modal in edit mode with a specific item. 411 editItem: function( query ) { 412 this.showItem( query ); 413 wp.media.frames.edit.content.mode( 'edit-details' ); 414 } 415 }); 416 417 module.exports = Router; 418 419 420 /***/ }), 421 422 /***/ "ZJBI": 423 /***/ (function(module, exports) { 424 425 var l10n = wp.media.view.l10n, 426 EditAttachmentMetadata; 427 428 /** 429 * wp.media.controller.EditAttachmentMetadata 430 * 431 * A state for editing an attachment's metadata. 432 * 433 * @memberOf wp.media.controller 434 * 435 * @class 436 * @augments wp.media.controller.State 437 * @augments Backbone.Model 438 */ 439 EditAttachmentMetadata = wp.media.controller.State.extend(/** @lends wp.media.controller.EditAttachmentMetadata.prototype */{ 440 defaults: { 441 id: 'edit-attachment', 442 // Title string passed to the frame's title region view. 443 title: l10n.attachmentDetails, 444 // Region mode defaults. 445 content: 'edit-metadata', 446 menu: false, 447 toolbar: false, 448 router: false 449 } 450 }); 451 452 module.exports = EditAttachmentMetadata; 453 454 455 /***/ }), 456 457 /***/ "lH8y": 458 /***/ (function(module, exports) { 459 460 var MediaFrame = wp.media.view.MediaFrame, 461 Library = wp.media.controller.Library, 462 463 $ = Backbone.$, 464 Manage; 465 466 /** 467 * wp.media.view.MediaFrame.Manage 468 * 469 * A generic management frame workflow. 470 * 471 * Used in the media grid view. 472 * 473 * @memberOf wp.media.view.MediaFrame 474 * 475 * @class 476 * @augments wp.media.view.MediaFrame 477 * @augments wp.media.view.Frame 478 * @augments wp.media.View 479 * @augments wp.Backbone.View 480 * @augments Backbone.View 481 * @mixes wp.media.controller.StateMachine 482 */ 483 Manage = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Manage.prototype */{ 484 /** 485 * @constructs 486 */ 487 initialize: function() { 488 _.defaults( this.options, { 489 title: '', 490 modal: false, 491 selection: [], 492 library: {}, // Options hash for the query to the media library. 493 multiple: 'add', 494 state: 'library', 495 uploader: true, 496 mode: [ 'grid', 'edit' ] 497 }); 498 499 this.$body = $( document.body ); 500 this.$window = $( window ); 501 this.$adminBar = $( '#wpadminbar' ); 502 // Store the Add New button for later reuse in wp.media.view.UploaderInline. 503 this.$uploaderToggler = $( '.page-title-action' ) 504 .attr( 'aria-expanded', 'false' ) 505 .on( 'click', _.bind( this.addNewClickHandler, this ) ); 506 507 this.$window.on( 'scroll resize', _.debounce( _.bind( this.fixPosition, this ), 15 ) ); 508 509 // Ensure core and media grid view UI is enabled. 510 this.$el.addClass('wp-core-ui'); 511 512 // Force the uploader off if the upload limit has been exceeded or 513 // if the browser isn't supported. 514 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) { 515 this.options.uploader = false; 516 } 517 518 // Initialize a window-wide uploader. 519 if ( this.options.uploader ) { 520 this.uploader = new wp.media.view.UploaderWindow({ 521 controller: this, 522 uploader: { 523 dropzone: document.body, 524 container: document.body 525 } 526 }).render(); 527 this.uploader.ready(); 528 $('body').append( this.uploader.el ); 529 530 this.options.uploader = false; 531 } 532 533 this.gridRouter = new wp.media.view.MediaFrame.Manage.Router(); 534 535 // Call 'initialize' directly on the parent class. 536 MediaFrame.prototype.initialize.apply( this, arguments ); 537 538 // Append the frame view directly the supplied container. 539 this.$el.appendTo( this.options.container ); 540 541 this.createStates(); 542 this.bindRegionModeHandlers(); 543 this.render(); 544 this.bindSearchHandler(); 545 546 wp.media.frames.browse = this; 547 }, 548 549 bindSearchHandler: function() { 550 var search = this.$( '#media-search-input' ), 551 searchView = this.browserView.toolbar.get( 'search' ).$el, 552 listMode = this.$( '.view-list' ), 553 554 input = _.throttle( function (e) { 555 var val = $( e.currentTarget ).val(), 556 url = ''; 557 558 if ( val ) { 559 url += '?search=' + val; 560 this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } ); 561 } 562 }, 1000 ); 563 564 // Update the URL when entering search string (at most once per second). 565 search.on( 'input', _.bind( input, this ) ); 566 567 this.gridRouter 568 .on( 'route:search', function () { 569 var href = window.location.href; 570 if ( href.indexOf( 'mode=' ) > -1 ) { 571 href = href.replace( /mode=[^&]+/g, 'mode=list' ); 572 } else { 573 href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list'; 574 } 575 href = href.replace( 'search=', 's=' ); 576 listMode.prop( 'href', href ); 577 }) 578 .on( 'route:reset', function() { 579 searchView.val( '' ).trigger( 'input' ); 580 }); 581 }, 582 583 /** 584 * Create the default states for the frame. 585 */ 586 createStates: function() { 587 var options = this.options; 588 589 if ( this.options.states ) { 590 return; 591 } 592 593 // Add the default states. 594 this.states.add([ 595 new Library({ 596 library: wp.media.query( options.library ), 597 multiple: options.multiple, 598 title: options.title, 599 content: 'browse', 600 toolbar: 'select', 601 contentUserSetting: false, 602 filterable: 'all', 603 autoSelect: false 604 }) 605 ]); 606 }, 607 608 /** 609 * Bind region mode activation events to proper handlers. 610 */ 611 bindRegionModeHandlers: function() { 612 this.on( 'content:create:browse', this.browseContent, this ); 613 614 // Handle a frame-level event for editing an attachment. 615 this.on( 'edit:attachment', this.openEditAttachmentModal, this ); 616 617 this.on( 'select:activate', this.bindKeydown, this ); 618 this.on( 'select:deactivate', this.unbindKeydown, this ); 619 }, 620 621 handleKeydown: function( e ) { 622 if ( 27 === e.which ) { 623 e.preventDefault(); 624 this.deactivateMode( 'select' ).activateMode( 'edit' ); 625 } 626 }, 627 628 bindKeydown: function() { 629 this.$body.on( 'keydown.select', _.bind( this.handleKeydown, this ) ); 630 }, 631 632 unbindKeydown: function() { 633 this.$body.off( 'keydown.select' ); 634 }, 635 636 fixPosition: function() { 637 var $browser, $toolbar; 638 if ( ! this.isModeActive( 'select' ) ) { 639 return; 640 } 641 642 $browser = this.$('.attachments-browser'); 643 $toolbar = $browser.find('.media-toolbar'); 644 645 // Offset doesn't appear to take top margin into account, hence +16. 646 if ( ( $browser.offset().top + 16 ) < this.$window.scrollTop() + this.$adminBar.height() ) { 647 $browser.addClass( 'fixed' ); 648 $toolbar.css('width', $browser.width() + 'px'); 649 } else { 650 $browser.removeClass( 'fixed' ); 651 $toolbar.css('width', ''); 652 } 653 }, 654 655 /** 656 * Click handler for the `Add New` button. 657 */ 658 addNewClickHandler: function( event ) { 659 event.preventDefault(); 660 this.trigger( 'toggle:upload:attachment' ); 661 662 if ( this.uploader ) { 663 this.uploader.refresh(); 664 } 665 }, 666 667 /** 668 * Open the Edit Attachment modal. 669 */ 670 openEditAttachmentModal: function( model ) { 671 // Create a new EditAttachment frame, passing along the library and the attachment model. 672 if ( wp.media.frames.edit ) { 673 wp.media.frames.edit.open().trigger( 'refresh', model ); 674 } else { 675 wp.media.frames.edit = wp.media( { 676 frame: 'edit-attachments', 677 controller: this, 678 library: this.state().get('library'), 679 model: model 680 } ); 681 } 682 }, 683 684 /** 685 * Create an attachments browser view within the content region. 686 * 687 * @param {Object} contentRegion Basic object with a `view` property, which 688 * should be set with the proper region view. 689 * @this wp.media.controller.Region 690 */ 691 browseContent: function( contentRegion ) { 692 var state = this.state(); 693 694 // Browse our library of attachments. 695 this.browserView = contentRegion.view = new wp.media.view.AttachmentsBrowser({ 696 controller: this, 697 collection: state.get('library'), 698 selection: state.get('selection'), 699 model: state, 700 sortable: state.get('sortable'), 701 search: state.get('searchable'), 702 filters: state.get('filterable'), 703 date: state.get('date'), 704 display: state.get('displaySettings'), 705 dragInfo: state.get('dragInfo'), 706 sidebar: 'errors', 707 708 suggestedWidth: state.get('suggestedWidth'), 709 suggestedHeight: state.get('suggestedHeight'), 710 711 AttachmentView: state.get('AttachmentView'), 712 713 scrollElement: document 714 }); 715 this.browserView.on( 'ready', _.bind( this.bindDeferred, this ) ); 716 717 this.errors = wp.Uploader.errors; 718 this.errors.on( 'add remove reset', this.sidebarVisibility, this ); 719 }, 720 721 sidebarVisibility: function() { 722 this.browserView.$( '.media-sidebar' ).toggle( !! this.errors.length ); 723 }, 724 725 bindDeferred: function() { 726 if ( ! this.browserView.dfd ) { 727 return; 728 } 729 this.browserView.dfd.done( _.bind( this.startHistory, this ) ); 730 }, 731 732 startHistory: function() { 733 // Verify pushState support and activate. 734 if ( window.history && window.history.pushState ) { 735 if ( Backbone.History.started ) { 736 Backbone.history.stop(); 737 } 738 Backbone.history.start( { 739 root: window._wpMediaGridSettings.adminUrl, 740 pushState: true 741 } ); 742 } 743 } 744 }); 745 746 module.exports = Manage; 747 748 749 /***/ }), 750 751 /***/ "nD7t": 752 /***/ (function(module, exports) { 753 754 var Button = wp.media.view.Button, 755 l10n = wp.media.view.l10n, 756 DeleteSelected; 757 758 /** 759 * wp.media.view.DeleteSelectedButton 760 * 761 * A button that handles bulk Delete/Trash logic 762 * 763 * @memberOf wp.media.view 764 * 765 * @class 766 * @augments wp.media.view.Button 767 * @augments wp.media.View 768 * @augments wp.Backbone.View 769 * @augments Backbone.View 770 */ 771 DeleteSelected = Button.extend(/** @lends wp.media.view.DeleteSelectedButton.prototype */{ 772 initialize: function() { 773 Button.prototype.initialize.apply( this, arguments ); 774 if ( this.options.filters ) { 775 this.options.filters.model.on( 'change', this.filterChange, this ); 776 } 777 this.controller.on( 'selection:toggle', this.toggleDisabled, this ); 778 this.controller.on( 'select:activate', this.toggleDisabled, this ); 779 }, 780 781 filterChange: function( model ) { 782 if ( 'trash' === model.get( 'status' ) ) { 783 this.model.set( 'text', l10n.restoreSelected ); 784 } else if ( wp.media.view.settings.mediaTrash ) { 785 this.model.set( 'text', l10n.trashSelected ); 786 } else { 787 this.model.set( 'text', l10n.deletePermanently ); 788 } 789 }, 790 791 toggleDisabled: function() { 792 this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length ); 793 }, 794 795 render: function() { 796 Button.prototype.render.apply( this, arguments ); 797 if ( this.controller.isModeActive( 'select' ) ) { 798 this.$el.addClass( 'delete-selected-button' ); 799 } else { 800 this.$el.addClass( 'delete-selected-button hidden' ); 801 } 802 this.toggleDisabled(); 803 return this; 804 } 805 }); 806 807 module.exports = DeleteSelected; 808 809 810 /***/ }), 811 812 /***/ "wQX5": 813 /***/ (function(module, exports) { 814 815 var Frame = wp.media.view.Frame, 816 MediaFrame = wp.media.view.MediaFrame, 817 818 $ = jQuery, 819 EditAttachments; 820 821 /** 822 * wp.media.view.MediaFrame.EditAttachments 823 * 824 * A frame for editing the details of a specific media item. 825 * 826 * Opens in a modal by default. 827 * 828 * Requires an attachment model to be passed in the options hash under `model`. 829 * 830 * @memberOf wp.media.view.MediaFrame 831 * 832 * @class 833 * @augments wp.media.view.Frame 834 * @augments wp.media.View 835 * @augments wp.Backbone.View 836 * @augments Backbone.View 837 * @mixes wp.media.controller.StateMachine 838 */ 839 EditAttachments = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.EditAttachments.prototype */{ 840 841 className: 'edit-attachment-frame', 842 template: wp.template( 'edit-attachment-frame' ), 843 regions: [ 'title', 'content' ], 844 845 events: { 846 'click .left': 'previousMediaItem', 847 'click .right': 'nextMediaItem' 848 }, 849 850 initialize: function() { 851 Frame.prototype.initialize.apply( this, arguments ); 852 853 _.defaults( this.options, { 854 modal: true, 855 state: 'edit-attachment' 856 }); 857 858 this.controller = this.options.controller; 859 this.gridRouter = this.controller.gridRouter; 860 this.library = this.options.library; 861 862 if ( this.options.model ) { 863 this.model = this.options.model; 864 } 865 866 this.bindHandlers(); 867 this.createStates(); 868 this.createModal(); 869 870 this.title.mode( 'default' ); 871 this.toggleNav(); 872 }, 873 874 bindHandlers: function() { 875 // Bind default title creation. 876 this.on( 'title:create:default', this.createTitle, this ); 877 878 this.on( 'content:create:edit-metadata', this.editMetadataMode, this ); 879 this.on( 'content:create:edit-image', this.editImageMode, this ); 880 this.on( 'content:render:edit-image', this.editImageModeRender, this ); 881 this.on( 'refresh', this.rerender, this ); 882 this.on( 'close', this.detach ); 883 884 this.bindModelHandlers(); 885 this.listenTo( this.gridRouter, 'route:search', this.close, this ); 886 }, 887 888 bindModelHandlers: function() { 889 // Close the modal if the attachment is deleted. 890 this.listenTo( this.model, 'change:status destroy', this.close, this ); 891 }, 892 893 createModal: function() { 894 // Initialize modal container view. 895 if ( this.options.modal ) { 896 this.modal = new wp.media.view.Modal({ 897 controller: this, 898 title: this.options.title, 899 hasCloseButton: false 900 }); 901 902 this.modal.on( 'open', _.bind( function () { 903 $( 'body' ).on( 'keydown.media-modal', _.bind( this.keyEvent, this ) ); 904 }, this ) ); 905 906 // Completely destroy the modal DOM element when closing it. 907 this.modal.on( 'close', _.bind( function() { 908 // Remove the keydown event. 909 $( 'body' ).off( 'keydown.media-modal' ); 910 // Move focus back to the original item in the grid if possible. 911 $( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).trigger( 'focus' ); 912 this.resetRoute(); 913 }, this ) ); 914 915 // Set this frame as the modal's content. 916 this.modal.content( this ); 917 this.modal.open(); 918 } 919 }, 920 921 /** 922 * Add the default states to the frame. 923 */ 924 createStates: function() { 925 this.states.add([ 926 new wp.media.controller.EditAttachmentMetadata({ 927 model: this.model, 928 library: this.library 929 }) 930 ]); 931 }, 932 933 /** 934 * Content region rendering callback for the `edit-metadata` mode. 935 * 936 * @param {Object} contentRegion Basic object with a `view` property, which 937 * should be set with the proper region view. 938 */ 939 editMetadataMode: function( contentRegion ) { 940 contentRegion.view = new wp.media.view.Attachment.Details.TwoColumn({ 941 controller: this, 942 model: this.model 943 }); 944 945 /** 946 * Attach a subview to display fields added via the 947 * `attachment_fields_to_edit` filter. 948 */ 949 contentRegion.view.views.set( '.attachment-compat', new wp.media.view.AttachmentCompat({ 950 controller: this, 951 model: this.model 952 }) ); 953 954 // Update browser url when navigating media details, except on load. 955 if ( this.model && ! this.model.get( 'skipHistory' ) ) { 956 this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) ); 957 } 958 }, 959 960 /** 961 * Render the EditImage view into the frame's content region. 962 * 963 * @param {Object} contentRegion Basic object with a `view` property, which 964 * should be set with the proper region view. 965 */ 966 editImageMode: function( contentRegion ) { 967 var editImageController = new wp.media.controller.EditImage( { 968 model: this.model, 969 frame: this 970 } ); 971 // Noop some methods. 972 editImageController._toolbar = function() {}; 973 editImageController._router = function() {}; 974 editImageController._menu = function() {}; 975 976 contentRegion.view = new wp.media.view.EditImage.Details( { 977 model: this.model, 978 frame: this, 979 controller: editImageController 980 } ); 981 982 this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) ); 983 984 }, 985 986 editImageModeRender: function( view ) { 987 view.on( 'ready', view.loadEditor ); 988 }, 989 990 toggleNav: function() { 991 this.$( '.left' ).prop( 'disabled', ! this.hasPrevious() ); 992 this.$( '.right' ).prop( 'disabled', ! this.hasNext() ); 993 }, 994 995 /** 996 * Rerender the view. 997 */ 998 rerender: function( model ) { 999 this.stopListening( this.model ); 1000 1001 this.model = model; 1002 1003 this.bindModelHandlers(); 1004 1005 // Only rerender the `content` region. 1006 if ( this.content.mode() !== 'edit-metadata' ) { 1007 this.content.mode( 'edit-metadata' ); 1008 } else { 1009 this.content.render(); 1010 } 1011 1012 this.toggleNav(); 1013 }, 1014 1015 /** 1016 * Click handler to switch to the previous media item. 1017 */ 1018 previousMediaItem: function() { 1019 if ( ! this.hasPrevious() ) { 1020 return; 1021 } 1022 1023 this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) ); 1024 // Move focus to the Previous button. When there are no more items, to the Next button. 1025 this.focusNavButton( this.hasPrevious() ? '.left' : '.right' ); 1026 }, 1027 1028 /** 1029 * Click handler to switch to the next media item. 1030 */ 1031 nextMediaItem: function() { 1032 if ( ! this.hasNext() ) { 1033 return; 1034 } 1035 1036 this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) ); 1037 // Move focus to the Next button. When there are no more items, to the Previous button. 1038 this.focusNavButton( this.hasNext() ? '.right' : '.left' ); 1039 }, 1040 1041 /** 1042 * Set focus to the navigation buttons depending on the browsing direction. 1043 * 1044 * @since 5.3.0 1045 * 1046 * @param {string} which A CSS selector to target the button to focus. 1047 */ 1048 focusNavButton: function( which ) { 1049 $( which ).trigger( 'focus' ); 1050 }, 1051 1052 getCurrentIndex: function() { 1053 return this.library.indexOf( this.model ); 1054 }, 1055 1056 hasNext: function() { 1057 return ( this.getCurrentIndex() + 1 ) < this.library.length; 1058 }, 1059 1060 hasPrevious: function() { 1061 return ( this.getCurrentIndex() - 1 ) > -1; 1062 }, 1063 /** 1064 * Respond to the keyboard events: right arrow, left arrow, except when 1065 * focus is in a textarea or input field. 1066 */ 1067 keyEvent: function( event ) { 1068 if ( ( 'INPUT' === event.target.nodeName || 'TEXTAREA' === event.target.nodeName ) && ! ( event.target.readOnly || event.target.disabled ) ) { 1069 return; 1070 } 1071 1072 // The right arrow key. 1073 if ( 39 === event.keyCode ) { 1074 this.nextMediaItem(); 1075 } 1076 // The left arrow key. 1077 if ( 37 === event.keyCode ) { 1078 this.previousMediaItem(); 1079 } 1080 }, 1081 1082 resetRoute: function() { 1083 var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(), 1084 url = '' !== searchTerm ? '?search=' + searchTerm : ''; 1085 this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } ); 1086 } 1087 }); 1088 1089 module.exports = EditAttachments; 1090 1091 1092 /***/ }) 1093 1094 /******/ });