WordPress.org

Make WordPress Core

Changeset 28993


Ignore:
Timestamp:
07/04/2014 03:38:33 AM (5 years ago)
Author:
helen
Message:

Media grid, round 2. Expect much more to come.

  • Instead of a sidebar for details, utilize a modal. The modal experience allows for a larger preview, editing images, audio/video previews, and previous/next navigation, like the theme browser. Think of it as an attachment browser.
  • Show some details in the grid view to more easily distinguish items.

props ericlewis, wonderboymusic, JerrySarcastic. see #24716.

Location:
trunk/src
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/upload.php

    r28992 r28993  
    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;
  • trunk/src/wp-includes/css/media-views.css

    r28732 r28993  
    749749    max-width: 100%;
    750750    max-height: 100%;
     751}
     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;
    751775}
    752776
     
    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
     
    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,
     
    941985    overflow: auto;
    942986    outline: none;
     987}
     988
     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;
    9431073}
    9441074
     
    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
     
    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}
  • trunk/src/wp-includes/js/media-audiovideo.js

    r28985 r28993  
    2323                for ( p in window.mejs.players ) {
    2424                    window.mejs.players[p].pause();
     25                }
     26            }
     27        },
     28
     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] );
    2536                }
    2637            }
  • trunk/src/wp-includes/js/media-grid.js

    r28992 r28993  
    1 (function( $, _, Backbone, wp ) {
     1(function($, _, Backbone, wp) {
    22    var media = wp.media, l10n;
    33
     
    99        delete l10n.settings;
    1010    }
     11
     12    /**
     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    });
    11101
    12102    /**
     
    37127                multiple:  false,
    38128                state:     'library',
    39                 uploader:  true
     129                uploader:  true,
     130                mode:      [ 'grid', 'edit' ]
    40131            });
    41132
     
    112203                    content:    'browse',
    113204                    filterable: 'mime-types'
    114                 }),
    115 
    116                 new media.controller.EditImage( { model: options.editImage } )
     205                })
    117206            ]);
    118207        },
     
    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 );
     217        },
     218
     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
     231        /**
     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            });
    123255        },
    124256
     
    144276                dragInfo:   state.get('dragInfo'),
    145277                bulkEdit:   true,
     278                sidebar:    false,
    146279
    147280                suggestedWidth:  state.get('suggestedWidth'),
     
    163296        }
    164297    });
    165    
    166 }( jQuery, _, Backbone, wp ));
     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));
  • trunk/src/wp-includes/js/media-views.js

    r28992 r28993  
    338338
    339339    /**
     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.
     343     */
     344    media.controller._State = Backbone.Model.extend({
     345        constructor: function() {
     346            this.on( 'activate', this._preActivate, this );
     347            this.on( 'activate', this.activate, this );
     348            this.on( 'activate', this._postActivate, this );
     349            this.on( 'deactivate', this._deactivate, this );
     350            this.on( 'deactivate', this.deactivate, this );
     351            this.on( 'reset', this.reset, this );
     352            this.on( 'ready', this.ready, this );
     353            /**
     354             * Call parent constructor with passed arguments
     355             */
     356            Backbone.Model.apply( this, arguments );
     357        },
     358
     359        /**
     360         * @abstract
     361         */
     362        ready: function() {},
     363        /**
     364         * @abstract
     365         */
     366        activate: function() {},
     367        /**
     368         * @abstract
     369         */
     370        deactivate: function() {},
     371        /**
     372         * @abstract
     373         */
     374        reset: function() {},
     375        /**
     376         * @access private
     377         */
     378        _preActivate: function() {
     379            this.active = true;
     380        },
     381        /**
     382         * @access private
     383         */
     384        _postActivate: function() {},
     385        /**
     386         * @access private
     387         */
     388        _deactivate: function() {
     389            this.active = false;
     390        }
     391    });
     392
     393    /**
    340394     * wp.media.controller.State
    341395     *
     
    347401     * @augments Backbone.Model
    348402     */
    349     media.controller.State = Backbone.Model.extend({
     403    media.controller.State = media.controller._State.extend({
    350404        constructor: function() {
    351405            this.on( 'activate', this._preActivate, this );
     
    365419
    366420        /**
    367          * @abstract
    368          */
    369         ready: function() {},
    370         /**
    371          * @abstract
    372          */
    373         activate: function() {},
    374         /**
    375          * @abstract
    376          */
    377         deactivate: function() {},
    378         /**
    379          * @abstract
    380          */
    381         reset: function() {},
    382         /**
    383421         * @access private
    384422         */
     
    386424            this._updateMenu();
    387425        },
    388         /**
    389          * @access private
    390          */
    391         _preActivate: function() {
    392             this.active = true;
    393         },
     426
    394427        /**
    395428         * @access private
     
    17591792                title:    '',
    17601793                modal:    true,
    1761                 uploader: true
     1794                uploader: true,
     1795                mode:     ['select']
    17621796            });
    17631797
     
    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 );
     
    45844618                    alt:           '',
    45854619                    description:   ''
    4586                 });
     4620                }, this.options );
    45874621
    45884622            options.buttons  = this.buttons;
     
    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';
     
    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
     
    54695510    });
    54705511
    5471 
    54725512    /**
    54735513     * wp.media.view.AttachmentsBrowser
     
    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            });
     
    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 );
     
    55175562
    55185563            this.views.add( this.toolbar );
     5564
     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            }
    55195578
    55205579            filters = this.options.filters;
     
    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.
  • trunk/src/wp-includes/media-template.php

    r28807 r28993  
    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>
     
    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 ) { #>
     
    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">
     
    258391                </div>
    259392            <# } #>
    260 
    261393            <# if ( data.buttons.close ) { #>
    262394                <a class="close media-modal-icon" href="#" title="<?php esc_attr_e('Remove'); ?>"></a>
     
    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 }} />
     
    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
Note: See TracChangeset for help on using the changeset viewer.