Ticket #24716: 24716.14.diff
File 24716.14.diff, 39.2 KB (added by , 11 years ago) |
---|
-
src/wp-admin/upload.php
22 22 23 23 if ( 'grid' === $mode ) { 24 24 wp_enqueue_media(); 25 wp_enqueue_script( 'media-grid' ); 25 26 wp_enqueue_script( 'media' ); 26 27 require_once( ABSPATH . 'wp-admin/admin-header.php' ); 27 ?><div class="view-switch media-grid-view-switch">28 <a href="<?php echo esc_url( add_query_arg( 'mode', 'list', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-list">29 <img id="view-switch-list" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="List View" alt="List View"/>30 </a>31 <a href="<?php echo esc_url( add_query_arg( 'mode', 'grid', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-grid current">32 <img id="view-switch-excerpt" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="Grid View" alt="Grid View"/>33 </a>34 </div><?php35 28 include( ABSPATH . 'wp-admin/admin-footer.php' ); 36 29 exit; 37 30 } -
src/wp-includes/css/media-views.css
750 750 max-height: 100%; 751 751 } 752 752 753 .attachment-preview.type-audio .thumbnail, 754 .attachment-preview.type-video .thumbnail { 755 z-index: 1; 756 margin: 5%; 757 max-width: 90%; 758 max-height: 90%; 759 } 760 761 .media-frame-content .attachment-preview.type-audio .icon, 762 .media-frame-content .attachment-preview.type-video .icon { 763 z-index: 2; 764 background: #f1f1f1; 765 position: relative; 766 padding: 0; 767 top: 15%; 768 left: auto; 769 right: auto; 770 } 771 772 .attachment-preview.type-audio .filename, 773 .attachment-preview.type-video .filename { 774 z-index: 3; 775 } 776 753 777 .attachment-preview .thumbnail:after { 754 778 content: ''; 755 779 display: block; … … 909 933 border-radius: 0; 910 934 } 911 935 936 .attachment .data-fields { 937 margin: 5px 0 0; 938 } 939 940 .attachment .data-field { 941 white-space: nowrap; 942 text-overflow: ellipsis; 943 overflow: hidden; 944 display: block; 945 line-height: 19px; 946 height: 19px; 947 text-align: left; 948 width: 90%; 949 margin: 0 5%; 950 } 951 912 952 /** 913 953 * Attachments Browser 914 954 */ … … 924 964 height: 50px; 925 965 } 926 966 967 .attachments-browser.hide-sidebar .media-toolbar { 968 right: 0; 969 } 970 927 971 .attachments-browser .media-toolbar-primary > .media-button, 928 972 .attachments-browser .media-toolbar-primary > .media-button-group, 929 973 .attachments-browser .media-toolbar-secondary > .media-button, … … 942 986 outline: none; 943 987 } 944 988 989 /** 990 * Copied styles from the theme browser view. 991 * 992 * This should be OOCSS'd so both use a shared selector. 993 */ 994 .attachment .edit-media { 995 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 996 opacity: 0; 997 position: absolute; 998 z-index: 4; 999 top: 25%; 1000 right: 25%; 1001 left: 25%; 1002 background: #222; 1003 background: rgba(0,0,0,0.7); 1004 color: #fff; 1005 font-size: 15px; 1006 text-shadow: 0 1px 0 rgba(0,0,0,0.6); 1007 -webkit-font-smoothing: antialiased; 1008 font-weight: 600; 1009 padding: 10px 0; 1010 text-align: center; 1011 -webkit-border-radius: 3px; 1012 border-radius: 3px; 1013 -webkit-transition: opacity 0.1s ease-in-out; 1014 transition: opacity 0.1s ease-in-out; 1015 } 1016 1017 .attachment:hover .edit-media { 1018 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; 1019 opacity: 1; 1020 } 1021 1022 .attachments-browser.hide-sidebar .attachments { 1023 right: 0; 1024 } 1025 945 1026 .attachments-browser .instructions { 946 1027 display: inline-block; 947 1028 margin-top: 16px; … … 2388 2469 line-height: 29px; 2389 2470 } 2390 2471 2391 .media-grid-view -switch {2392 position: fixed;2393 right: 10px;2394 top: 44px;2395 z-index: 300;2472 .media-grid-view .view-switch { 2473 display: inline-block; 2474 float: none; 2475 margin-top: 13px; 2476 vertical-align: middle; 2396 2477 } 2397 2478 2398 2479 /** … … 2427 2508 display: none; 2428 2509 } 2429 2510 2511 /** 2512 * Copied styles from the Add theme toolbar. 2513 * 2514 * This should be OOCSS'd so both use a shared selector. 2515 */ 2516 .media-grid-view .media-toolbar { 2517 background: #fff; 2518 -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); 2519 box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); 2520 -webkit-box-sizing: border-box; 2521 -moz-box-sizing: border-box; 2522 box-sizing: border-box; 2523 color: #555; 2524 display: inline-block; 2525 font-size: 13px; 2526 padding: 0 20px; 2527 position: relative; 2528 width: 100%; 2529 } 2530 2531 /** 2532 * The left and right buttons are copied from the expanded theme details modal. 2533 * 2534 * This should be OOCSS'd so both use a shared selector. 2535 */ 2536 .edit-attachment-frame .edit-media-header .left, 2537 .edit-attachment-frame .edit-media-header .right { 2538 cursor: pointer; 2539 color: #777; 2540 background-color: transparent; 2541 height: 48px; 2542 width: 54px; 2543 float: left; 2544 text-align: center; 2545 border: 0; 2546 border-right: 1px solid #ddd; 2547 } 2548 2549 .edit-attachment-frame .edit-media-header .right:before, 2550 .edit-attachment-frame .edit-media-header .left:before { 2551 font: normal 20px/50px 'dashicons' !important; 2552 display: inline; 2553 font-weight: 300; 2554 } 2555 2556 2557 .edit-attachment-frame .edit-media-header .left:before { 2558 content: '\f340'; 2559 } 2560 2561 .edit-attachment-frame .edit-media-header .right:before { 2562 content: '\f344'; 2563 } 2564 2565 .edit-attachment-frame .edit-media-header .left.disabled, 2566 .edit-attachment-frame .edit-media-header .right.disabled, 2567 .edit-attachment-frame .edit-media-header .left.disabled:hover, 2568 .edit-attachment-frame .edit-media-header .right.disabled:hover { 2569 color: #ccc; 2570 background: inherit; 2571 cursor: inherit; 2572 } 2573 2574 .edit-attachment-frame .edit-media-header .close:hover, 2575 .edit-attachment-frame .edit-media-header .right:hover, 2576 .edit-attachment-frame .edit-media-header .left:hover, 2577 .edit-attachment-frame .edit-media-header .close:focus, 2578 .edit-attachment-frame .edit-media-header .right:focus, 2579 .edit-attachment-frame .edit-media-header .left:focus { 2580 background: #0074a2; 2581 color: #fff; 2582 } 2583 2584 .edit-attachment-frame .media-frame-content, 2585 .edit-attachment-frame .media-frame-router { 2586 left: 0; 2587 } 2588 2589 /* Hiding this for the moment instead of removing it from the template. */ 2590 .edit-attachment-frame h3 { 2591 display: none; 2592 } 2593 2594 .edit-attachment-frame .attachment-details { 2595 position: absolute; 2596 overflow: auto; 2597 top: 0; 2598 bottom: 0; 2599 right: 0; 2600 left: 0; 2601 } 2602 2603 .edit-attachment-frame .attachment-info { 2604 border-bottom: 0; 2605 border-right: 1px solid #ddd; 2606 bottom: 0; 2607 position: absolute; 2608 top: 0; 2609 left: 0; 2610 margin-bottom: 0; 2611 padding: 2% 4%; 2612 right: 50%; 2613 } 2614 2615 .edit-attachment-frame .attachment-info .thumbnail { 2616 max-width: none; 2617 max-height: none; 2618 } 2619 2620 .edit-attachment-frame .attachment-info .thumbnail-image img { 2621 margin: 0; 2622 } 2623 2624 .edit-attachment-frame .attachment-info .thumbnail-image:after { 2625 -webkit-box-shadow: none; 2626 box-shadow: none; 2627 } 2628 2629 .edit-attachment-frame .attachment-info .thumbnail img { 2630 max-width: none; 2631 max-height: 50%; 2632 } 2633 2634 .edit-attachment-frame .attachment-info .details { 2635 float: none; 2636 } 2637 2638 .edit-attachment-frame .attachment-fields { 2639 bottom: 0; 2640 padding: 2% 4%; 2641 position: absolute; 2642 top: 0; 2643 left: 50%; 2644 right: 0; 2645 } 2646 2647 .edit-attachment-frame .attachment-fields .setting { 2648 display: block; 2649 float: left; 2650 width: 100%; 2651 margin: 1px 0; 2652 } 2653 2654 .edit-attachment-frame .attachment-fields .setting label { 2655 display: block; 2656 } 2657 2658 .edit-attachment-frame .attachment-fields .setting .link-to-custom { 2659 margin: 3px 0; 2660 } 2661 2662 .edit-attachment-frame .attachment-fields .setting .name { 2663 min-width: 30%; 2664 margin-right: 4%; 2665 font-size: 12px; 2666 text-align: right; 2667 } 2668 2669 .edit-attachment-frame .attachment-fields .setting select { 2670 max-width: 65%; 2671 } 2672 2673 .edit-attachment-frame .attachment-fields .setting input[type="checkbox"], 2674 .edit-attachment-frame .attachment-fields .field input[type="checkbox"] { 2675 width: 16px; 2676 float: none; 2677 margin: 8px 3px 0; 2678 padding: 0; 2679 } 2680 2681 .edit-attachment-frame .attachment-fields .setting span { 2682 float: left; 2683 min-height: 22px; 2684 padding-top: 8px; 2685 line-height: 16px; 2686 font-weight: normal; 2687 color: #666; 2688 } 2689 2690 .edit-attachment-frame .attachment-fields .setting input[type="text"], 2691 .edit-attachment-frame .attachment-fields .setting input[type="password"], 2692 .edit-attachment-frame .attachment-fields .setting input[type="number"], 2693 .edit-attachment-frame .attachment-fields .setting input[type="search"], 2694 .edit-attachment-frame .attachment-fields .setting input[type="email"], 2695 .edit-attachment-frame .attachment-fields .setting input[type="url"], 2696 .edit-attachment-frame .attachment-fields .setting textarea, 2697 .edit-attachment-frame .attachment-fields .setting .value { 2698 margin: 1px; 2699 width: 65%; 2700 float: right; 2701 padding: 6px 8px; 2702 -webkit-box-sizing: border-box; 2703 -moz-box-sizing: border-box; 2704 box-sizing: border-box; 2705 } 2706 2707 .edit-attachment-frame .attachment-fields .setting textarea { 2708 height: 62px; 2709 resize: vertical; 2710 } 2711 2712 .edit-attachment-frame .attachment-fields select { 2713 margin-top: 3px; 2714 } 2715 2716 .media-grid-view.hide-router .media-frame-title { 2717 box-shadow: none; 2718 } 2719 2430 2720 .media-grid-view .media-frame-content { 2721 background-color: transparent; 2431 2722 bottom: 40px; 2432 2723 } 2433 2724 @media screen and (max-width: 782px) { -
src/wp-includes/js/media-grid.js
1 (function($, _, Backbone, wp) { 2 var media = wp.media, l10n; 3 4 // Link any localized strings. 5 if ( media.view.l10n ) { 6 l10n = media.view.l10n; 7 } else { 8 l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n; 9 delete l10n.settings; 10 } 11 12 /** 13 * A more abstracted state, because media.controller.State expects 14 * specific regions (menu, title, etc.) to exist on the frame, which do not 15 * exist in media.view.Frame.EditAttachment. 16 */ 17 media.controller._State = Backbone.Model.extend({ 18 constructor: function() { 19 this.on( 'activate', this._preActivate, this ); 20 this.on( 'activate', this.activate, this ); 21 this.on( 'activate', this._postActivate, this ); 22 this.on( 'deactivate', this._deactivate, this ); 23 this.on( 'deactivate', this.deactivate, this ); 24 this.on( 'reset', this.reset, this ); 25 this.on( 'ready', this.ready, this ); 26 /** 27 * Call parent constructor with passed arguments 28 */ 29 Backbone.Model.apply( this, arguments ); 30 }, 31 32 /** 33 * @abstract 34 */ 35 ready: function() {}, 36 /** 37 * @abstract 38 */ 39 activate: function() {}, 40 /** 41 * @abstract 42 */ 43 deactivate: function() {}, 44 /** 45 * @abstract 46 */ 47 reset: function() {}, 48 /** 49 * @access private 50 */ 51 _preActivate: function() { 52 this.active = true; 53 }, 54 /** 55 * @access private 56 */ 57 _postActivate: function() {}, 58 /** 59 * @access private 60 */ 61 _deactivate: function() { 62 this.active = false; 63 } 64 }); 65 66 /** 67 * A state for editing (cropping, etc.) an image. 68 * 69 * @constructor 70 * @augments wp.media.controller.State 71 * @augments Backbone.Model 72 */ 73 media.controller.EditImageNoFrame = media.controller._State.extend({ 74 defaults: { 75 id: 'edit-attachment', 76 title: l10n.editImage, 77 // Region mode defaults. 78 menu: false, 79 router: 'edit-metadata', 80 content: 'edit-metadata', 81 toolbar: 'toolbar', 82 83 url: '' 84 }, 85 86 initialize: function() { 87 media.controller._State.prototype.initialize.apply( this, arguments ); 88 }, 89 90 activate: function() { 91 this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar ); 92 }, 93 94 _postActivate: function() { 95 this._content(); 96 this._router(); 97 }, 98 99 deactivate: function() { 100 this.stopListening( this.frame ); 101 }, 102 103 toolbar: function() { 104 var frame = this.frame, 105 lastState = frame.lastState(), 106 previous = lastState && lastState.id; 107 108 frame.toolbar.set( new media.view.Toolbar({ 109 controller: frame, 110 items: { 111 back: { 112 style: 'primary', 113 text: l10n.back, 114 priority: 20, 115 click: function() { 116 if ( previous ) { 117 frame.setState( previous ); 118 } else { 119 frame.close(); 120 } 121 } 122 } 123 } 124 }) ); 125 }, 126 127 /** 128 * @access private 129 */ 130 _router: function() { 131 var router = this.frame.router, 132 mode = this.get('router'), 133 view; 134 135 this.frame.$el.toggleClass( 'hide-router', ! mode ); 136 if ( ! mode ) { 137 return; 138 } 139 140 this.frame.router.render( mode ); 141 142 view = router.get(); 143 if ( view && view.select ) { 144 view.select( this.frame.content.mode() ); 145 } 146 }, 147 148 _content: function() { 149 var mode = this.get( 'content' ); 150 if ( mode ) { 151 this.frame[ 'content' ].render( mode ); 152 } 153 } 154 }); 155 156 /** 157 * wp.media.view.MediaFrame.Manage 158 * 159 * A generic management frame workflow. 160 * 161 * Used in the media grid view. 162 * 163 * @constructor 164 * @augments wp.media.view.MediaFrame 165 * @augments wp.media.view.Frame 166 * @augments wp.media.View 167 * @augments wp.Backbone.View 168 * @augments Backbone.View 169 * @mixes wp.media.controller.StateMachine 170 */ 171 media.view.MediaFrame.Manage = media.view.MediaFrame.extend({ 172 /** 173 * @global wp.Uploader 174 */ 175 initialize: function() { 176 _.defaults( this.options, { 177 title: l10n.mediaLibraryTitle, 178 modal: false, 179 selection: [], 180 library: {}, 181 multiple: false, 182 state: 'library', 183 uploader: true, 184 mode: [ 'grid', 'edit' ] 185 }); 186 187 // Ensure core and media grid view UI is enabled. 188 this.$el.addClass('wp-core-ui media-grid-view'); 189 190 // Force the uploader off if the upload limit has been exceeded or 191 // if the browser isn't supported. 192 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) { 193 this.options.uploader = false; 194 } 195 196 // Initialize a window-wide uploader. 197 if ( this.options.uploader ) { 198 this.uploader = new media.view.UploaderWindow({ 199 controller: this, 200 uploader: { 201 dropzone: $('body'), 202 container: $('body') 203 } 204 }).render(); 205 this.uploader.ready(); 206 $('body').append( this.uploader.el ); 207 208 this.options.uploader = false; 209 } 210 211 /** 212 * call 'initialize' directly on the parent class 213 */ 214 media.view.MediaFrame.prototype.initialize.apply( this, arguments ); 215 216 // Since we're not using the default modal built into 217 // a media frame, append our $element to the supplied container. 218 this.$el.appendTo( this.options.container ); 219 220 this.createSelection(); 221 this.createStates(); 222 this.bindHandlers(); 223 this.render(); 224 }, 225 226 createSelection: function() { 227 var selection = this.options.selection; 228 229 if ( ! (selection instanceof media.model.Selection) ) { 230 this.options.selection = new media.model.Selection( selection, { 231 multiple: this.options.multiple 232 }); 233 } 234 235 this._selection = { 236 attachments: new media.model.Attachments(), 237 difference: [] 238 }; 239 }, 240 241 createStates: function() { 242 var options = this.options; 243 244 if ( this.options.states ) { 245 return; 246 } 247 248 // Add the default states. 249 this.states.add([ 250 new media.controller.Library({ 251 library: media.query( options.library ), 252 multiple: options.multiple, 253 title: options.title, 254 priority: 20, 255 toolbar: false, 256 router: false, 257 content: 'browse', 258 filterable: 'mime-types' 259 }) 260 ]); 261 }, 262 263 bindHandlers: function() { 264 this.on( 'content:create:browse', this.browseContent, this ); 265 this.on( 'content:render:edit-image', this.editImageContent, this ); 266 267 // Handle a frame-level event for editing an attachment. 268 this.on( 'edit:attachment', this.editAttachment, this ); 269 this.on( 'edit:attachment:next', this.editNextAttachment, this ); 270 this.on( 'edit:attachment:previous', this.editPreviousAttachment, this ); 271 }, 272 273 editPreviousAttachment: function( currentModel ) { 274 var library = this.state().get('library'), 275 currentModelIndex = library.indexOf( currentModel ); 276 this.trigger( 'edit:attachment', library.at( currentModelIndex - 1 ) ); 277 }, 278 279 editNextAttachment: function( currentModel ) { 280 var library = this.state().get('library'), 281 currentModelIndex = library.indexOf( currentModel ); 282 this.trigger( 'edit:attachment', library.at( currentModelIndex + 1 ) ); 283 }, 284 285 /** 286 * Open the Edit Attachment modal. 287 */ 288 editAttachment: function( model ) { 289 var library = this.state().get('library'), hasPrevious, hasNext; 290 if ( library.indexOf( model ) > 0 ) { 291 hasPrevious = true; 292 } 293 else { 294 hasPrevious = false; 295 } 296 if ( library.indexOf( model ) < library.length - 1 ) { 297 hasNext = true; 298 } 299 else { 300 hasNext = false; 301 } 302 303 new media.view.Frame.EditAttachment({ 304 hasPrevious: hasPrevious, 305 hasNext: hasNext, 306 model: model, 307 gridController: this 308 }); 309 }, 310 311 /** 312 * Content 313 * 314 * @param {Object} content 315 * @this wp.media.controller.Region 316 */ 317 browseContent: function( content ) { 318 var state = this.state(); 319 320 // Browse our library of attachments. 321 content.view = new media.view.AttachmentsBrowser({ 322 controller: this, 323 collection: state.get('library'), 324 selection: state.get('selection'), 325 model: state, 326 sortable: state.get('sortable'), 327 search: state.get('searchable'), 328 filters: state.get('filterable'), 329 display: state.get('displaySettings'), 330 dragInfo: state.get('dragInfo'), 331 bulkEdit: true, 332 sidebar: false, 333 334 suggestedWidth: state.get('suggestedWidth'), 335 suggestedHeight: state.get('suggestedHeight'), 336 337 AttachmentView: state.get('AttachmentView') 338 }); 339 }, 340 341 editImageContent: function() { 342 var image = this.state().get('image'), 343 view = new media.view.EditImage( { model: image, controller: this } ).render(); 344 345 this.content.set( view ); 346 347 // after creating the wrapper view, load the actual editor via an ajax call 348 view.loadEditor(); 349 350 } 351 }); 352 353 media.view.Attachment.Details.TwoColumn = media.view.Attachment.Details.extend({ 354 template: wp.template( 'attachment-details-two-column' ), 355 356 initialize: function() { 357 this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false); 358 this.model.on( 'change:sizes change:uploading', this.render, this ); 359 this.model.on( 'change:title', this._syncTitle, this ); 360 this.model.on( 'change:caption', this._syncCaption, this ); 361 this.model.on( 'change:percent', this.progress, this ); 362 363 // Update the selection. 364 this.model.on( 'add', this.select, this ); 365 this.model.on( 'remove', this.deselect, this ); 366 } 367 }); 368 369 /** 370 * A frame for editing the details of a specific media item. 371 * 372 * Opens in a modal by default. 373 * 374 * Requires an attachment model to be passed in the options hash under `model`. 375 */ 376 media.view.Frame.EditAttachment = media.view.Frame.extend({ 377 378 className: 'edit-attachment-frame', 379 template: media.template( 'edit-attachment-frame' ), 380 regions: [ 'router', 'content' ], 381 382 events: { 383 'click': 'collapse', 384 'click .delete-media-item': 'deleteMediaItem', 385 'click .left': 'previousMediaItem', 386 'click .right': 'nextMediaItem' 387 }, 388 389 initialize: function( options ) { 390 var self = this; 391 media.view.Frame.prototype.initialize.apply( this, arguments ); 392 393 _.defaults( this.options, { 394 modal: true, 395 state: 'edit-attachment' 396 }); 397 398 this.createStates(); 399 400 this.on( 'content:render:edit-metadata', this.editMetadataContent, this ); 401 this.on( 'content:render:edit-image', this.editImageContentUgh, this ); 402 403 // Only need a tab to Edit Image for images. 404 if ( this.model.get( 'type' ) === 'image' ) { 405 this.on( 'router:create', this.createRouter, this ); 406 this.on( 'router:render', this.browseRouter, this ); 407 } 408 409 // Initialize modal container view. 410 if ( this.options.modal ) { 411 this.modal = new media.view.Modal({ 412 controller: this, 413 title: this.options.title 414 }); 415 416 // Completely destroy the modal DOM element when closing it. 417 this.modal.close = function() { 418 self.modal.remove(); 419 }; 420 421 this.modal.content( this ); 422 this.modal.open(); 423 } 424 }, 425 426 /** 427 * Add the default states to the frame. 428 */ 429 createStates: function() { 430 this.states.add([ 431 new media.controller.EditImageNoFrame( { model: this.model } ) 432 ]); 433 }, 434 435 /** 436 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 437 */ 438 render: function() { 439 // Activate the default state if no active state exists. 440 if ( ! this.state() && this.options.state ) { 441 this.setState( this.options.state ); 442 } 443 /** 444 * call 'render' directly on the parent class 445 */ 446 return media.view.Frame.prototype.render.apply( this, arguments ); 447 }, 448 449 /** 450 * Content region rendering callback for the `edit-metadata` mode. 451 */ 452 editMetadataContent: function() { 453 var view = new media.view.Attachment.Details.TwoColumn({ 454 controller: this, 455 model: this.model 456 }); 457 this.content.set( view ); 458 }, 459 460 /** 461 * For some reason the view doesn't exist in the DOM yet, don't have the 462 * patience to track this down right now. 463 */ 464 editImageContentUgh: function() { 465 _.defer( _.bind( this.editImageContent, this ) ); 466 }, 467 468 /** 469 * Render the EditImage view into the frame's content region. 470 */ 471 editImageContent: function() { 472 var view = new media.view.EditImage( { model: this.model, controller: this } ).render(); 473 474 this.content.set( view ); 475 476 // after creating the wrapper view, load the actual editor via an ajax call 477 view.loadEditor(); 478 }, 479 480 /** 481 * Create the router view. 482 * 483 * @param {Object} router 484 * @this wp.media.controller.Region 485 */ 486 createRouter: function( router ) { 487 router.view = new media.view.Router({ 488 controller: this 489 }); 490 }, 491 492 /** 493 * Router rendering callback. 494 * 495 * @param media.view.Router view Instantiated in this.createRouter() 496 */ 497 browseRouter: function( view ) { 498 view.set({ 499 'edit-metadata': { 500 text: 'Edit Metadata', 501 priority: 20 502 }, 503 'edit-image': { 504 text: 'Edit Image', 505 priority: 40 506 } 507 }); 508 }, 509 510 /** 511 * Click handler to switch to the previous media item. 512 */ 513 previousMediaItem: function() { 514 if ( ! this.options.hasPrevious ) 515 return; 516 this.modal.close(); 517 this.options.gridController.trigger( 'edit:attachment:previous', this.model ); 518 }, 519 520 /** 521 * Click handler to switch to the next media item. 522 */ 523 nextMediaItem: function() { 524 if ( ! this.options.hasNext ) 525 return; 526 this.modal.close(); 527 this.options.gridController.trigger( 'edit:attachment:next', this.model ); 528 } 529 530 }); 531 532 }(jQuery, _, Backbone, wp)); 533 No newline at end of file -
src/wp-includes/js/media-views.js
1758 1758 _.defaults( this.options, { 1759 1759 title: '', 1760 1760 modal: true, 1761 uploader: true 1761 uploader: true, 1762 mode: ['select'] 1762 1763 }); 1763 1764 1764 1765 // Ensure core UI is enabled. … … 1955 1956 }); 1956 1957 1957 1958 /** 1958 * wp.media.view.MediaFrame.Manage1959 *1960 * A generic management frame workflow.1961 *1962 * Used in the media grid view.1963 *1964 * @constructor1965 * @augments wp.media.view.MediaFrame1966 * @augments wp.media.view.Frame1967 * @augments wp.media.View1968 * @augments wp.Backbone.View1969 * @augments Backbone.View1970 * @mixes wp.media.controller.StateMachine1971 */1972 media.view.MediaFrame.Manage = media.view.MediaFrame.extend({1973 /**1974 * @global wp.Uploader1975 */1976 initialize: function() {1977 _.defaults( this.options, {1978 title: l10n.mediaLibraryTitle,1979 modal: false,1980 selection: [],1981 library: {},1982 multiple: false,1983 state: 'library',1984 uploader: true1985 });1986 1987 // Ensure core and media grid view UI is enabled.1988 this.$el.addClass('wp-core-ui media-grid-view');1989 1990 // Force the uploader off if the upload limit has been exceeded or1991 // if the browser isn't supported.1992 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {1993 this.options.uploader = false;1994 }1995 1996 // Initialize a window-wide uploader.1997 if ( this.options.uploader ) {1998 this.uploader = new media.view.UploaderWindow({1999 controller: this,2000 uploader: {2001 dropzone: $('body'),2002 container: $('body')2003 }2004 }).render();2005 this.uploader.ready();2006 $('body').append( this.uploader.el );2007 2008 this.options.uploader = false;2009 }2010 2011 /**2012 * call 'initialize' directly on the parent class2013 */2014 media.view.MediaFrame.prototype.initialize.apply( this, arguments );2015 2016 // Since we're not using the default modal built into2017 // a media frame, append our $element to the supplied container.2018 this.$el.appendTo( this.options.container );2019 2020 this.createSelection();2021 this.createStates();2022 this.bindHandlers();2023 this.render();2024 },2025 2026 createSelection: function() {2027 var selection = this.options.selection;2028 2029 if ( ! (selection instanceof media.model.Selection) ) {2030 this.options.selection = new media.model.Selection( selection, {2031 multiple: this.options.multiple2032 });2033 }2034 2035 this._selection = {2036 attachments: new media.model.Attachments(),2037 difference: []2038 };2039 },2040 2041 createStates: function() {2042 var options = this.options;2043 2044 if ( this.options.states ) {2045 return;2046 }2047 2048 // Add the default states.2049 this.states.add([2050 new media.controller.Library({2051 library: media.query( options.library ),2052 multiple: options.multiple,2053 title: options.title,2054 priority: 20,2055 toolbar: false,2056 router: false,2057 content: 'browse',2058 filterable: 'mime-types'2059 }),2060 2061 new media.controller.EditImage( { model: options.editImage } )2062 ]);2063 },2064 2065 bindHandlers: function() {2066 this.on( 'content:create:browse', this.browseContent, this );2067 this.on( 'content:render:edit-image', this.editImageContent, this );2068 },2069 2070 /**2071 * Content2072 *2073 * @param {Object} content2074 * @this wp.media.controller.Region2075 */2076 browseContent: function( content ) {2077 var state = this.state();2078 2079 // Browse our library of attachments.2080 content.view = new media.view.AttachmentsBrowser({2081 controller: this,2082 collection: state.get('library'),2083 selection: state.get('selection'),2084 model: state,2085 sortable: state.get('sortable'),2086 search: state.get('searchable'),2087 filters: state.get('filterable'),2088 display: state.get('displaySettings'),2089 dragInfo: state.get('dragInfo'),2090 bulkEdit: true,2091 2092 suggestedWidth: state.get('suggestedWidth'),2093 suggestedHeight: state.get('suggestedHeight'),2094 2095 AttachmentView: state.get('AttachmentView')2096 });2097 },2098 2099 editImageContent: function() {2100 var image = this.state().get('image'),2101 view = new media.view.EditImage( { model: image, controller: this } ).render();2102 2103 this.content.set( view );2104 2105 // after creating the wrapper view, load the actual editor via an ajax call2106 view.loadEditor();2107 2108 }2109 });2110 2111 /**2112 1959 * wp.media.view.MediaFrame.Select 2113 1960 * 2114 1961 * Type of media frame that is used to select an item or items from the media library … … 4665 4512 var selection = this.options.selection; 4666 4513 4667 4514 this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false); 4668 this.model.on( 'change :sizes change:uploading', this.render, this );4515 this.model.on( 'change', this.render, this ); 4669 4516 this.model.on( 'change:title', this._syncTitle, this ); 4670 4517 this.model.on( 'change:caption', this._syncCaption, this ); 4671 4518 this.model.on( 'change:percent', this.progress, this ); … … 4718 4565 compat: false, 4719 4566 alt: '', 4720 4567 description: '' 4721 } );4568 }, this.options ); 4722 4569 4723 4570 options.buttons = this.buttons; 4724 4571 options.describe = this.controller.state().get('describe'); … … 4768 4615 */ 4769 4616 toggleSelectionHandler: function( event ) { 4770 4617 var method; 4771 4772 4618 // Catch enter and space events 4773 4619 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) { 4774 4620 return; 4775 4621 } 4622 4623 // In the grid view, bubble up an edit:attachment event to the controller. 4624 if ( _.contains( this.controller.options.mode, 'grid' ) ) { 4625 this.controller.trigger( 'edit:attachment', this.model ); 4626 return; 4627 } 4628 4776 4629 if ( event.shiftKey ) { 4777 4630 method = 'between'; 4778 4631 } else if ( event.ctrlKey || event.metaKey ) { … … 5303 5156 */ 5304 5157 createAttachmentView: function( attachment ) { 5305 5158 var view = new this.options.AttachmentView({ 5306 controller: this.controller, 5307 model: attachment, 5308 collection: this.collection, 5309 selection: this.options.selection 5159 controller: this.controller, 5160 model: attachment, 5161 collection: this.collection, 5162 selection: this.options.selection, 5163 showAttachmentFields: this.options.showAttachmentFields 5310 5164 }); 5311 5165 5312 5166 return this._viewsByCid[ attachment.cid ] = view; … … 5603 5457 } 5604 5458 }); 5605 5459 5606 5607 5460 /** 5608 5461 * wp.media.view.AttachmentsBrowser 5609 5462 * … … 5621 5474 filters: false, 5622 5475 search: true, 5623 5476 display: false, 5624 5477 sidebar: true, 5478 showAttachmentFields: getUserSetting( 'showAttachmentFields', [ 'title', 'uploadedTo', 'dateFormatted', 'mime' ] ), 5625 5479 AttachmentView: media.view.Attachment.Library 5626 5480 }); 5627 5481 5628 5482 this.createToolbar(); 5629 5483 this.updateContent(); 5630 this.createSidebar(); 5484 if ( this.options.sidebar ) { 5485 this.createSidebar(); 5486 } else { 5487 this.$el.addClass( 'hide-sidebar' ); 5488 } 5631 5489 5632 5490 this.collection.on( 'add remove reset', this.updateContent, this ); 5633 5491 }, … … 5652 5510 5653 5511 this.views.add( this.toolbar ); 5654 5512 5513 // Feels odd to bring the global media library switcher into the Attachment 5514 // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar ); 5515 // which the controller can tap into and add this view? 5516 if ( _.contains( this.controller.options.mode, 'grid' ) ) { 5517 var libraryViewSwitcherConstructor = media.View.extend({ 5518 className: 'view-switch media-grid-view-switch', 5519 template: media.template( 'media-library-view-switcher') 5520 }); 5521 this.toolbar.set( 'libraryViewSwitcher', new libraryViewSwitcherConstructor({ 5522 controller: this.controller, 5523 priority: -90 5524 }).render() ); 5525 } 5526 5655 5527 filters = this.options.filters; 5656 5528 if ( 'uploaded' === filters ) { 5657 5529 FiltersConstructor = media.view.AttachmentFilters.Uploaded; … … 5746 5618 this.removeContent(); 5747 5619 5748 5620 this.attachments = new media.view.Attachments({ 5749 controller: this.controller, 5750 collection: this.collection, 5751 selection: this.options.selection, 5752 model: this.model, 5753 sortable: this.options.sortable, 5621 controller: this.controller, 5622 collection: this.collection, 5623 selection: this.options.selection, 5624 model: this.model, 5625 sortable: this.options.sortable, 5626 showAttachmentFields: this.options.showAttachmentFields, 5754 5627 5755 5628 // The single `Attachment` view to be used in the `Attachments` view. 5756 5629 AttachmentView: this.options.AttachmentView -
src/wp-includes/media-template.php
220 220 </div> 221 221 </script> 222 222 223 <script type="text/html" id="tmpl-media-library-view-switcher"> 224 <a href="<?php echo esc_url( add_query_arg( 'mode', 'list', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-list"> 225 <img id="view-switch-list" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="List View" alt="List View"/> 226 </a> 227 <a href="<?php echo esc_url( add_query_arg( 'mode', 'grid', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-grid current"> 228 <img id="view-switch-excerpt" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="Grid View" alt="Grid View"/> 229 </a> 230 </script> 231 223 232 <script type="text/html" id="tmpl-uploader-status"> 224 233 <h3><?php _e( 'Uploading' ); ?></h3> 225 234 <a class="upload-dismiss-errors" href="#"><?php _e('Dismiss Errors'); ?></a> … … 241 250 <span class="upload-error-message">{{ data.message }}</span> 242 251 </script> 243 252 253 <script type="text/html" id="tmpl-edit-attachment-frame"> 254 <div class="edit-media-header"> 255 <button class="left dashicons dashicons-no<# if ( ! data.hasPrevious ) { #> disabled <# } #>"><span class="screen-reader-text"><?php _e( 'Edit previous media item' ); ?></span></button> 256 <button class="right dashicons dashicons-no<# if ( ! data.hasNext ) { #> disabled <# } #>"><span class="screen-reader-text"><?php _e( 'Edit next media item' ); ?></span></button> 257 </div> 258 <div class="media-frame-router"></div> 259 <div class="media-frame-content"></div> 260 <div class="media-frame-toolbar"></div> 261 </script> 262 263 <script type="text/html" id="tmpl-attachment-details-two-column"> 264 <h3> 265 <?php _e('Attachment Details'); ?> 266 267 <span class="settings-save-status"> 268 <span class="spinner"></span> 269 <span class="saved"><?php esc_html_e('Saved.'); ?></span> 270 </span> 271 </h3> 272 <div class="attachment-info"> 273 <div class="thumbnail thumbnail-{{ data.type }}"> 274 <# if ( data.uploading ) { #> 275 <div class="media-progress-bar"><div></div></div> 276 <# } else if ( 'image' === data.type ) { #> 277 <img src="{{ data.sizes.full.url }}" draggable="false" /> 278 <# } else { #> 279 <img src="{{ data.icon }}" class="icon" draggable="false" /> 280 <# } #> 281 </div> 282 <div class="details"> 283 <div class="filename">{{ data.filename }}</div> 284 <div class="uploaded">{{ data.dateFormatted }}</div> 285 286 <div class="file-size">{{ data.filesizeHumanReadable }}</div> 287 <# if ( 'image' === data.type && ! data.uploading ) { #> 288 <# if ( data.width && data.height ) { #> 289 <div class="dimensions">{{ data.width }} × {{ data.height }}</div> 290 <# } #> 291 292 <# if ( data.can.save ) { #> 293 <a class="edit-attachment" href="{{ data.editLink }}&image-editor" target="_blank"><?php _e( 'Edit Image' ); ?></a> 294 <a class="refresh-attachment" href="#"><?php _e( 'Refresh' ); ?></a> 295 <# } #> 296 <# } #> 297 298 <# if ( data.fileLength ) { #> 299 <div class="file-length"><?php _e( 'Length:' ); ?> {{ data.fileLength }}</div> 300 <# } #> 301 302 <# if ( ! data.uploading && data.can.remove ) { #> 303 <?php if ( MEDIA_TRASH ): ?> 304 <a class="trash-attachment" href="#"><?php _e( 'Trash' ); ?></a> 305 <?php else: ?> 306 <a class="delete-attachment" href="#"><?php _e( 'Delete Permanently' ); ?></a> 307 <?php endif; ?> 308 <# } #> 309 310 <div class="compat-meta"> 311 <# if ( data.compat && data.compat.meta ) { #> 312 {{{ data.compat.meta }}} 313 <# } #> 314 </div> 315 </div> 316 </div> 317 <div class="attachment-fields"> 318 <label class="setting" data-setting="url"> 319 <span class="name"><?php _e('URL'); ?></span> 320 <input type="text" value="{{ data.url }}" readonly /> 321 </label> 322 <# var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly'; #> 323 <label class="setting" data-setting="title"> 324 <span class="name"><?php _e('Title'); ?></span> 325 <input type="text" value="{{ data.title }}" {{ maybeReadOnly }} /> 326 </label> 327 <label class="setting" data-setting="caption"> 328 <span class="name"><?php _e('Caption'); ?></span> 329 <textarea {{ maybeReadOnly }}>{{ data.caption }}</textarea> 330 </label> 331 <# if ( 'image' === data.type ) { #> 332 <label class="setting" data-setting="alt"> 333 <span class="name"><?php _e('Alt Text'); ?></span> 334 <input type="text" value="{{ data.alt }}" {{ maybeReadOnly }} /> 335 </label> 336 <# } #> 337 <label class="setting" data-setting="description"> 338 <span class="name"><?php _e('Description'); ?></span> 339 <textarea {{ maybeReadOnly }}>{{ data.description }}</textarea> 340 </label> 341 <label class="setting"> 342 <span class="name"><?php _e( 'Uploaded By' ); ?></span> 343 <span class="value">{{ data.authorName }}</span> 344 </label> 345 <# if ( data.uploadedTo ) { #> 346 <label class="setting"> 347 <span class="name"><?php _e('Uploaded To'); ?></span> 348 <span class="value"><a href="{{ data.uploadedToLink }}">{{ data.uploadedToTitle }}</a></span> 349 </label> 350 <# } #> 351 </div> 352 </script> 353 244 354 <script type="text/html" id="tmpl-attachment"> 245 355 <div class="attachment-preview type-{{ data.type }} subtype-{{ data.subtype }} {{ data.orientation }}"> 246 356 <# if ( data.uploading ) { #> … … 251 361 <img src="{{ data.size.url }}" draggable="false" alt="" /> 252 362 </div> 253 363 </div> 254 <# } else { #> 364 <# } else { 365 if ( data.thumb && data.thumb.src && data.thumb.src !== data.icon ) { 366 #><img src="{{ data.thumb.src }}" class="thumbnail" draggable="false" /><# 367 } #> 255 368 <img src="{{ data.icon }}" class="icon" draggable="false" /> 256 369 <div class="filename"> 257 370 <div>{{ data.filename }}</div> 258 371 </div> 259 372 <# } #> 260 373 <# if ( _.contains( data.controller.options.mode, 'grid' ) ) { #> 374 <span class="edit-media">Edit</span> 375 <# } #> 261 376 <# if ( data.buttons.close ) { #> 262 377 <a class="close media-modal-icon" href="#" title="<?php esc_attr_e('Remove'); ?>"></a> 263 378 <# } #> … … 268 383 </div> 269 384 <# 270 385 var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly'; 271 if ( data.describe ) { #>272 <#if ( 'image' === data.type ) { #>386 if ( data.describe ) { 387 if ( 'image' === data.type ) { #> 273 388 <input type="text" value="{{ data.caption }}" class="describe" data-setting="caption" 274 389 placeholder="<?php esc_attr_e('Caption this image…'); ?>" {{ maybeReadOnly }} /> 275 390 <# } else { #> … … 281 396 <# } else { #> 282 397 placeholder="<?php esc_attr_e('Describe this media file…'); ?>" 283 398 <# } #> {{ maybeReadOnly }} /> 284 <# } #> 399 <# } 400 } 401 402 if ( _.contains( data.controller.options.mode, 'grid' ) ) { #> 403 <div class="data-fields"> 404 <# _.each( data.showAttachmentFields, function( field ) { #> 405 <div class="data-field data-{{ field }}"><# 406 if ( 'uploadedTo' === field ) { 407 if ( data[field] ) { 408 #><?php _e( 'Uploaded To:' ) ?><# 409 } else { 410 #><?php _e( 'Unattached' ) ?><# 411 } 412 } else if ( 'title' === field && ! data[ field ] ) { 413 #><?php _e( '(No title)' ) ?><# 414 } 415 416 if ( data[ field ] ) { 417 #>{{ data[ field ] }}<# 418 } 419 #></div> 420 <# }); #> 421 </div> 285 422 <# } #> 423 286 424 </script> 287 425 288 426 <script type="text/html" id="tmpl-attachment-details"> -
src/wp-includes/script-loader.php
506 506 $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox' ), false, 1 ); 507 507 508 508 $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" ); 509 509 $scripts->add( 'media-grid', "/wp-includes/js/media-grid$suffix.js", array( 'media-editor' ), false, 1 ); 510 510 $scripts->add( 'media', "/wp-admin/js/media$suffix.js", array( 'jquery' ), false, 1 ); 511 511 did_action( 'init' ) && $scripts->localize( 'media', 'attachMediaBoxL10n', array( 512 512 'error' => __( 'An error has occurred. Please reload the page and try again.' ),