WordPress.org

Make WordPress Core

Ticket #24716: 24716.18.diff

File 24716.18.diff, 35.1 KB (added by wonderboymusic, 4 years ago)
  • src/wp-admin/upload.php

     
    2424        wp_enqueue_media();
    2525        wp_enqueue_script( 'media-grid' );
    2626        wp_enqueue_script( 'media' );
    27        
     27
    2828        require_once( ABSPATH . 'wp-admin/admin-header.php' );
    29         ?><div class="view-switch media-grid-view-switch">
    30                 <a href="<?php echo esc_url( add_query_arg( 'mode', 'list', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-list">
    31                         <img id="view-switch-list" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="List View" alt="List View"/>
    32                 </a>
    33                 <a href="<?php echo esc_url( add_query_arg( 'mode', 'grid', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-grid current">
    34                         <img id="view-switch-excerpt" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="Grid View" alt="Grid View"/>
    35                 </a>
    36         </div><?php
    3729        include( ABSPATH . 'wp-admin/admin-footer.php' );
    3830        exit;
    3931}
  • src/wp-includes/css/media-views.css

     
    750750        max-height: 100%;
    751751}
    752752
     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
    753777.attachment-preview .thumbnail:after {
    754778        content: '';
    755779        display: block;
     
    909933        border-radius: 0;
    910934}
    911935
     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
    912952/**
    913953 * Attachments Browser
    914954 */
     
    924964        height: 50px;
    925965}
    926966
     967.attachments-browser.hide-sidebar .media-toolbar {
     968        right: 0;
     969}
     970
    927971.attachments-browser .media-toolbar-primary > .media-button,
    928972.attachments-browser .media-toolbar-primary > .media-button-group,
    929973.attachments-browser .media-toolbar-secondary > .media-button,
     
    942986        outline: none;
    943987}
    944988
     989.inline-toolbar {
     990        position: absolute;
     991        top: 0;
     992        left: 0;
     993        display: none;
     994        z-index: 100;
     995}
     996
     997.inline-toolbar .remove {
     998        display: none;
     999}
     1000
     1001.active-video .inline-toolbar .remove {
     1002        display: inline-block;
     1003}
     1004
     1005.attachment:hover .inline-toolbar {
     1006        display: block;
     1007}
     1008
     1009.inline-toolbar div,
     1010.inline-toolbar .inline-media-control {
     1011        display: inline-block;
     1012        margin-top: 4px;
     1013        margin-left: 4px;
     1014        padding: 2px;
     1015        width: 20px;
     1016        height: 20px;
     1017        box-shadow: 0 1px 3px rgba(0,0,0,0.5);
     1018        background-color: #000;
     1019        background-color: rgba(0,0,0,0.9);
     1020        cursor: pointer;
     1021        color: white;
     1022        font-size: 20px;
     1023}
     1024
     1025.ie8 .inline-toolbar div,
     1026.ie7 .inline-toolbar div {
     1027        display: inline;
     1028        padding: 0;
     1029}
     1030
     1031.inline-media-control span {
     1032        display: block;
     1033        width: 16px;
     1034        height: 16px;
     1035        margin: 2px;
     1036        background: url(/wp-includes/js/mediaelement/controls.png) 0 0 no-repeat;
     1037}
     1038
     1039.inline-media-control.active span {
     1040        margin: 2px;
     1041        background-position: 0 -16px;
     1042}
     1043
     1044.inline-media-control.paused span {
     1045        margin: 2px;
     1046        background-position: 0 0;
     1047}
     1048
     1049audio#inline-media-node {
     1050        display: none;
     1051}
     1052
     1053video#inline-media-node {
     1054        position: relative;
     1055        z-index: 5;
     1056        top: 0;
     1057        left: 0;
     1058}
     1059
     1060.inline-video-wrap {
     1061        width: 100%;
     1062        height: auto;
     1063        position: absolute;
     1064        z-index: 5;
     1065        background: #000;
     1066        padding: 10px 0 5px;
     1067        top: 0;
     1068        left: 0;
     1069}
     1070
     1071.attachments-browser.hide-sidebar .attachments {
     1072        right: 0;
     1073}
     1074
    9451075.attachments-browser .instructions {
    9461076        display: inline-block;
    9471077        margin-top: 16px;
     
    23882518        line-height: 29px;
    23892519}
    23902520
    2391 .media-grid-view-switch {
    2392         position: fixed;
    2393         right: 10px;
    2394         top: 44px;
    2395         z-index: 300;
     2521.media-grid-view .view-switch {
     2522        display: inline-block;
     2523        float: none;
     2524        margin-top: 13px;
     2525        vertical-align: middle;
    23962526}
    23972527
    23982528/**
     
    24272557        display: none;
    24282558}
    24292559
     2560/**
     2561 * Copied styles from the Add theme toolbar.
     2562 *
     2563 * This should be OOCSS'd so both use a shared selector.
     2564 */
     2565.media-grid-view .media-toolbar {
     2566        background: #fff;
     2567        -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
     2568        box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
     2569        -webkit-box-sizing: border-box;
     2570        -moz-box-sizing: border-box;
     2571        box-sizing: border-box;
     2572        color: #555;
     2573        display: inline-block;
     2574        font-size: 13px;
     2575        padding: 0 20px;
     2576        position: relative;
     2577        width: 100%;
     2578}
     2579
     2580/**
     2581 * The left and right buttons are copied from the expanded theme details modal.
     2582 *
     2583 * This should be OOCSS'd so both use a shared selector.
     2584 */
     2585.edit-attachment-frame .edit-media-header .left,
     2586.edit-attachment-frame .edit-media-header .right {
     2587        cursor: pointer;
     2588        color: #777;
     2589        background-color: transparent;
     2590        height: 48px;
     2591        width: 54px;
     2592        float: left;
     2593        text-align: center;
     2594        border: 0;
     2595        border-right: 1px solid #ddd;
     2596}
     2597
     2598.edit-attachment-frame .edit-media-header .right:before,
     2599.edit-attachment-frame .edit-media-header .left:before {
     2600        font: normal 20px/50px 'dashicons' !important;
     2601        display: inline;
     2602        font-weight: 300;
     2603}
     2604
     2605
     2606.edit-attachment-frame .edit-media-header .left:before {
     2607        content: '\f340';
     2608}
     2609
     2610.edit-attachment-frame .edit-media-header .right:before {
     2611        content: '\f344';
     2612}
     2613
     2614.edit-attachment-frame .edit-media-header .left.disabled,
     2615.edit-attachment-frame .edit-media-header .right.disabled,
     2616.edit-attachment-frame .edit-media-header .left.disabled:hover,
     2617.edit-attachment-frame .edit-media-header .right.disabled:hover {
     2618        color: #ccc;
     2619        background: inherit;
     2620        cursor: inherit;
     2621}
     2622
     2623.edit-attachment-frame .edit-media-header .close:hover,
     2624.edit-attachment-frame .edit-media-header .right:hover,
     2625.edit-attachment-frame .edit-media-header .left:hover,
     2626.edit-attachment-frame .edit-media-header .close:focus,
     2627.edit-attachment-frame .edit-media-header .right:focus,
     2628.edit-attachment-frame .edit-media-header .left:focus {
     2629        background: #0074a2;
     2630        color: #fff;
     2631}
     2632
     2633.edit-attachment-frame .media-frame-content,
     2634.edit-attachment-frame .media-frame-router {
     2635        left: 0;
     2636}
     2637
     2638/* Hiding this for the moment instead of removing it from the template. */
     2639.edit-attachment-frame h3 {
     2640        display: none;
     2641}
     2642
     2643.edit-attachment-frame .attachment-details {
     2644        position: absolute;
     2645        overflow: auto;
     2646        top: 0;
     2647        bottom: 0;
     2648        right: 0;
     2649        left: 0;
     2650}
     2651
     2652.edit-attachment-frame .attachment-info {
     2653        border-bottom: 0;
     2654        border-right: 1px solid #ddd;
     2655        bottom: 0;
     2656        position: absolute;
     2657        top: 0;
     2658        left: 0;
     2659        margin-bottom: 0;
     2660        padding: 2% 4%;
     2661        right: 50%;
     2662}
     2663
     2664.edit-attachment-frame .attachment-info .thumbnail {
     2665        max-width: none;
     2666        max-height: none;
     2667}
     2668
     2669.edit-attachment-frame .attachment-info .thumbnail-image img {
     2670        margin: 0;
     2671}
     2672
     2673.edit-attachment-frame .attachment-info .thumbnail-image:after {
     2674        -webkit-box-shadow: none;
     2675                box-shadow: none;
     2676}
     2677
     2678.edit-attachment-frame .attachment-info .thumbnail img {
     2679        max-width: none;
     2680        max-height: 50%;
     2681}
     2682
     2683.edit-attachment-frame .attachment-info .details {
     2684        float: none;
     2685}
     2686
     2687.edit-attachment-frame .wp-media-wrapper {
     2688        margin-top: 20px;
     2689}
     2690
     2691.edit-attachment-frame .attachment-fields {
     2692        bottom: 0;
     2693        padding: 2% 4%;
     2694        position: absolute;
     2695        top: 0;
     2696        left: 50%;
     2697        right: 0;
     2698}
     2699
     2700.edit-attachment-frame .attachment-fields .setting {
     2701        display: block;
     2702        float: left;
     2703        width: 100%;
     2704        margin: 1px 0;
     2705}
     2706
     2707.edit-attachment-frame .attachment-fields .setting label {
     2708        display: block;
     2709}
     2710
     2711.edit-attachment-frame .attachment-fields .setting .link-to-custom {
     2712        margin: 3px 0;
     2713}
     2714
     2715.edit-attachment-frame .attachment-fields .setting .name {
     2716        min-width: 30%;
     2717        margin-right: 4%;
     2718        font-size: 12px;
     2719        text-align: right;
     2720}
     2721
     2722.edit-attachment-frame .attachment-fields .setting select {
     2723        max-width: 65%;
     2724}
     2725
     2726.edit-attachment-frame .attachment-fields .setting input[type="checkbox"],
     2727.edit-attachment-frame .attachment-fields .field input[type="checkbox"] {
     2728        width: 16px;
     2729        float: none;
     2730        margin: 8px 3px 0;
     2731        padding: 0;
     2732}
     2733
     2734.edit-attachment-frame .attachment-fields .setting span {
     2735        float: left;
     2736        min-height: 22px;
     2737        padding-top: 8px;
     2738        line-height: 16px;
     2739        font-weight: normal;
     2740        color: #666;
     2741}
     2742
     2743.edit-attachment-frame .attachment-fields .setting input[type="text"],
     2744.edit-attachment-frame .attachment-fields .setting input[type="password"],
     2745.edit-attachment-frame .attachment-fields .setting input[type="number"],
     2746.edit-attachment-frame .attachment-fields .setting input[type="search"],
     2747.edit-attachment-frame .attachment-fields .setting input[type="email"],
     2748.edit-attachment-frame .attachment-fields .setting input[type="url"],
     2749.edit-attachment-frame .attachment-fields .setting textarea,
     2750.edit-attachment-frame .attachment-fields .setting .value {
     2751        margin: 1px;
     2752        width: 65%;
     2753        float: right;
     2754        padding: 6px 8px;
     2755        -webkit-box-sizing: border-box;
     2756        -moz-box-sizing: border-box;
     2757        box-sizing: border-box;
     2758}
     2759
     2760.edit-attachment-frame .attachment-fields .setting textarea {
     2761        height: 62px;
     2762        resize: vertical;
     2763}
     2764
     2765.edit-attachment-frame .attachment-fields select {
     2766        margin-top: 3px;
     2767}
     2768
     2769.media-grid-view.hide-router .media-frame-title {
     2770        box-shadow: none;
     2771}
     2772
    24302773.media-grid-view .media-frame-content {
     2774        background-color: transparent;
    24312775        bottom: 40px;
    24322776}
    24332777@media screen and (max-width: 782px) {
  • src/wp-includes/js/media-audiovideo.js

     
    2626                        }
    2727                },
    2828
     29                removeAllPlayers: function() {
     30                        var p;
     31
     32                        if ( window.mejs && window.mejs.players ) {
     33                                for ( p in window.mejs.players ) {
     34                                        window.mejs.players[p].pause();
     35                                        this.removePlayer( window.mejs.players[p] );
     36                                }
     37                        }
     38                },
     39
    2940                /**
    3041                 * Pauses the current object's instances of MediaElementPlayer
    3142                 */
  • src/wp-includes/js/media-grid.js

     
    1 (function( $, _, Backbone, wp ) {
     1(function($, _, Backbone, wp) {
    22        var media = wp.media, l10n;
    33
    44        // Link any localized strings.
     
    1010        }
    1111
    1212        /**
     13         * A state for editing (cropping, etc.) an image.
     14         *
     15         * @constructor
     16         * @augments wp.media.controller.State
     17         * @augments Backbone.Model
     18         */
     19        media.controller.EditImageNoFrame = media.controller._State.extend({
     20                defaults: {
     21                        id:      'edit-attachment',
     22                        title:   l10n.editImage,
     23                        // Region mode defaults.
     24                        menu:    false,
     25                        router:  'edit-metadata',
     26                        content: 'edit-metadata',
     27                        toolbar: 'toolbar',
     28
     29                        url:     ''
     30                },
     31
     32                initialize: function() {
     33                        media.controller._State.prototype.initialize.apply( this, arguments );
     34                },
     35
     36                activate: function() {
     37                        this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
     38                },
     39
     40                _postActivate: function() {
     41                        this._content();
     42                        this._router();
     43                },
     44
     45                deactivate: function() {
     46                        this.stopListening( this.frame );
     47                },
     48
     49                toolbar: function() {
     50                        var frame = this.frame,
     51                                lastState = frame.lastState(),
     52                                previous = lastState && lastState.id;
     53
     54                        frame.toolbar.set( new media.view.Toolbar({
     55                                controller: frame,
     56                                items: {
     57                                        back: {
     58                                                style: 'primary',
     59                                                text:     l10n.back,
     60                                                priority: 20,
     61                                                click:    function() {
     62                                                        if ( previous ) {
     63                                                                frame.setState( previous );
     64                                                        } else {
     65                                                                frame.close();
     66                                                        }
     67                                                }
     68                                        }
     69                                }
     70                        }) );
     71                },
     72
     73                /**
     74                 * @access private
     75                 */
     76                _router: function() {
     77                        var router = this.frame.router,
     78                                mode = this.get('router'),
     79                                view;
     80
     81                        this.frame.$el.toggleClass( 'hide-router', ! mode );
     82                        if ( ! mode ) {
     83                                return;
     84                        }
     85
     86                        this.frame.router.render( mode );
     87
     88                        view = router.get();
     89                        if ( view && view.select ) {
     90                                view.select( this.frame.content.mode() );
     91                        }
     92                },
     93
     94                _content: function() {
     95                        var mode = this.get( 'content' );
     96                        if ( mode ) {
     97                                this.frame[ 'content' ].render( mode );
     98                        }
     99                }
     100        });
     101
     102        /**
    13103         * wp.media.view.MediaFrame.Manage
    14104         *
    15105         * A generic management frame workflow.
     
    36126                                library:   {},
    37127                                multiple:  false,
    38128                                state:     'library',
    39                                 uploader:  true
     129                                uploader:  true,
     130                                mode:      [ 'grid', 'edit' ]
    40131                        });
    41132
    42133                        // Ensure core and media grid view UI is enabled.
     
    111202                                        router:     false,
    112203                                        content:    'browse',
    113204                                        filterable: 'mime-types'
    114                                 }),
    115 
    116                                 new media.controller.EditImage( { model: options.editImage } )
     205                                })
    117206                        ]);
    118207                },
    119208
     
    120209                bindHandlers: function() {
    121210                        this.on( 'content:create:browse', this.browseContent, this );
    122211                        this.on( 'content:render:edit-image', this.editImageContent, this );
     212
     213                        // Handle a frame-level event for editing an attachment.
     214                        this.on( 'edit:attachment', this.editAttachment, this );
     215                        this.on( 'edit:attachment:next', this.editNextAttachment, this );
     216                        this.on( 'edit:attachment:previous', this.editPreviousAttachment, this );
    123217                },
    124218
     219                editPreviousAttachment: function( currentModel ) {
     220                        var library = this.state().get('library'),
     221                            currentModelIndex = library.indexOf( currentModel );
     222                        this.trigger( 'edit:attachment', library.at( currentModelIndex - 1 ) );
     223                },
     224
     225                editNextAttachment: function( currentModel ) {
     226                        var library = this.state().get('library'),
     227                            currentModelIndex = library.indexOf( currentModel );
     228                        this.trigger( 'edit:attachment', library.at( currentModelIndex + 1 ) );
     229                },
     230
    125231                /**
     232                 * Open the Edit Attachment modal.
     233                 */
     234                editAttachment: function( model ) {
     235                        var library = this.state().get('library'), hasPrevious, hasNext;
     236                        if ( library.indexOf( model ) > 0 ) {
     237                                hasPrevious = true;
     238                        }
     239                        else {
     240                                hasPrevious = false;
     241                        }
     242                        if ( library.indexOf( model ) < library.length - 1 ) {
     243                                hasNext = true;
     244                        }
     245                        else {
     246                                hasNext = false;
     247                        }
     248
     249                        new media.view.Frame.EditAttachment({
     250                                hasPrevious:    hasPrevious,
     251                                hasNext:        hasNext,
     252                                model:          model,
     253                                gridController: this
     254                        });
     255                },
     256
     257                /**
    126258                 * Content
    127259                 *
    128260                 * @param {Object} content
     
    143275                                display:    state.get('displaySettings'),
    144276                                dragInfo:   state.get('dragInfo'),
    145277                                bulkEdit:   true,
     278                                sidebar:    false,
    146279
    147280                                suggestedWidth:  state.get('suggestedWidth'),
    148281                                suggestedHeight: state.get('suggestedHeight'),
     
    162295
    163296                }
    164297        });
    165        
    166 }( jQuery, _, Backbone, wp ));
    167  No newline at end of file
     298
     299        media.view.Attachment.Details.TwoColumn = media.view.Attachment.Details.extend({
     300                template: wp.template( 'attachment-details-two-column' ),
     301
     302                initialize: function() {
     303                        this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
     304                        this.model.on( 'change:sizes change:uploading', this.render, this );
     305                        this.model.on( 'change:title', this._syncTitle, this );
     306                        this.model.on( 'change:caption', this._syncCaption, this );
     307                        this.model.on( 'change:percent', this.progress, this );
     308
     309                        // Update the selection.
     310                        this.model.on( 'add', this.select, this );
     311                        this.model.on( 'remove', this.deselect, this );
     312                },
     313
     314                render: function() {
     315                        media.view.Attachment.Details.prototype.render.apply( this, arguments );
     316
     317                        media.mixin.removeAllPlayers();
     318                        $( 'audio, video', this.$el ).each( function (i, elem) {
     319                                var el = media.view.MediaDetails.prepareSrc( elem );
     320                                new MediaElementPlayer( el, media.mixin.mejsSettings );
     321                        } );
     322                }
     323        });
     324
     325        /**
     326         * A frame for editing the details of a specific media item.
     327         *
     328         * Opens in a modal by default.
     329         *
     330         * Requires an attachment model to be passed in the options hash under `model`.
     331         */
     332        media.view.Frame.EditAttachment = media.view.Frame.extend({
     333
     334                className: 'edit-attachment-frame',
     335                template: media.template( 'edit-attachment-frame' ),
     336                regions:   [ 'router', 'content' ],
     337
     338                events: {
     339                        'click':                    'collapse',
     340                        'click .delete-media-item': 'deleteMediaItem',
     341                        'click .left':              'previousMediaItem',
     342                        'click .right':             'nextMediaItem'
     343                },
     344
     345                initialize: function( options ) {
     346                        var self = this;
     347                        media.view.Frame.prototype.initialize.apply( this, arguments );
     348
     349                        _.defaults( this.options, {
     350                                modal: true,
     351                                state: 'edit-attachment'
     352                        });
     353
     354                        this.createStates();
     355
     356                        this.on( 'content:render:edit-metadata', this.editMetadataContent, this );
     357                        this.on( 'content:render:edit-image', this.editImageContentUgh, this );
     358
     359                        // Only need a tab to Edit Image for images.
     360                        if ( this.model.get( 'type' ) === 'image' ) {
     361                                this.on( 'router:create', this.createRouter, this );
     362                                this.on( 'router:render', this.browseRouter, this );
     363                        }
     364
     365                        // Initialize modal container view.
     366                        if ( this.options.modal ) {
     367                                this.modal = new media.view.Modal({
     368                                        controller: this,
     369                                        title:      this.options.title
     370                                });
     371
     372                                // Completely destroy the modal DOM element when closing it.
     373                                this.modal.close = function() {
     374                                        self.modal.remove();
     375                                };
     376
     377                                this.modal.content( this );
     378                                this.modal.open();
     379                        }
     380                },
     381
     382                /**
     383                 * Add the default states to the frame.
     384                 */
     385                createStates: function() {
     386                        this.states.add([
     387                                new media.controller.EditImageNoFrame( { model: this.model } )
     388                        ]);
     389                },
     390
     391                /**
     392                 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
     393                 */
     394                render: function() {
     395                        // Activate the default state if no active state exists.
     396                        if ( ! this.state() && this.options.state ) {
     397                                this.setState( this.options.state );
     398                        }
     399                        /**
     400                         * call 'render' directly on the parent class
     401                         */
     402                        return media.view.Frame.prototype.render.apply( this, arguments );
     403                },
     404
     405                /**
     406                 * Content region rendering callback for the `edit-metadata` mode.
     407                 */
     408                editMetadataContent: function() {
     409                        var view = new media.view.Attachment.Details.TwoColumn({
     410                                controller: this,
     411                                model:      this.model
     412                        });
     413                        this.content.set( view );
     414                },
     415
     416                /**
     417                 * For some reason the view doesn't exist in the DOM yet, don't have the
     418                 * patience to track this down right now.
     419                 */
     420                editImageContentUgh: function() {
     421                        _.defer( _.bind( this.editImageContent, this ) );
     422                },
     423
     424                /**
     425                 * Render the EditImage view into the frame's content region.
     426                 */
     427                editImageContent: function() {
     428                        var view = new media.view.EditImage( { model: this.model, controller: this } ).render();
     429
     430                        this.content.set( view );
     431
     432                        // after creating the wrapper view, load the actual editor via an ajax call
     433                        view.loadEditor();
     434                },
     435
     436                /**
     437                 * Create the router view.
     438                 *
     439                 * @param {Object} router
     440                 * @this wp.media.controller.Region
     441                 */
     442                createRouter: function( router ) {
     443                        router.view = new media.view.Router({
     444                                controller: this
     445                        });
     446                },
     447
     448                /**
     449                 * Router rendering callback.
     450                 *
     451                 * @param  media.view.Router view Instantiated in this.createRouter()
     452                 */
     453                browseRouter: function( view ) {
     454                        view.set({
     455                                'edit-metadata': {
     456                                        text:     'Edit Metadata',
     457                                        priority: 20
     458                                },
     459                                'edit-image': {
     460                                        text:     'Edit Image',
     461                                        priority: 40
     462                                }
     463                        });
     464                },
     465
     466                /**
     467                 * Click handler to switch to the previous media item.
     468                 */
     469                previousMediaItem: function() {
     470                        if ( ! this.options.hasPrevious )
     471                                return;
     472                        this.modal.close();
     473                        this.options.gridController.trigger( 'edit:attachment:previous', this.model );
     474                },
     475
     476                /**
     477                 * Click handler to switch to the next media item.
     478                 */
     479                nextMediaItem: function() {
     480                        if ( ! this.options.hasNext )
     481                                return;
     482                        this.modal.close();
     483                        this.options.gridController.trigger( 'edit:attachment:next', this.model );
     484                }
     485
     486        });
     487
     488}(jQuery, _, Backbone, wp));
     489 No newline at end of file
  • src/wp-includes/js/media-views.js

     
    337337        });
    338338
    339339        /**
    340          * wp.media.controller.State
    341          *
    342          * A state is a step in a workflow that when set will trigger the controllers
    343          * for the regions to be updated as specified in the frame. This is the base
    344          * class that the various states used in wp.media extend.
    345          *
    346          * @constructor
    347          * @augments Backbone.Model
     340         * A more abstracted state, because media.controller.State expects
     341         * specific regions (menu, title, etc.) to exist on the frame, which do not
     342         * exist in media.view.Frame.EditAttachment.
    348343         */
    349         media.controller.State = Backbone.Model.extend({
     344        media.controller._State = Backbone.Model.extend({
    350345                constructor: function() {
    351346                        this.on( 'activate', this._preActivate, this );
    352347                        this.on( 'activate', this.activate, this );
     
    354349                        this.on( 'deactivate', this._deactivate, this );
    355350                        this.on( 'deactivate', this.deactivate, this );
    356351                        this.on( 'reset', this.reset, this );
    357                         this.on( 'ready', this._ready, this );
    358352                        this.on( 'ready', this.ready, this );
    359353                        /**
    360354                         * Call parent constructor with passed arguments
    361355                         */
    362356                        Backbone.Model.apply( this, arguments );
    363                         this.on( 'change:menu', this._updateMenu, this );
    364357                },
    365358
    366359                /**
     
    382375                /**
    383376                 * @access private
    384377                 */
    385                 _ready: function() {
    386                         this._updateMenu();
     378                _preActivate: function() {
     379                        this.active = true;
    387380                },
    388381                /**
    389382                 * @access private
    390383                 */
    391                 _preActivate: function() {
    392                         this.active = true;
     384                _postActivate: function() {},
     385                /**
     386                 * @access private
     387                 */
     388                _deactivate: function() {
     389                        this.active = false;
     390                }
     391        });
     392
     393        /**
     394         * wp.media.controller.State
     395         *
     396         * A state is a step in a workflow that when set will trigger the controllers
     397         * for the regions to be updated as specified in the frame. This is the base
     398         * class that the various states used in wp.media extend.
     399         *
     400         * @constructor
     401         * @augments Backbone.Model
     402         */
     403        media.controller.State = media.controller._State.extend({
     404                constructor: function() {
     405                        this.on( 'activate', this._preActivate, this );
     406                        this.on( 'activate', this.activate, this );
     407                        this.on( 'activate', this._postActivate, this );
     408                        this.on( 'deactivate', this._deactivate, this );
     409                        this.on( 'deactivate', this.deactivate, this );
     410                        this.on( 'reset', this.reset, this );
     411                        this.on( 'ready', this._ready, this );
     412                        this.on( 'ready', this.ready, this );
     413                        /**
     414                         * Call parent constructor with passed arguments
     415                         */
     416                        Backbone.Model.apply( this, arguments );
     417                        this.on( 'change:menu', this._updateMenu, this );
    393418                },
     419
    394420                /**
    395421                 * @access private
    396422                 */
     423                _ready: function() {
     424                        this._updateMenu();
     425                },
     426
     427                /**
     428                 * @access private
     429                 */
    397430                _postActivate: function() {
    398431                        this.on( 'change:menu', this._menu, this );
    399432                        this.on( 'change:titleMode', this._title, this );
     
    17581791                        _.defaults( this.options, {
    17591792                                title:    '',
    17601793                                modal:    true,
    1761                                 uploader: true
     1794                                uploader: true,
     1795                                mode:     ['select']
    17621796                        });
    17631797
    17641798                        // Ensure core UI is enabled.
     
    45304564                        var selection = this.options.selection;
    45314565
    45324566                        this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
    4533                         this.model.on( 'change:sizes change:uploading', this.render, this );
     4567                        this.model.on( 'change', this.render, this );
    45344568                        this.model.on( 'change:title', this._syncTitle, this );
    45354569                        this.model.on( 'change:caption', this._syncCaption, this );
    45364570                        this.model.on( 'change:percent', this.progress, this );
     
    45834617                                        compat:        false,
    45844618                                        alt:           '',
    45854619                                        description:   ''
    4586                                 });
     4620                                }, this.options );
    45874621
    45884622                        options.buttons  = this.buttons;
    45894623                        options.describe = this.controller.state().get('describe');
     
    46334667                 */
    46344668                toggleSelectionHandler: function( event ) {
    46354669                        var method;
    4636 
    46374670                        // Catch enter and space events
    46384671                        if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
    46394672                                return;
    46404673                        }
     4674
     4675                        // In the grid view, bubble up an edit:attachment event to the controller.
     4676                        if ( _.contains( this.controller.options.mode, 'grid' ) ) {
     4677                                this.controller.trigger( 'edit:attachment', this.model );
     4678                                return;
     4679                        }
     4680
    46414681                        if ( event.shiftKey ) {
    46424682                                method = 'between';
    46434683                        } else if ( event.ctrlKey || event.metaKey ) {
     
    51685208                 */
    51695209                createAttachmentView: function( attachment ) {
    51705210                        var view = new this.options.AttachmentView({
    5171                                 controller: this.controller,
    5172                                 model:      attachment,
    5173                                 collection: this.collection,
    5174                                 selection:  this.options.selection
     5211                                controller:           this.controller,
     5212                                model:                attachment,
     5213                                collection:           this.collection,
     5214                                selection:            this.options.selection,
     5215                                showAttachmentFields: this.options.showAttachmentFields
    51755216                        });
    51765217
    51775218                        return this._viewsByCid[ attachment.cid ] = view;
     
    54685509                }
    54695510        });
    54705511
    5471 
    54725512        /**
    54735513         * wp.media.view.AttachmentsBrowser
    54745514         *
     
    54865526                                filters: false,
    54875527                                search:  true,
    54885528                                display: false,
    5489 
     5529                                sidebar: true,
     5530                                showAttachmentFields: getUserSetting( 'showAttachmentFields', [ 'title', 'uploadedTo', 'dateFormatted', 'mime' ] ),
    54905531                                AttachmentView: media.view.Attachment.Library
    54915532                        });
    54925533
    54935534                        this.createToolbar();
    54945535                        this.updateContent();
    5495                         this.createSidebar();
     5536                        if ( this.options.sidebar ) {
     5537                                this.createSidebar();
     5538                        } else {
     5539                                this.$el.addClass( 'hide-sidebar' );
     5540                        }
    54965541
    54975542                        this.collection.on( 'add remove reset', this.updateContent, this );
    54985543                },
     
    55175562
    55185563                        this.views.add( this.toolbar );
    55195564
     5565                        // Feels odd to bring the global media library switcher into the Attachment
     5566                        // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
     5567                        // which the controller can tap into and add this view?
     5568                        if ( _.contains( this.controller.options.mode, 'grid' ) ) {
     5569                                var libraryViewSwitcherConstructor = media.View.extend({
     5570                                        className: 'view-switch media-grid-view-switch',
     5571                                        template: media.template( 'media-library-view-switcher')
     5572                                });
     5573                                this.toolbar.set( 'libraryViewSwitcher', new libraryViewSwitcherConstructor({
     5574                                        controller: this.controller,
     5575                                        priority: -90
     5576                                }).render() );
     5577                        }
     5578
    55205579                        filters = this.options.filters;
    55215580                        if ( 'uploaded' === filters ) {
    55225581                                FiltersConstructor = media.view.AttachmentFilters.Uploaded;
     
    56115670                        this.removeContent();
    56125671
    56135672                        this.attachments = new media.view.Attachments({
    5614                                 controller: this.controller,
    5615                                 collection: this.collection,
    5616                                 selection:  this.options.selection,
    5617                                 model:      this.model,
    5618                                 sortable:   this.options.sortable,
     5673                                controller:           this.controller,
     5674                                collection:           this.collection,
     5675                                selection:            this.options.selection,
     5676                                model:                this.model,
     5677                                sortable:             this.options.sortable,
     5678                                showAttachmentFields: this.options.showAttachmentFields,
    56195679
    56205680                                // The single `Attachment` view to be used in the `Attachments` view.
    56215681                                AttachmentView: this.options.AttachmentView
  • src/wp-includes/media-template.php

     
    220220                </div>
    221221        </script>
    222222
     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
    223232        <script type="text/html" id="tmpl-uploader-status">
    224233                <h3><?php _e( 'Uploading' ); ?></h3>
    225234                <a class="upload-dismiss-errors" href="#"><?php _e('Dismiss Errors'); ?></a>
     
    241250                <span class="upload-error-message">{{ data.message }}</span>
    242251        </script>
    243252
     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 }} &times; {{ data.height }}</div>
     290                                        <# } #>
     291
     292                                        <# if ( data.can.save ) { #>
     293                                                <a class="edit-attachment" href="{{ data.editLink }}&amp;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                        <# if ( 'audio' === data.type ) { #>
     317                        <div class="wp-media-wrapper">
     318                                <audio style="visibility: hidden" controls class="wp-audio-shortcode" width="100%" preload="none">
     319                                        <source type="{{ data.mime }}" src="{{ data.url }}"/>
     320                                </audio>
     321                        </div>
     322                        <# } else if ( 'video' === data.type ) { #>
     323                        <div style="max-width: 100%; width: {{ data.width }}px" class="wp-media-wrapper">
     324                                <video controls class="wp-video-shortcode" preload="metadata"
     325                                        width="{{ data.width }}" height="{{ data.height }}"
     326                                        <# if ( data.image && data.image.src !== data.icon ) { #>poster="{{ data.image.src }}"<# } #>>
     327                                        <source type="{{ data.mime }}" src="{{ data.url }}"/>
     328                                </video>
     329                        </div>
     330                        <# } #>
     331                </div>
     332                <div class="attachment-fields">
     333                        <label class="setting" data-setting="url">
     334                                <span class="name"><?php _e('URL'); ?></span>
     335                                <input type="text" value="{{ data.url }}" readonly />
     336                        </label>
     337                        <# var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly'; #>
     338                        <label class="setting" data-setting="title">
     339                                <span class="name"><?php _e('Title'); ?></span>
     340                                <input type="text" value="{{ data.title }}" {{ maybeReadOnly }} />
     341                        </label>
     342                        <label class="setting" data-setting="caption">
     343                                <span class="name"><?php _e('Caption'); ?></span>
     344                                <textarea {{ maybeReadOnly }}>{{ data.caption }}</textarea>
     345                        </label>
     346                        <# if ( 'image' === data.type ) { #>
     347                                <label class="setting" data-setting="alt">
     348                                        <span class="name"><?php _e('Alt Text'); ?></span>
     349                                        <input type="text" value="{{ data.alt }}" {{ maybeReadOnly }} />
     350                                </label>
     351                        <# } #>
     352                        <label class="setting" data-setting="description">
     353                                <span class="name"><?php _e('Description'); ?></span>
     354                                <textarea {{ maybeReadOnly }}>{{ data.description }}</textarea>
     355                        </label>
     356                        <label class="setting">
     357                                        <span class="name"><?php _e( 'Uploaded By' ); ?></span>
     358                                        <span class="value">{{ data.authorName }}</span>
     359                                </label>
     360                        <# if ( data.uploadedTo ) { #>
     361                                <label class="setting">
     362                                        <span class="name"><?php _e('Uploaded To'); ?></span>
     363                                        <span class="value"><a href="{{ data.uploadedToLink }}">{{ data.uploadedToTitle }}</a></span>
     364                                </label>
     365                        <# } #>
     366                </div>
     367        </script>
     368
    244369        <script type="text/html" id="tmpl-attachment">
     370                <# if ( _.contains( data.controller.options.mode, 'grid' ) ) { #>
     371                <div class="inline-toolbar">
     372                        <div class="dashicons dashicons-edit edit edit-media"></div>
     373                </div>
     374                <# } #>
    245375                <div class="attachment-preview type-{{ data.type }} subtype-{{ data.subtype }} {{ data.orientation }}">
    246376                        <# if ( data.uploading ) { #>
    247377                                <div class="media-progress-bar"><div></div></div>
     
    251381                                                <img src="{{ data.size.url }}" draggable="false" alt="" />
    252382                                        </div>
    253383                                </div>
    254                         <# } else { #>
     384                        <# } else {
     385                                if ( data.thumb && data.thumb.src && data.thumb.src !== data.icon ) {
     386                                #><img src="{{ data.thumb.src }}" class="thumbnail" draggable="false" /><#
     387                                } #>
    255388                                <img src="{{ data.icon }}" class="icon" draggable="false" />
    256389                                <div class="filename">
    257390                                        <div>{{ data.filename }}</div>
    258391                                </div>
    259392                        <# } #>
    260 
    261393                        <# if ( data.buttons.close ) { #>
    262394                                <a class="close media-modal-icon" href="#" title="<?php esc_attr_e('Remove'); ?>"></a>
    263395                        <# } #>
     
    268400                </div>
    269401                <#
    270402                var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly';
    271                 if ( data.describe ) { #>
    272                         <# if ( 'image' === data.type ) { #>
     403                if ( data.describe ) {
     404                        if ( 'image' === data.type ) { #>
    273405                                <input type="text" value="{{ data.caption }}" class="describe" data-setting="caption"
    274406                                        placeholder="<?php esc_attr_e('Caption this image&hellip;'); ?>" {{ maybeReadOnly }} />
    275407                        <# } else { #>
     
    281413                                        <# } else { #>
    282414                                                placeholder="<?php esc_attr_e('Describe this media file&hellip;'); ?>"
    283415                                        <# } #> {{ maybeReadOnly }} />
    284                         <# } #>
     416                        <# }
     417                }
     418
     419                if ( _.contains( data.controller.options.mode, 'grid' ) ) { #>
     420                <div class="data-fields">
     421                <# _.each( data.showAttachmentFields, function( field ) { #>
     422                        <div class="data-field data-{{ field }}"><#
     423                                if ( 'uploadedTo' === field ) {
     424                                        if ( data[field] ) {
     425                                        #><?php _e( 'Uploaded To:' ) ?><#
     426                                        } else {
     427                                        #><?php _e( 'Unattached' ) ?><#
     428                                        }
     429                                } else if ( 'title' === field && ! data[ field ] ) {
     430                                #><?php _e( '(No title)' ) ?><#
     431                                }
     432
     433                                if ( data[ field ] ) {
     434                                        #>{{ data[ field ] }}<#
     435                                }
     436                        #></div>
     437                <# }); #>
     438                </div>
    285439                <# } #>
     440
    286441        </script>
    287442
    288443        <script type="text/html" id="tmpl-attachment-details">