From ef0774026b3a2567a48656787ddf4f01164b8903 Mon Sep 17 00:00:00 2001 From: Daryl Koopersmith Date: Thu, 8 Nov 2012 14:15:09 +0000 Subject: [PATCH] Media: Improve sidebar handling and make attachment display properties attachment-specific. `wp.mce.media` * Watch all workflows for the `insert` event and attempt to insert the current state's `selection` if it exists. * Fetch and pass attachment display properties through to `wp.media.string.image()`. `wp.media.controller.Region` * Separate the concept of events and modes. * All events triggered on a `Region` trigger both `event` and `event:mode` callbacks. * When a mode is deactivated, `deactivate` and `deactivate:mode` events are fired. * When a mode is activated, `activate` and `activate:mode` events are fired. `wp.media.controller.Library` * Remove the `details()`, `buildDetails()`, and `clearDetails()` methods that juggled sidebar views. Instead, handle the sidebar views using modes. `wp.media.controller.Gallery` * Shift the overloaded `sidebar()` method to use modes. `wp.media.view.MediaFrame.Post` * Declare `activate:mode` event bindings using an nested object to reduce repetition. * Update sidebar activation callbacks. `wp.media.view.Settings` * Refactor to leverage HTML data attributes and implicit values (instead of setting the fallback whenever an object was created). This has the additional benefit that gallery shortcode parameters are not output when the user has left them set to the default. see #21390. git-svn-id: http://core.svn.wordpress.org/trunk@22466 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/js/media-upload.js | 16 +- wp-includes/css/media-views.css | 4 + wp-includes/js/media-views.js | 330 ++++++++++++++++---------------- wp-includes/media.php | 29 ++- 4 files changed, 207 insertions(+), 172 deletions(-) diff --git a/wp-admin/js/media-upload.js b/wp-admin/js/media-upload.js index 4b7188bb8a..9801477951 100644 --- a/wp-admin/js/media-upload.js +++ b/wp-admin/js/media-upload.js @@ -107,10 +107,22 @@ var tb_position; multiple: true } ) ); - workflow.get('library').on( 'insert', function( selection ) { + workflow.on( 'insert', function() { + var state = workflow.state(), + selection = state.get('selection'), + details = state.get('details'); + + if ( ! selection || ! details ) + return; + this.insert( selection.map( function( attachment ) { + var detail = details[ attachment.cid ]; + + if ( detail ) + detail = detail.toJSON(); + if ( 'image' === attachment.get('type') ) - return wp.media.string.image( attachment ) + ' '; + return wp.media.string.image( attachment, detail ) + ' '; else return wp.media.string.link( attachment ) + ' '; }).join('') ); diff --git a/wp-includes/css/media-views.css b/wp-includes/css/media-views.css index d6d9e058dd..b3ecc14f7a 100644 --- a/wp-includes/css/media-views.css +++ b/wp-includes/css/media-views.css @@ -759,6 +759,10 @@ * Attachment Details */ +.attachment-details { + overflow: auto; +} + .attachment-details-preview { cursor: default; } diff --git a/wp-includes/js/media-views.js b/wp-includes/js/media-views.js index 5cb2977b15..0ff3493d58 100644 --- a/wp-includes/js/media-views.js +++ b/wp-includes/js/media-views.js @@ -58,7 +58,7 @@ media.controller.Region = function( options ) { _.extend( this, _.pick( options || {}, 'id', 'controller' ) ); - this.on( 'empty', this.empty, this ); + this.on( 'activate:empty', this.empty, this ); this.mode('empty'); }; @@ -66,14 +66,26 @@ media.controller.Region.extend = Backbone.Model.extend; _.extend( media.controller.Region.prototype, Backbone.Events, { - trigger: function( id ) { - this._mode = id; - return Backbone.Events.trigger.apply( this, arguments ); - }, + trigger: (function() { + var eventSplitter = /\s+/, + trigger = Backbone.Events.trigger; + + return function( events ) { + var mode = ':' + this._mode, + modeEvents = events.split( eventSplitter ).join( mode ) + mode; + + trigger.apply( this, arguments ); + trigger.apply( this, [ modeEvents ].concat( _.rest( arguments ) ) ); + return this; + }; + }()), mode: function( mode ) { - if ( mode ) - return this.trigger( mode ); + if ( mode ) { + this.trigger('deactivate'); + this._mode = mode; + return this.trigger('activate'); + } return this._mode; }, @@ -270,6 +282,9 @@ if ( ! this.get('gutter') ) this.set( 'gutter', 8 ); + if ( ! this.get('details') ) + this.set( 'details', [] ); + media.controller.State.prototype.initialize.apply( this, arguments ); }, @@ -281,8 +296,7 @@ if ( this.get('multiple') ) wp.Uploader.queue.on( 'add', this.selectUpload, this ); - selection.on( 'selection:single', this.buildDetails, this ); - selection.on( 'selection:unsingle', this.clearDetails, this ); + selection.on( 'selection:single selection:unsingle', this.sidebar, this ); selection.on( 'add remove reset', this.refreshToolbar, this ); this._updateEmpty(); @@ -306,8 +320,12 @@ }, sidebar: function() { - media.controller.State.prototype.sidebar.apply( this, arguments ); - this.details(); + var sidebar = this.frame.sidebar; + + if ( this.get('selection').single() ) + sidebar.mode( this.get('sidebar') ); + else + sidebar.mode('clear'); }, content: function() { @@ -344,31 +362,6 @@ this.get('selection').add( attachment ); }, - details: function() { - var single = this.get('selection').single(); - this[ single ? 'buildDetails' : 'clearDetails' ]( single ); - }, - - buildDetails: function( model ) { - var frame = this.frame; - frame.sidebar.view().add( 'details', new media.view.Attachment.Details({ - controller: frame, - model: model, - priority: 80 - }).render() ); - return this; - }, - - clearDetails: function( model ) { - if ( this.get('selection').single() ) - return this; - - this.frame.sidebar.view().add( 'details', new Backbone.View({ - priority: 80 - }).render() ); - return this; - }, - toggleSelection: function( model ) { var selection = this.get('selection'); @@ -436,18 +429,9 @@ }, sidebar: function() { - var frame = this.frame; - - media.controller.State.prototype.sidebar.apply( this, arguments ); - this.details(); - - frame.sidebar.view().add({ - settings: new media.view.Settings.Gallery({ - controller: frame, - model: this.get('library').props, - priority: 40 - }).render() - }); + media.controller.Library.prototype.sidebar.apply( this, arguments ); + this.frame.sidebar.trigger('gallery-settings'); + return this; } }); @@ -590,23 +574,42 @@ }, bindHandlers: function() { - this.menu.on( 'main', this.mainMenu, this ); - this.menu.on( 'batch', this.batchMenu, this ); - this.menu.on( 'gallery', this.galleryMenu, this ); + var handlers = { + menu: { + main: 'mainMenu', + batch: 'batchMenu', + gallery: 'galleryMenu' + }, - this.content.on( 'browse', this.browseContent, this ); - this.content.on( 'upload', this.uploadContent, this ); - this.content.on( 'embed', this.embedContent, this ); + content: { + browse: 'browseContent', + upload: 'uploadContent', + embed: 'embedContent' + }, - this.sidebar.on( 'settings', this.settingsSidebar, this ); - this.sidebar.on( 'attachment-settings', this.attachmentSettingsSidebar, this ); + sidebar: { + 'clear': 'clearSidebar', + 'settings': 'settingsSidebar', + 'attachment-settings': 'attachmentSettingsSidebar' + }, - this.toolbar.on( 'main-attachments', this.mainAttachmentsToolbar, this ); - this.toolbar.on( 'main-embed', this.mainEmbedToolbar, this ); - this.toolbar.on( 'batch-edit', this.batchEditToolbar, this ); - this.toolbar.on( 'batch-add', this.batchAddToolbar, this ); - this.toolbar.on( 'gallery-edit', this.galleryEditToolbar, this ); - this.toolbar.on( 'gallery-add', this.galleryAddToolbar, this ); + toolbar: { + 'main-attachments': 'mainAttachmentsToolbar', + 'main-embed': 'mainEmbedToolbar', + 'batch-edit': 'batchEditToolbar', + 'batch-add': 'batchAddToolbar', + 'gallery-edit': 'galleryEditToolbar', + 'gallery-add': 'galleryAddToolbar' + } + }; + + _.each( handlers, function( regionHandlers, region ) { + _.each( regionHandlers, function( callback, handler ) { + this[ region ].on( 'activate:' + handler, this[ callback ], this ); + }, this ); + }, this ); + + this.sidebar.on( 'gallery-settings', this.onSidebarGallerySettings, this ); }, createSelection: function() { @@ -621,43 +624,47 @@ }, createStates: function() { - var options = this.options; + var options = this.options, + main, gallery; + + main = { + multiple: this.options.multiple, + menu: 'main', + sidebar: 'attachment-settings', + + // Update user settings when users adjust the + // attachment display settings. + displayUserSettings: true + }; + + gallery = { + multiple: true, + menu: 'gallery', + toolbar: 'gallery-add' + }; // Add the default states. this.states.add([ - new media.controller.Library({ + new media.controller.Library( _.defaults({ selection: options.selection, - library: media.query( options.library ), - multiple: this.options.multiple, - menu: 'main', - sidebar: 'attachment-settings' - }), + library: media.query( options.library ) + }, main ) ), - new media.controller.Upload({ - multiple: this.options.multiple, - menu: 'main', - sidebar: 'attachment-settings' - }), + new media.controller.Upload( main ), new media.controller.Gallery({ editing: options.editing, menu: 'gallery' }), - new media.controller.Library({ - id: 'gallery-library', - library: media.query({ type: 'image' }), - multiple: true, - menu: 'gallery', - toolbar: 'gallery-add' - }), + new media.controller.Library( _.defaults({ + id: 'gallery-library', + library: media.query({ type: 'image' }) + }, gallery ) ), - new media.controller.Upload({ - id: 'gallery-upload', - multiple: true, - menu: 'gallery', - toolbar: 'gallery-add' - }) + new media.controller.Upload( _.defaults({ + id: 'gallery-upload' + }, gallery ) ) ]); this.get('gallery-edit').on( 'change:library', this.updateGalleryLibraries, this ).set({ @@ -813,24 +820,60 @@ embedContent: function() {}, // Sidebars - settingsSidebar: function() { + clearSidebar: function() { this.sidebar.view( new media.view.Sidebar({ controller: this }) ); }, - attachmentSettingsSidebar: function() { + settingsSidebar: function( options ) { this.sidebar.view( new media.view.Sidebar({ controller: this, + silent: options && options.silent, + views: { - settings: new media.view.Settings.AttachmentDisplay({ + details: new media.view.Attachment.Details({ controller: this, - priority: 20 + model: this.state().get('selection').single(), + priority: 80 }).render() } }) ); }, + onSidebarGallerySettings: function( options ) { + this.sidebar.view().add({ + gallery: new media.view.Settings.Gallery({ + controller: this, + model: this.state().get('library').props, + priority: 40 + }).render() + }, options ); + }, + + attachmentSettingsSidebar: function( options ) { + var state = this.state(), + display = state.get('details'), + single = state.get('selection').single().cid; + + this.settingsSidebar({ silent: true }); + + display[ single ] = display[ single ] || new Backbone.Model({ + align: getUserSetting( 'align', 'none' ), + size: getUserSetting( 'imgsize', 'medium' ), + link: getUserSetting( 'urlbutton', 'post' ) + }); + + this.sidebar.view().add({ + display: new media.view.Settings.AttachmentDisplay({ + controller: this, + model: display[ single ], + priority: 100, + userSettings: state.get('displayUserSettings') + }).render() + }, options ); + }, + // Toolbars mainAttachmentsToolbar: function() { this.toolbar.view( new media.view.Toolbar.Insert.Post({ @@ -2125,44 +2168,15 @@ 'change textarea': 'updateHandler' }, - settings: {}, - initialize: function() { - var settings = this.settings; - this.model = this.model || new Backbone.Model(); - - _.each( settings, function( setting, key ) { - if ( setting.name ) - this.model.set( key, getUserSetting( setting.name, setting.fallback ) ); - else - this.model.set( key, this.model.get( key ) || setting.fallback ); - }, this ); - - this.model.validate = function( attrs ) { - return _.any( attrs, function( value, key ) { - // If we don't have a `setting` for the `key`, assume the - // `value` is valid. Otherwise, check if the `value` exists - // in the `setting.accepts` array. - return settings[ key ] && ! _.contains( settings[ key ].accepts, value ); - }); - }; - - this.model.on( 'change', function( model, options ) { - if ( ! options.changes ) - return; - - _.each( _.keys( options.changes ), function( key ) { - if ( settings[ key ] && settings[ key ].name ) - setUserSetting( settings[ key ].name, model.get( key ) ); - }); - }, this ); - this.model.on( 'change', this.updateChanges, this ); }, render: function() { - this.$el.html( this.template( this.model.toJSON() ) ); + this.$el.html( this.template( _.defaults({ + model: this.model.toJSON() + }, this.options ) ) ); // Select the correct values. _( this.model.attributes ).chain().keys().each( this.update, this ); @@ -2170,28 +2184,44 @@ }, update: function( key ) { - var setting = this.settings[ key ], + var value = this.model.get( key ), $setting = this.$('[data-setting="' + key + '"]'), $buttons; - if ( ! setting ) + // Bail if we didn't find a matching setting. + if ( ! $setting.length ) return; - if ( 'select' === setting.type ) { - $setting.find('[value="' + this.model.get( key ) + '"]').attr( 'selected', true ); - } else { + // Attempt to determine how the setting is rendered and update + // the selected value. + + // Handle dropdowns. + if ( $setting.is('select') ) { + $setting.find('[value="' + value + '"]').attr( 'selected', true ); + + // Handle button groups. + } else if ( $setting.hasClass('button-group') ) { $buttons = $setting.find('button').removeClass('active'); - $buttons.filter( '[value="' + this.model.get( key ) + '"]' ).addClass('active'); + $buttons.filter( '[value="' + value + '"]' ).addClass('active'); } }, updateHandler: function( event ) { - var $setting = $( event.target ).closest('[data-setting]'); + var $setting = $( event.target ).closest('[data-setting]'), + value = event.target.value, + userSetting; event.preventDefault(); - if ( $setting.length ) - this.model.set( $setting.data('setting'), event.target.value ); + if ( ! $setting.length ) + return; + + this.model.set( $setting.data('setting'), value ); + + // If the setting has a corresponding user setting, + // update that as well. + if ( userSetting = $setting.data('userSetting') ) + setUserSetting( userSetting, value ); }, updateChanges: function( model, options ) { @@ -2207,23 +2237,11 @@ className: 'attachment-display-settings', template: media.template('attachment-display-settings'), - settings: { - align: { - accepts: ['left','center','right','none'], - name: 'align', - fallback: 'none' - }, - link: { - accepts: ['post','file','none'], - name: 'urlbutton', - fallback: 'post' - }, - size: { - // @todo: Dynamically generate these. - accepts: ['thumbnail','medium','large','full'], - name: 'imgsize', - fallback: 'medium' - } + initialize: function() { + _.defaults( this.options, { + userSettings: false + }); + media.view.Settings.prototype.initialize.apply( this, arguments ); } }); @@ -2232,19 +2250,7 @@ */ media.view.Settings.Gallery = media.view.Settings.extend({ className: 'gallery-settings', - template: media.template('gallery-settings'), - - settings: { - columns: { - accepts: _.invoke( _.range( 1, 10 ), 'toString' ), - fallback: '3', - type: 'select' - }, - link: { - accepts: ['post','file'], - fallback: 'post' - } - } + template: media.template('gallery-settings') }); /** diff --git a/wp-includes/media.php b/wp-includes/media.php index f3c6131f1c..301285f233 100644 --- a/wp-includes/media.php +++ b/wp-includes/media.php @@ -1417,7 +1417,12 @@ function wp_print_media_templates( $attachment ) {

-
+
+ data-user-setting="align" + <# } #>> + @@ -1427,14 +1432,19 @@ function wp_print_media_templates( $attachment ) { -

-