Source: media/views/media-frame.js

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;