Source: media/views/modal.js

var $ = jQuery,
	Modal;

/**
 * wp.media.view.Modal
 *
 * A modal view, which the media modal uses as its default container.
 *
 * @memberOf wp.media.view
 *
 * @class
 * @augments wp.media.View
 * @augments wp.Backbone.View
 * @augments Backbone.View
 */
Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{
	tagName:  'div',
	template: wp.template('media-modal'),

	events: {
		'click .media-modal-backdrop, .media-modal-close': 'escapeHandler',
		'keydown': 'keydown'
	},

	clickedOpenerEl: null,

	initialize: function() {
		_.defaults( this.options, {
			container:      document.body,
			title:          '',
			propagate:      true,
			hasCloseButton: true
		});

		this.focusManager = new wp.media.view.FocusManager({
			el: this.el
		});
	},
	/**
	 * @return {Object}
	 */
	prepare: function() {
		return {
			title:          this.options.title,
			hasCloseButton: this.options.hasCloseButton
		};
	},

	/**
	 * @return {wp.media.view.Modal} Returns itself to allow chaining.
	 */
	attach: function() {
		if ( this.views.attached ) {
			return this;
		}

		if ( ! this.views.rendered ) {
			this.render();
		}

		this.$el.appendTo( this.options.container );

		// Manually mark the view as attached and trigger ready.
		this.views.attached = true;
		this.views.ready();

		return this.propagate('attach');
	},

	/**
	 * @return {wp.media.view.Modal} Returns itself to allow chaining.
	 */
	detach: function() {
		if ( this.$el.is(':visible') ) {
			this.close();
		}

		this.$el.detach();
		this.views.attached = false;
		return this.propagate('detach');
	},

	/**
	 * @return {wp.media.view.Modal} Returns itself to allow chaining.
	 */
	open: function() {
		var $el = this.$el,
			mceEditor;

		if ( $el.is(':visible') ) {
			return this;
		}

		this.clickedOpenerEl = document.activeElement;

		if ( ! this.views.attached ) {
			this.attach();
		}

		// Disable page scrolling.
		$( 'body' ).addClass( 'modal-open' );

		$el.show();

		// Try to close the onscreen keyboard.
		if ( 'ontouchend' in document ) {
			if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor ) && ! mceEditor.isHidden() && mceEditor.iframeElement ) {
				mceEditor.iframeElement.focus();
				mceEditor.iframeElement.blur();

				setTimeout( function() {
					mceEditor.iframeElement.blur();
				}, 100 );
			}
		}

		// Set initial focus on the content instead of this view element, to avoid page scrolling.
		this.$( '.media-modal' ).focus();

		// Hide the page content from assistive technologies.
		this.focusManager.setAriaHiddenOnBodyChildren( $el );

		return this.propagate('open');
	},

	/**
	 * @param {Object} options
	 * @return {wp.media.view.Modal} Returns itself to allow chaining.
	 */
	close: function( options ) {
		if ( ! this.views.attached || ! this.$el.is(':visible') ) {
			return this;
		}

		// Enable page scrolling.
		$( 'body' ).removeClass( 'modal-open' );

		// Hide modal and remove restricted media modal tab focus once it's closed.
		this.$el.hide().undelegate( 'keydown' );

		/*
		 * Make visible again to assistive technologies all body children that
		 * have been made hidden when the modal opened.
		 */
		this.focusManager.removeAriaHiddenFromBodyChildren();

		// Move focus back in useful location once modal is closed.
		if ( null !== this.clickedOpenerEl ) {
			// Move focus back to the element that opened the modal.
			this.clickedOpenerEl.focus();
		} else {
			// Fallback to the admin page main element.
			$( '#wpbody-content' )
				.attr( 'tabindex', '-1' )
				.focus();
		}

		this.propagate('close');

		if ( options && options.escape ) {
			this.propagate('escape');
		}

		return this;
	},
	/**
	 * @return {wp.media.view.Modal} Returns itself to allow chaining.
	 */
	escape: function() {
		return this.close({ escape: true });
	},
	/**
	 * @param {Object} event
	 */
	escapeHandler: function( event ) {
		event.preventDefault();
		this.escape();
	},

	/**
	 * @param {Array|Object} content Views to register to '.media-modal-content'
	 * @return {wp.media.view.Modal} Returns itself to allow chaining.
	 */
	content: function( content ) {
		this.views.set( '.media-modal-content', content );
		return this;
	},

	/**
	 * Triggers a modal event and if the `propagate` option is set,
	 * forwards events to the modal's controller.
	 *
	 * @param {string} id
	 * @return {wp.media.view.Modal} Returns itself to allow chaining.
	 */
	propagate: function( id ) {
		this.trigger( id );

		if ( this.options.propagate ) {
			this.controller.trigger( id );
		}

		return this;
	},
	/**
	 * @param {Object} event
	 */
	keydown: function( event ) {
		// Close the modal when escape is pressed.
		if ( 27 === event.which && this.$el.is(':visible') ) {
			this.escape();
			event.stopImmediatePropagation();
		}
	}
});

module.exports = Modal;