WordPress.org

Make WordPress Core

Changeset 28680


Ignore:
Timestamp:
06/05/2014 05:26:24 PM (7 years ago)
Author:
wonderboymusic
Message:

Simplify the creation of MCE views somewhat:

  • Rename the wp.mce.media mixin (which was named too ambiguously) to wp.mce.av.
  • Use the same technique for extending mixins for MCE base classes for views and their base.View property class
  • wp.mce.views.register() should have default constructor logic if one is not passed.

Props avryl.
See #28458.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/js/mce-view.js

    r28594 r28680  
    2323     * A Backbone-like View constructor intended for use when rendering a TinyMCE View. The main difference is
    2424     * that the TinyMCE View is not tied to a particular DOM node.
     25     *
     26     * @param {Object} [options={}]
    2527     */
    2628    wp.mce.View = function( options ) {
    27         options || (options = {});
    28         _.extend(this, _.pick(options, viewOptions));
    29         this.initialize.apply(this, arguments);
     29        options = options || {};
     30        _.extend( this, _.pick( options, viewOptions ) );
     31        this.initialize.apply( this, arguments );
    3032    };
    3133
     
    3739            // Search all tinymce editor instances and update the placeholders
    3840            _.each( tinymce.editors, function( editor ) {
    39                 var doc, self = this;
     41                var self = this;
    4042                if ( editor.plugins.wpview ) {
    41                     doc = editor.getDoc();
    42                     $( doc ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each(function (i, elem) {
    43                         var node = $( elem );
     43                    $( editor.getDoc() ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each( function ( i, element ) {
     44                        var node = $( element );
    4445                        // The <ins> is used to mark the end of the wrapper div. Needed when comparing
    4546                        // the content as string for preventing extra undo levels.
    4647                        node.html( html ).append( '<ins data-wpview-end="1"></ins>' );
    47                         $( self ).trigger( 'ready', elem );
     48                        $( self ).trigger( 'ready', element );
    4849                    });
    4950                }
     
    7576         */
    7677        register: function( type, constructor ) {
     78            var defaultConstructor = {
     79                    type: type,
     80                    View: {},
     81                    toView: function( content ) {
     82                        var match = wp.shortcode.next( this.type, content );
     83
     84                        if ( ! match ) {
     85                            return;
     86                        }
     87
     88                        return {
     89                            index: match.index,
     90                            content: match.content,
     91                            options: {
     92                                shortcode: match.shortcode
     93                            }
     94                        };
     95                    }
     96                };
     97
     98            constructor = _.defaults( constructor, defaultConstructor );
     99            constructor.View = wp.mce.View.extend( constructor.View );
     100
    77101            views[ type ] = constructor;
    78102        },
     
    82106         *
    83107         * Returns a TinyMCE view constructor.
     108         *
     109         * @param type
    84110         */
    85111        get: function( type ) {
     
    91117         *
    92118         * Unregisters a TinyMCE view.
     119         *
     120         * @param type
    93121         */
    94122        unregister: function( type ) {
     
    113141         * every match, which triggers the related data to be fetched.
    114142         *
     143         * @param content
    115144         */
    116145        toViews: function( content ) {
     
    253282    };
    254283
    255     wp.mce.gallery = {
    256         shortcode: 'gallery',
    257         toView:  function( content ) {
    258             var match = wp.shortcode.next( this.shortcode, content );
    259 
    260             if ( ! match ) {
    261                 return;
    262             }
    263 
    264             return {
    265                 index:   match.index,
    266                 content: match.content,
    267                 options: {
    268                     shortcode: match.shortcode
    269                 }
    270             };
    271         },
    272         View: wp.mce.View.extend({
    273             className: 'editor-gallery',
    274             template:  media.template('editor-gallery'),
     284    wp.mce.views.register( 'gallery', {
     285        View: {
     286            template: media.template( 'editor-gallery' ),
    275287
    276288            // The fallback post ID to use as a parent for galleries that don't
     
    322334
    323335            }
    324         }),
     336        },
    325337
    326338        edit: function( node ) {
     
    339351            });
    340352        }
    341 
    342     };
    343     wp.mce.views.register( 'gallery', wp.mce.gallery );
     353    } );
    344354
    345355    /**
    346      * Tiny MCE Views for Audio / Video
    347      *
    348      */
    349 
    350     /**
    351      * These are base methods that are shared by each shortcode's MCE controller
     356     * These are base methods that are shared by the audio and video shortcode's MCE controller.
    352357     *
    353358     * @mixin
    354359     */
    355     wp.mce.media = {
     360    wp.mce.av = {
    356361        loaded: false,
    357         toView: wp.mce.gallery.toView,
     362
     363        View: _.extend( {}, wp.media.mixin, {
     364            initialize: function( options ) {
     365                this.players = [];
     366                this.shortcode = options.shortcode;
     367                _.bindAll( this, 'setPlayer', 'pausePlayers' );
     368                $( this ).on( 'ready', this.setPlayer );
     369                $( 'body' ).on( 'click', '.wp-switch-editor', this.pausePlayers );
     370                $( document ).on( 'media:edit', this.pausePlayers );
     371            },
     372
     373            /**
     374             * Creates the player instance for the current node
     375             *
     376             * @global MediaElementPlayer
     377             *
     378             * @param {Event} e
     379             * @param {HTMLElement} node
     380             */
     381            setPlayer: function(e, node) {
     382                // if the ready event fires on an empty node
     383                if ( ! node ) {
     384                    return;
     385                }
     386
     387                var self = this,
     388                    media,
     389                    firefox = this.ua.is( 'ff' ),
     390                    className = '.wp-' +  this.shortcode.tag + '-shortcode';
     391
     392                media = $( node ).find( className );
     393
     394                if ( ! this.isCompatible( media ) ) {
     395                    media.closest( '.wpview-wrap' ).addClass( 'wont-play' );
     396                    if ( ! media.parent().hasClass( 'wpview-wrap' ) ) {
     397                        media.parent().replaceWith( media );
     398                    }
     399                    media.replaceWith( '<p>' + media.find( 'source' ).eq(0).prop( 'src' ) + '</p>' );
     400                    return;
     401                } else {
     402                    media.closest( '.wpview-wrap' ).removeClass( 'wont-play' );
     403                    if ( firefox ) {
     404                        media.prop( 'preload', 'metadata' );
     405                    } else {
     406                        media.prop( 'preload', 'none' );
     407                    }
     408                }
     409
     410                media = wp.media.view.MediaDetails.prepareSrc( media.get(0) );
     411
     412                setTimeout( function() {
     413                    wp.mce.av.loaded = true;
     414                    self.players.push( new MediaElementPlayer( media, self.mejsSettings ) );
     415                }, wp.mce.av.loaded ? 10 : 500 );
     416            },
     417
     418            /**
     419             * Pass data to the View's Underscore template and return the compiled output
     420             *
     421             * @returns {string}
     422             */
     423            getHtml: function() {
     424                var attrs = this.shortcode.attrs.named;
     425                attrs.content = this.shortcode.content;
     426
     427                return this.template({ model: _.defaults(
     428                    attrs,
     429                    wp.media[ this.shortcode.tag ].defaults )
     430                });
     431            },
     432
     433            unbind: function() {
     434                this.unsetPlayers();
     435            }
     436        } ),
    358437
    359438        /**
     
    398477
    399478    /**
    400      * Base View class for audio and video shortcodes
    401      *
    402      * @constructor
    403      * @augments wp.mce.View
    404      * @mixes wp.media.mixin
    405      */
    406     wp.mce.media.View = wp.mce.View.extend({
    407         initialize: function( options ) {
    408             this.players = [];
    409             this.shortcode = options.shortcode;
    410             _.bindAll( this, 'setPlayer', 'pausePlayers' );
    411             $( this ).on( 'ready', this.setPlayer );
    412             $( 'body' ).on( 'click', '.wp-switch-editor', this.pausePlayers );
    413             $( document ).on( 'media:edit', this.pausePlayers );
    414         },
    415 
    416         /**
    417          * Creates the player instance for the current node
    418          *
    419          * @global MediaElementPlayer
    420          * @global _wpmejsSettings
    421          *
    422          * @param {Event} e
    423          * @param {HTMLElement} node
    424          */
    425         setPlayer: function(e, node) {
    426             // if the ready event fires on an empty node
    427             if ( ! node ) {
    428                 return;
    429             }
    430 
    431             var self = this,
    432                 media,
    433                 firefox = this.ua.is( 'ff' ),
    434                 className = '.wp-' +  this.shortcode.tag + '-shortcode';
    435 
    436             media = $( node ).find( className );
    437 
    438             if ( ! this.isCompatible( media ) ) {
    439                 media.closest( '.wpview-wrap' ).addClass( 'wont-play' );
    440                 if ( ! media.parent().hasClass( 'wpview-wrap' ) ) {
    441                     media.parent().replaceWith( media );
    442                 }
    443                 media.replaceWith( '<p>' + media.find( 'source' ).eq(0).prop( 'src' ) + '</p>' );
    444                 return;
    445             } else {
    446                 media.closest( '.wpview-wrap' ).removeClass( 'wont-play' );
    447                 if ( firefox ) {
    448                     media.prop( 'preload', 'metadata' );
    449                 } else {
    450                     media.prop( 'preload', 'none' );
    451                 }
    452             }
    453 
    454             media = wp.media.view.MediaDetails.prepareSrc( media.get(0) );
    455 
    456             setTimeout( function() {
    457                 wp.mce.media.loaded = true;
    458                 self.players.push( new MediaElementPlayer( media, self.mejsSettings ) );
    459             }, wp.mce.media.loaded ? 10 : 500 );
    460         },
    461 
    462         /**
    463          * Pass data to the View's Underscore template and return the compiled output
    464          *
    465          * @returns {string}
    466          */
    467         getHtml: function() {
    468             var attrs = this.shortcode.attrs.named;
    469             attrs.content = this.shortcode.content;
    470 
    471             return this.template({ model: _.defaults(
    472                 attrs,
    473                 wp.media[ this.shortcode.tag ].defaults )
    474             });
    475         },
    476 
    477         unbind: function() {
    478             this.unsetPlayers();
    479         }
    480     });
    481     _.extend( wp.mce.media.View.prototype, wp.media.mixin );
    482 
    483     /**
    484479     * TinyMCE handler for the video shortcode
    485480     *
    486      * @mixes wp.mce.media
     481     * @mixes wp.mce.av
    487482     */
    488     wp.mce.video = _.extend( {}, wp.mce.media, {
     483    wp.mce.views.register( 'video', _.extend( {}, wp.mce.av, {
    489484        shortcode: 'video',
    490485        state: 'video-details',
    491         View: wp.mce.media.View.extend({
    492             className: 'editor-video',
    493             template:  media.template('editor-video')
    494         })
    495     } );
    496     wp.mce.views.register( 'video', wp.mce.video );
     486        View: _.extend( {}, wp.mce.av.View, {
     487            template: media.template( 'editor-video' )
     488        } )
     489    } ) );
    497490
    498491    /**
    499492     * TinyMCE handler for the audio shortcode
    500493     *
    501      * @mixes wp.mce.media
     494     * @mixes wp.mce.av
    502495     */
    503     wp.mce.audio = _.extend( {}, wp.mce.media, {
     496    wp.mce.views.register( 'audio', _.extend( {}, wp.mce.av, {
    504497        shortcode: 'audio',
    505498        state: 'audio-details',
    506         View: wp.mce.media.View.extend({
    507             className: 'editor-audio',
    508             template:  media.template('editor-audio')
    509         })
    510     } );
    511     wp.mce.views.register( 'audio', wp.mce.audio );
    512 
    513     /**
    514      * Base View class for playlist shortcodes
    515      *
    516      * @constructor
    517      * @augments wp.mce.View
    518      * @mixes wp.media.mixin
    519      */
    520     wp.mce.media.PlaylistView = wp.mce.View.extend({
    521         className: 'editor-playlist',
    522         template:  media.template('editor-playlist'),
    523 
    524         initialize: function( options ) {
    525             this.players = [];
    526             this.data = {};
    527             this.attachments = [];
    528             this.shortcode = options.shortcode;
    529 
    530             $( 'body' ).on( 'click', '.wp-switch-editor', this.pausePlayers );
    531             $( document ).on( 'media:edit', this.pausePlayers );
    532 
    533             this.fetch();
    534         },
    535 
    536         /**
    537          * Asynchronously fetch the shortcode's attachments
    538          */
    539         fetch: function() {
    540             this.attachments = wp.media.playlist.attachments( this.shortcode );
    541             this.dfd = this.attachments.more().done( _.bind( this.render, this ) );
    542         },
    543 
    544         /**
    545          * Get the HTML for the view (which also set's the data), replace the
    546          *   current HTML, and then invoke the WPPlaylistView instance to render
    547          *   the playlist in the editor
    548          *
    549          * @global WPPlaylistView
    550          * @global tinymce.editors
    551          */
    552         render: function() {
    553             var html = this.getHtml(), self = this;
    554 
    555             _.each( tinymce.editors, function( editor ) {
    556                 var doc;
    557                 if ( editor.plugins.wpview ) {
    558                     doc = editor.getDoc();
    559                     $( doc ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each(function (i, elem) {
    560                         var node = $( elem );
    561 
    562                         // The <ins> is used to mark the end of the wrapper div. Needed when comparing
    563                         // the content as string for preventing extra undo levels.
    564                         node.html( html ).append( '<ins data-wpview-end="1"></ins>' );
    565 
    566                         if ( ! self.data.tracks ) {
    567                             return;
    568                         }
    569 
    570                         self.players.push( new WPPlaylistView({
    571                             el: $( elem ).find( '.wp-playlist' ).get(0),
    572                             metadata: self.data
    573                         }).player );
    574                     });
    575                 }
    576             }, this );
    577         },
    578 
    579         /**
    580          * Set the data that will be used to compile the Underscore template,
    581          *  compile the template, and then return it.
    582          *
    583          * @returns {string}
    584          */
    585         getHtml: function() {
    586             var data = this.shortcode.attrs.named,
    587                 model = wp.media.playlist,
    588                 options,
    589                 attachments,
    590                 tracks = [];
    591 
    592             // Don't render errors while still fetching attachments
    593             if ( this.dfd && 'pending' === this.dfd.state() && ! this.attachments.length ) {
    594                 return;
    595             }
    596 
    597             _.each( model.defaults, function( value, key ) {
    598                 data[ key ] = model.coerce( data, key );
    599             });
    600 
    601             options = {
    602                 type: data.type,
    603                 style: data.style,
    604                 tracklist: data.tracklist,
    605                 tracknumbers: data.tracknumbers,
    606                 images: data.images,
    607                 artists: data.artists
    608             };
    609 
    610             if ( ! this.attachments.length ) {
    611                 return this.template( options );
    612             }
    613 
    614             attachments = this.attachments.toJSON();
    615 
    616             _.each( attachments, function( attachment ) {
    617                 var size = {}, resize = {}, track = {
    618                     src : attachment.url,
    619                     type : attachment.mime,
    620                     title : attachment.title,
    621                     caption : attachment.caption,
    622                     description : attachment.description,
    623                     meta : attachment.meta
    624                 };
    625 
    626                 if ( 'video' === data.type ) {
    627                     size.width = attachment.width;
    628                     size.height = attachment.height;
    629                     if ( media.view.settings.contentWidth ) {
    630                         resize.width = media.view.settings.contentWidth - 22;
    631                         resize.height = Math.ceil( ( size.height * resize.width ) / size.width );
    632                         if ( ! options.width ) {
    633                             options.width = resize.width;
    634                             options.height = resize.height;
    635                         }
    636                     } else {
    637                         if ( ! options.width ) {
    638                             options.width = attachment.width;
    639                             options.height = attachment.height;
    640                         }
    641                     }
    642                     track.dimensions = {
    643                         original : size,
    644                         resized : _.isEmpty( resize ) ? size : resize
    645                     };
    646                 } else {
    647                     options.width = 400;
    648                 }
    649 
    650                 track.image = attachment.image;
    651                 track.thumb = attachment.thumb;
    652 
    653                 tracks.push( track );
    654             } );
    655 
    656             options.tracks = tracks;
    657             this.data = options;
    658 
    659             return this.template( options );
    660         },
    661 
    662         unbind: function() {
    663             this.unsetPlayers();
    664         }
    665     });
    666     _.extend( wp.mce.media.PlaylistView.prototype, wp.media.mixin );
     499        View: _.extend( {}, wp.mce.av.View, {
     500            template: media.template( 'editor-audio' )
     501        } )
     502    } ) );
    667503
    668504    /**
    669505     * TinyMCE handler for the playlist shortcode
    670506     *
    671      * @mixes wp.mce.media
     507     * @mixes wp.mce.av
    672508     */
    673     wp.mce.playlist = _.extend( {}, wp.mce.media, {
     509    wp.mce.views.register( 'playlist', _.extend( {}, wp.mce.av, {
    674510        shortcode: 'playlist',
    675511        state: ['playlist-edit', 'video-playlist-edit'],
    676         View: wp.mce.media.PlaylistView
    677     } );
    678     wp.mce.views.register( 'playlist', wp.mce.playlist );
     512        View: _.extend( {}, wp.media.mixin, {
     513            template:  media.template( 'editor-playlist' ),
     514
     515            initialize: function( options ) {
     516                this.players = [];
     517                this.data = {};
     518                this.attachments = [];
     519                this.shortcode = options.shortcode;
     520
     521                $( 'body' ).on( 'click', '.wp-switch-editor', this.pausePlayers );
     522                $( document ).on( 'media:edit', this.pausePlayers );
     523
     524                this.fetch();
     525
     526                $( this ).on( 'ready', this.setPlaylist );
     527            },
     528
     529            /**
     530             * Asynchronously fetch the shortcode's attachments
     531             */
     532            fetch: function() {
     533                this.attachments = wp.media.playlist.attachments( this.shortcode );
     534                this.dfd = this.attachments.more().done( _.bind( this.render, this ) );
     535            },
     536
     537            setPlaylist: function( event, element ) {
     538                if ( ! this.data.tracks ) {
     539                    return;
     540                }
     541
     542                this.players.push( new WPPlaylistView( {
     543                    el: $( element ).find( '.wp-playlist' ).get( 0 ),
     544                    metadata: this.data
     545                } ).player );
     546            },
     547
     548            /**
     549             * Set the data that will be used to compile the Underscore template,
     550             *  compile the template, and then return it.
     551             *
     552             * @returns {string}
     553             */
     554            getHtml: function() {
     555                var data = this.shortcode.attrs.named,
     556                    model = wp.media.playlist,
     557                    options,
     558                    attachments,
     559                    tracks = [];
     560
     561                // Don't render errors while still fetching attachments
     562                if ( this.dfd && 'pending' === this.dfd.state() && ! this.attachments.length ) {
     563                    return;
     564                }
     565
     566                _.each( model.defaults, function( value, key ) {
     567                    data[ key ] = model.coerce( data, key );
     568                });
     569
     570                options = {
     571                    type: data.type,
     572                    style: data.style,
     573                    tracklist: data.tracklist,
     574                    tracknumbers: data.tracknumbers,
     575                    images: data.images,
     576                    artists: data.artists
     577                };
     578
     579                if ( ! this.attachments.length ) {
     580                    return this.template( options );
     581                }
     582
     583                attachments = this.attachments.toJSON();
     584
     585                _.each( attachments, function( attachment ) {
     586                    var size = {}, resize = {}, track = {
     587                        src : attachment.url,
     588                        type : attachment.mime,
     589                        title : attachment.title,
     590                        caption : attachment.caption,
     591                        description : attachment.description,
     592                        meta : attachment.meta
     593                    };
     594
     595                    if ( 'video' === data.type ) {
     596                        size.width = attachment.width;
     597                        size.height = attachment.height;
     598                        if ( media.view.settings.contentWidth ) {
     599                            resize.width = media.view.settings.contentWidth - 22;
     600                            resize.height = Math.ceil( ( size.height * resize.width ) / size.width );
     601                            if ( ! options.width ) {
     602                                options.width = resize.width;
     603                                options.height = resize.height;
     604                            }
     605                        } else {
     606                            if ( ! options.width ) {
     607                                options.width = attachment.width;
     608                                options.height = attachment.height;
     609                            }
     610                        }
     611                        track.dimensions = {
     612                            original : size,
     613                            resized : _.isEmpty( resize ) ? size : resize
     614                        };
     615                    } else {
     616                        options.width = 400;
     617                    }
     618
     619                    track.image = attachment.image;
     620                    track.thumb = attachment.thumb;
     621
     622                    tracks.push( track );
     623                } );
     624
     625                options.tracks = tracks;
     626                this.data = options;
     627
     628                return this.template( options );
     629            },
     630
     631            unbind: function() {
     632                this.unsetPlayers();
     633            }
     634        } )
     635    } ) );
    679636
    680637    /**
    681638     * TinyMCE handler for the embed shortcode
    682639     */
    683     wp.mce.embed = {
     640    wp.mce.views.register( 'embed', {
    684641        shortcode: 'embed',
    685         toView: wp.mce.gallery.toView,
    686         View: wp.mce.View.extend( {
    687             className: 'editor-embed',
     642        View: _.extend( {}, wp.media.mixin, {
    688643            template: media.template( 'editor-embed' ),
    689644            initialize: function( options ) {
     
    738693        } ),
    739694        edit: function() {}
    740     };
    741 
    742     _.extend( wp.mce.embed.View.prototype, wp.media.mixin );
    743 
    744     wp.mce.views.register( 'embed', wp.mce.embed );
     695    } );
    745696
    746697}(jQuery));
Note: See TracChangeset for help on using the changeset viewer.