var Frame = wp.media.view.Frame,
l10n = wp.media.view.l10n,
$ = jQuery,
MediaFrame;
/**
* wp.media.view.MediaFrame
*
* The frame used to create the media modal.
*
* @memberOf wp.media.view
*
* @class
* @augments wp.media.view.Frame
* @augments wp.media.View
* @augments wp.Backbone.View
* @augments Backbone.View
* @mixes wp.media.controller.StateMachine
*/
MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{
className: 'media-frame',
template: wp.template('media-frame'),
regions: ['menu','title','content','toolbar','router'],
events: {
'click .media-frame-menu-toggle': 'toggleMenu'
},
/**
* @constructs
*/
initialize: function() {
Frame.prototype.initialize.apply( this, arguments );
_.defaults( this.options, {
title: l10n.mediaFrameDefaultTitle,
modal: true,
uploader: true
});
// Ensure core UI is enabled.
this.$el.addClass('wp-core-ui');
// Initialize modal container view.
if ( this.options.modal ) {
this.modal = new wp.media.view.Modal({
controller: this,
title: this.options.title
});
this.modal.content( this );
}
// Force the uploader off if the upload limit has been exceeded or
// if the browser isn't supported.
if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
this.options.uploader = false;
}
// Initialize window-wide uploader.
if ( this.options.uploader ) {
this.uploader = new wp.media.view.UploaderWindow({
controller: this,
uploader: {
dropzone: this.modal ? this.modal.$el : this.$el,
container: this.$el
}
});
this.views.set( '.media-frame-uploader', this.uploader );
}
this.on( 'attach', _.bind( this.views.ready, this.views ), this );
// Bind default title creation.
this.on( 'title:create:default', this.createTitle, this );
this.title.mode('default');
// Bind default menu.
this.on( 'menu:create:default', this.createMenu, this );
// Set the menu ARIA tab panel attributes when the modal opens.
this.on( 'open', this.setMenuTabPanelAriaAttributes, this );
// Set the router ARIA tab panel attributes when the modal opens.
this.on( 'open', this.setRouterTabPanelAriaAttributes, this );
// Update the menu ARIA tab panel attributes when the content updates.
this.on( 'content:render', this.setMenuTabPanelAriaAttributes, this );
// Update the router ARIA tab panel attributes when the content updates.
this.on( 'content:render', this.setRouterTabPanelAriaAttributes, this );
},
/**
* Sets the attributes to be used on the menu ARIA tab panel.
*
* @since 5.3.0
*
* @return {void}
*/
setMenuTabPanelAriaAttributes: function() {
var stateId = this.state().get( 'id' ),
tabPanelEl = this.$el.find( '.media-frame-tab-panel' ),
ariaLabelledby;
tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
if ( this.state().get( 'menu' ) && this.menuView && this.menuView.isVisible ) {
ariaLabelledby = 'menu-item-' + stateId;
// Set the tab panel attributes only if the tabs are visible.
tabPanelEl
.attr( {
role: 'tabpanel',
'aria-labelledby': ariaLabelledby,
tabIndex: '0'
} );
}
},
/**
* Sets the attributes to be used on the router ARIA tab panel.
*
* @since 5.3.0
*
* @return {void}
*/
setRouterTabPanelAriaAttributes: function() {
var tabPanelEl = this.$el.find( '.media-frame-content' ),
ariaLabelledby;
tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
// Set the tab panel attributes only if the tabs are visible.
if ( this.state().get( 'router' ) && this.routerView && this.routerView.isVisible && this.content._mode ) {
ariaLabelledby = 'menu-item-' + this.content._mode;
tabPanelEl
.attr( {
role: 'tabpanel',
'aria-labelledby': ariaLabelledby,
tabIndex: '0'
} );
}
},
/**
* @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
*/
render: function() {
// Activate the default state if no active state exists.
if ( ! this.state() && this.options.state ) {
this.setState( this.options.state );
}
/**
* call 'render' directly on the parent class
*/
return Frame.prototype.render.apply( this, arguments );
},
/**
* @param {Object} title
* @this wp.media.controller.Region
*/
createTitle: function( title ) {
title.view = new wp.media.View({
controller: this,
tagName: 'h1'
});
},
/**
* @param {Object} menu
* @this wp.media.controller.Region
*/
createMenu: function( menu ) {
menu.view = new wp.media.view.Menu({
controller: this,
attributes: {
role: 'tablist',
'aria-orientation': 'vertical'
}
});
this.menuView = menu.view;
},
toggleMenu: function( event ) {
var menu = this.$el.find( '.media-menu' );
menu.toggleClass( 'visible' );
$( event.target ).attr( 'aria-expanded', menu.hasClass( 'visible' ) );
},
/**
* @param {Object} toolbar
* @this wp.media.controller.Region
*/
createToolbar: function( toolbar ) {
toolbar.view = new wp.media.view.Toolbar({
controller: this
});
},
/**
* @param {Object} router
* @this wp.media.controller.Region
*/
createRouter: function( router ) {
router.view = new wp.media.view.Router({
controller: this,
attributes: {
role: 'tablist',
'aria-orientation': 'horizontal'
}
});
this.routerView = router.view;
},
/**
* @param {Object} options
*/
createIframeStates: function( options ) {
var settings = wp.media.view.settings,
tabs = settings.tabs,
tabUrl = settings.tabUrl,
$postId;
if ( ! tabs || ! tabUrl ) {
return;
}
// Add the post ID to the tab URL if it exists.
$postId = $('#post_ID');
if ( $postId.length ) {
tabUrl += '&post_id=' + $postId.val();
}
// Generate the tab states.
_.each( tabs, function( title, id ) {
this.state( 'iframe:' + id ).set( _.defaults({
tab: id,
src: tabUrl + '&tab=' + id,
title: title,
content: 'iframe',
menu: 'default'
}, options ) );
}, this );
this.on( 'content:create:iframe', this.iframeContent, this );
this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this );
this.on( 'menu:render:default', this.iframeMenu, this );
this.on( 'open', this.hijackThickbox, this );
this.on( 'close', this.restoreThickbox, this );
},
/**
* @param {Object} content
* @this wp.media.controller.Region
*/
iframeContent: function( content ) {
this.$el.addClass('hide-toolbar');
content.view = new wp.media.view.Iframe({
controller: this
});
},
iframeContentCleanup: function() {
this.$el.removeClass('hide-toolbar');
},
iframeMenu: function( view ) {
var views = {};
if ( ! view ) {
return;
}
_.each( wp.media.view.settings.tabs, function( title, id ) {
views[ 'iframe:' + id ] = {
text: this.state( 'iframe:' + id ).get('title'),
priority: 200
};
}, this );
view.set( views );
},
hijackThickbox: function() {
var frame = this;
if ( ! window.tb_remove || this._tb_remove ) {
return;
}
this._tb_remove = window.tb_remove;
window.tb_remove = function() {
frame.close();
frame.reset();
frame.setState( frame.options.state );
frame._tb_remove.call( window );
};
},
restoreThickbox: function() {
if ( ! this._tb_remove ) {
return;
}
window.tb_remove = this._tb_remove;
delete this._tb_remove;
}
});
// Map some of the modal's methods to the frame.
_.each(['open','close','attach','detach','escape'], function( method ) {
/**
* @function open
* @memberOf wp.media.view.MediaFrame
* @instance
*
* @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
*/
/**
* @function close
* @memberOf wp.media.view.MediaFrame
* @instance
*
* @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
*/
/**
* @function attach
* @memberOf wp.media.view.MediaFrame
* @instance
*
* @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
*/
/**
* @function detach
* @memberOf wp.media.view.MediaFrame
* @instance
*
* @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
*/
/**
* @function escape
* @memberOf wp.media.view.MediaFrame
* @instance
*
* @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
*/
MediaFrame.prototype[ method ] = function() {
if ( this.modal ) {
this.modal[ method ].apply( this.modal, arguments );
}
return this;
};
});
module.exports = MediaFrame;