403 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			403 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
/**
 | 
						|
 * @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
 | 
						|
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 | 
						|
 */
 | 
						|
 | 
						|
( function() {
 | 
						|
	CKEDITOR.plugins.add( 'panel', {
 | 
						|
		beforeInit: function( editor ) {
 | 
						|
			editor.ui.addHandler( CKEDITOR.UI_PANEL, CKEDITOR.ui.panel.handler );
 | 
						|
		}
 | 
						|
	} );
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Panel UI element.
 | 
						|
	 *
 | 
						|
	 * @readonly
 | 
						|
	 * @property {String} [='panel']
 | 
						|
	 * @member CKEDITOR
 | 
						|
	 */
 | 
						|
	CKEDITOR.UI_PANEL = 'panel';
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @class
 | 
						|
	 * @constructor Creates a panel class instance.
 | 
						|
	 * @param {CKEDITOR.dom.document} document
 | 
						|
	 * @param {Object} definition
 | 
						|
	 */
 | 
						|
	CKEDITOR.ui.panel = function( document, definition ) {
 | 
						|
		// Copy all definition properties to this object.
 | 
						|
		if ( definition )
 | 
						|
			CKEDITOR.tools.extend( this, definition );
 | 
						|
 | 
						|
		// Set defaults.
 | 
						|
		CKEDITOR.tools.extend( this, {
 | 
						|
			className: '',
 | 
						|
			css: []
 | 
						|
		} );
 | 
						|
 | 
						|
		this.id = CKEDITOR.tools.getNextId();
 | 
						|
		this.document = document;
 | 
						|
		this.isFramed = this.forceIFrame || this.css.length;
 | 
						|
 | 
						|
		this._ = {
 | 
						|
			blocks: {}
 | 
						|
		};
 | 
						|
	};
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Represents panel handler object.
 | 
						|
	 *
 | 
						|
	 * @class
 | 
						|
	 * @singleton
 | 
						|
	 * @extends CKEDITOR.ui.handlerDefinition
 | 
						|
	 */
 | 
						|
	CKEDITOR.ui.panel.handler = {
 | 
						|
		/**
 | 
						|
		 * Transforms a panel definition in a {@link CKEDITOR.ui.panel} instance.
 | 
						|
		 *
 | 
						|
		 * @param {Object} definition
 | 
						|
		 * @returns {CKEDITOR.ui.panel}
 | 
						|
		 */
 | 
						|
		create: function( definition ) {
 | 
						|
			return new CKEDITOR.ui.panel( definition );
 | 
						|
		}
 | 
						|
	};
 | 
						|
 | 
						|
	var panelTpl = CKEDITOR.addTemplate( 'panel', '<div lang="{langCode}" id="{id}" dir={dir}' +
 | 
						|
		' class="cke cke_reset_all {editorId} cke_panel cke_panel {cls} cke_{dir}"' +
 | 
						|
		' style="z-index:{z-index}" role="presentation">' +
 | 
						|
		'{frame}' +
 | 
						|
		'</div>' );
 | 
						|
 | 
						|
	var frameTpl = CKEDITOR.addTemplate( 'panel-frame', '<iframe id="{id}" class="cke_panel_frame" role="presentation" frameborder="0" src="{src}"></iframe>' );
 | 
						|
 | 
						|
	var frameDocTpl = CKEDITOR.addTemplate( 'panel-frame-inner', '<!DOCTYPE html>' +
 | 
						|
		'<html class="cke_panel_container {env}" dir="{dir}" lang="{langCode}">' +
 | 
						|
			'<head>{css}</head>' +
 | 
						|
			'<body class="cke_{dir}"' +
 | 
						|
				' style="margin:0;padding:0" onload="{onload}"></body>' +
 | 
						|
		'<\/html>' );
 | 
						|
 | 
						|
	/** @class CKEDITOR.ui.panel */
 | 
						|
	CKEDITOR.ui.panel.prototype = {
 | 
						|
		/**
 | 
						|
		 * Renders the combo.
 | 
						|
		 *
 | 
						|
		 * @param {CKEDITOR.editor} editor The editor instance which this button is
 | 
						|
		 * to be used by.
 | 
						|
		 * @param {Array} [output] The output array to which append the HTML relative
 | 
						|
		 * to this button.
 | 
						|
		 */
 | 
						|
		render: function( editor, output ) {
 | 
						|
			this.getHolderElement = function() {
 | 
						|
				var holder = this._.holder;
 | 
						|
 | 
						|
				if ( !holder ) {
 | 
						|
					if ( this.isFramed ) {
 | 
						|
						var iframe = this.document.getById( this.id + '_frame' ),
 | 
						|
							parentDiv = iframe.getParent(),
 | 
						|
							doc = iframe.getFrameDocument();
 | 
						|
 | 
						|
						// Make it scrollable on iOS. (#8308)
 | 
						|
						CKEDITOR.env.iOS && parentDiv.setStyles( {
 | 
						|
							'overflow': 'scroll',
 | 
						|
							'-webkit-overflow-scrolling': 'touch'
 | 
						|
						} );
 | 
						|
 | 
						|
						var onLoad = CKEDITOR.tools.addFunction( CKEDITOR.tools.bind( function( ev ) {
 | 
						|
							this.isLoaded = true;
 | 
						|
							if ( this.onLoad )
 | 
						|
								this.onLoad();
 | 
						|
						}, this ) );
 | 
						|
 | 
						|
						doc.write( frameDocTpl.output( CKEDITOR.tools.extend( {
 | 
						|
							css: CKEDITOR.tools.buildStyleHtml( this.css ),
 | 
						|
							onload: 'window.parent.CKEDITOR.tools.callFunction(' + onLoad + ');'
 | 
						|
						}, data ) ) );
 | 
						|
 | 
						|
						var win = doc.getWindow();
 | 
						|
 | 
						|
						// Register the CKEDITOR global.
 | 
						|
						win.$.CKEDITOR = CKEDITOR;
 | 
						|
 | 
						|
						// Arrow keys for scrolling is only preventable with 'keypress' event in Opera (#4534).
 | 
						|
						doc.on( 'key' + ( CKEDITOR.env.opera ? 'press' : 'down' ), function( evt ) {
 | 
						|
							var keystroke = evt.data.getKeystroke(),
 | 
						|
								dir = this.document.getById( this.id ).getAttribute( 'dir' );
 | 
						|
 | 
						|
							// Delegate key processing to block.
 | 
						|
							if ( this._.onKeyDown && this._.onKeyDown( keystroke ) === false ) {
 | 
						|
								evt.data.preventDefault();
 | 
						|
								return;
 | 
						|
							}
 | 
						|
 | 
						|
							// ESC/ARROW-LEFT(ltr) OR ARROW-RIGHT(rtl)
 | 
						|
							if ( keystroke == 27 || keystroke == ( dir == 'rtl' ? 39 : 37 ) ) {
 | 
						|
								if ( this.onEscape && this.onEscape( keystroke ) === false )
 | 
						|
									evt.data.preventDefault();
 | 
						|
							}
 | 
						|
						}, this );
 | 
						|
 | 
						|
						holder = doc.getBody();
 | 
						|
						holder.unselectable();
 | 
						|
						CKEDITOR.env.air && CKEDITOR.tools.callFunction( onLoad );
 | 
						|
					} else
 | 
						|
						holder = this.document.getById( this.id );
 | 
						|
 | 
						|
					this._.holder = holder;
 | 
						|
				}
 | 
						|
 | 
						|
				return holder;
 | 
						|
			};
 | 
						|
 | 
						|
			var data = {
 | 
						|
				editorId: editor.id,
 | 
						|
				id: this.id,
 | 
						|
				langCode: editor.langCode,
 | 
						|
				dir: editor.lang.dir,
 | 
						|
				cls: this.className,
 | 
						|
				frame: '',
 | 
						|
				env: CKEDITOR.env.cssClass,
 | 
						|
				'z-index': editor.config.baseFloatZIndex + 1
 | 
						|
			};
 | 
						|
 | 
						|
			if ( this.isFramed ) {
 | 
						|
				// With IE, the custom domain has to be taken care at first,
 | 
						|
				// for other browers, the 'src' attribute should be left empty to
 | 
						|
				// trigger iframe's 'load' event.
 | 
						|
				var src =
 | 
						|
					CKEDITOR.env.air ? 'javascript:void(0)' :
 | 
						|
					CKEDITOR.env.ie ? 'javascript:void(function(){' + encodeURIComponent(
 | 
						|
						'document.open();' +
 | 
						|
						// In IE, the document domain must be set any time we call document.open().
 | 
						|
						'(' + CKEDITOR.tools.fixDomain + ')();' +
 | 
						|
						'document.close();'
 | 
						|
					) + '}())' :
 | 
						|
					'';
 | 
						|
 | 
						|
				data.frame = frameTpl.output( {
 | 
						|
					id: this.id + '_frame',
 | 
						|
					src: src
 | 
						|
				} );
 | 
						|
			}
 | 
						|
 | 
						|
			var html = panelTpl.output( data );
 | 
						|
 | 
						|
			if ( output )
 | 
						|
				output.push( html );
 | 
						|
 | 
						|
			return html;
 | 
						|
		},
 | 
						|
 | 
						|
		/**
 | 
						|
		 * @todo
 | 
						|
		 */
 | 
						|
		addBlock: function( name, block ) {
 | 
						|
			block = this._.blocks[ name ] = block instanceof CKEDITOR.ui.panel.block ? block : new CKEDITOR.ui.panel.block( this.getHolderElement(), block );
 | 
						|
 | 
						|
			if ( !this._.currentBlock )
 | 
						|
				this.showBlock( name );
 | 
						|
 | 
						|
			return block;
 | 
						|
		},
 | 
						|
 | 
						|
		/**
 | 
						|
		 * @todo
 | 
						|
		 */
 | 
						|
		getBlock: function( name ) {
 | 
						|
			return this._.blocks[ name ];
 | 
						|
		},
 | 
						|
 | 
						|
		/**
 | 
						|
		 * @todo
 | 
						|
		 */
 | 
						|
		showBlock: function( name ) {
 | 
						|
			var blocks = this._.blocks,
 | 
						|
				block = blocks[ name ],
 | 
						|
				current = this._.currentBlock;
 | 
						|
 | 
						|
			// ARIA role works better in IE on the body element, while on the iframe
 | 
						|
			// for FF. (#8864)
 | 
						|
			var holder = !this.forceIFrame || CKEDITOR.env.ie ? this._.holder : this.document.getById( this.id + '_frame' );
 | 
						|
 | 
						|
			if ( current )
 | 
						|
				current.hide();
 | 
						|
 | 
						|
			this._.currentBlock = block;
 | 
						|
 | 
						|
			CKEDITOR.fire( 'ariaWidget', holder );
 | 
						|
 | 
						|
			// Reset the focus index, so it will always go into the first one.
 | 
						|
			block._.focusIndex = -1;
 | 
						|
 | 
						|
			this._.onKeyDown = block.onKeyDown && CKEDITOR.tools.bind( block.onKeyDown, block );
 | 
						|
 | 
						|
			block.show();
 | 
						|
 | 
						|
			return block;
 | 
						|
		},
 | 
						|
 | 
						|
		/**
 | 
						|
		 * @todo
 | 
						|
		 */
 | 
						|
		destroy: function() {
 | 
						|
			this.element && this.element.remove();
 | 
						|
		}
 | 
						|
	};
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @class
 | 
						|
	 *
 | 
						|
	 * @todo class and all methods
 | 
						|
	 */
 | 
						|
	CKEDITOR.ui.panel.block = CKEDITOR.tools.createClass( {
 | 
						|
		/**
 | 
						|
		 * Creates a block class instances.
 | 
						|
		 *
 | 
						|
		 * @constructor
 | 
						|
		 * @todo
 | 
						|
		 */
 | 
						|
		$: function( blockHolder, blockDefinition ) {
 | 
						|
			this.element = blockHolder.append( blockHolder.getDocument().createElement( 'div', {
 | 
						|
				attributes: {
 | 
						|
					'tabindex': -1,
 | 
						|
					'class': 'cke_panel_block'
 | 
						|
				},
 | 
						|
				styles: {
 | 
						|
					display: 'none'
 | 
						|
				}
 | 
						|
			} ) );
 | 
						|
 | 
						|
			// Copy all definition properties to this object.
 | 
						|
			if ( blockDefinition )
 | 
						|
				CKEDITOR.tools.extend( this, blockDefinition );
 | 
						|
 | 
						|
			// Set the a11y attributes of this element ...
 | 
						|
			this.element.setAttributes( {
 | 
						|
				'role': this.attributes.role || 'presentation',
 | 
						|
				'aria-label': this.attributes[ 'aria-label' ],
 | 
						|
				'title': this.attributes.title || this.attributes[ 'aria-label' ]
 | 
						|
			} );
 | 
						|
 | 
						|
			this.keys = {};
 | 
						|
 | 
						|
			this._.focusIndex = -1;
 | 
						|
 | 
						|
			// Disable context menu for panels.
 | 
						|
			this.element.disableContextMenu();
 | 
						|
		},
 | 
						|
 | 
						|
		_: {
 | 
						|
 | 
						|
			/**
 | 
						|
			 * Mark the item specified by the index as current activated.
 | 
						|
			 */
 | 
						|
			markItem: function( index ) {
 | 
						|
				if ( index == -1 )
 | 
						|
					return;
 | 
						|
				var links = this.element.getElementsByTag( 'a' );
 | 
						|
				var item = links.getItem( this._.focusIndex = index );
 | 
						|
 | 
						|
				// Safari need focus on the iframe window first(#3389), but we need
 | 
						|
				// lock the blur to avoid hiding the panel.
 | 
						|
				if ( CKEDITOR.env.webkit || CKEDITOR.env.opera )
 | 
						|
					item.getDocument().getWindow().focus();
 | 
						|
				item.focus();
 | 
						|
 | 
						|
				this.onMark && this.onMark( item );
 | 
						|
			}
 | 
						|
		},
 | 
						|
 | 
						|
		proto: {
 | 
						|
			show: function() {
 | 
						|
				this.element.setStyle( 'display', '' );
 | 
						|
			},
 | 
						|
 | 
						|
			hide: function() {
 | 
						|
				if ( !this.onHide || this.onHide.call( this ) !== true )
 | 
						|
					this.element.setStyle( 'display', 'none' );
 | 
						|
			},
 | 
						|
 | 
						|
			onKeyDown: function( keystroke, noCycle ) {
 | 
						|
				var keyAction = this.keys[ keystroke ];
 | 
						|
				switch ( keyAction ) {
 | 
						|
					// Move forward.
 | 
						|
					case 'next':
 | 
						|
						var index = this._.focusIndex,
 | 
						|
							links = this.element.getElementsByTag( 'a' ),
 | 
						|
							link;
 | 
						|
 | 
						|
						while ( ( link = links.getItem( ++index ) ) ) {
 | 
						|
							// Move the focus only if the element is marked with
 | 
						|
							// the _cke_focus and it it's visible (check if it has
 | 
						|
							// width).
 | 
						|
							if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth ) {
 | 
						|
								this._.focusIndex = index;
 | 
						|
								link.focus();
 | 
						|
								break;
 | 
						|
							}
 | 
						|
						}
 | 
						|
 | 
						|
						// If no link was found, cycle and restart from the top. (#11125)
 | 
						|
						if ( !link && !noCycle ) {
 | 
						|
							this._.focusIndex = -1;
 | 
						|
							return this.onKeyDown( keystroke, 1 );
 | 
						|
						}
 | 
						|
 | 
						|
						return false;
 | 
						|
 | 
						|
						// Move backward.
 | 
						|
					case 'prev':
 | 
						|
						index = this._.focusIndex;
 | 
						|
						links = this.element.getElementsByTag( 'a' );
 | 
						|
 | 
						|
						while ( index > 0 && ( link = links.getItem( --index ) ) ) {
 | 
						|
							// Move the focus only if the element is marked with
 | 
						|
							// the _cke_focus and it it's visible (check if it has
 | 
						|
							// width).
 | 
						|
							if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth ) {
 | 
						|
								this._.focusIndex = index;
 | 
						|
								link.focus();
 | 
						|
								break;
 | 
						|
							}
 | 
						|
 | 
						|
							// Make sure link is null when the loop ends and nothing was
 | 
						|
							// found (#11125).
 | 
						|
							link = null;
 | 
						|
						}
 | 
						|
 | 
						|
						// If no link was found, cycle and restart from the bottom. (#11125)
 | 
						|
						if ( !link && !noCycle ) {
 | 
						|
							this._.focusIndex = links.count();
 | 
						|
							return this.onKeyDown( keystroke, 1 );
 | 
						|
						}
 | 
						|
 | 
						|
						return false;
 | 
						|
 | 
						|
					case 'click':
 | 
						|
					case 'mouseup':
 | 
						|
						index = this._.focusIndex;
 | 
						|
						link = index >= 0 && this.element.getElementsByTag( 'a' ).getItem( index );
 | 
						|
 | 
						|
						if ( link )
 | 
						|
							link.$[ keyAction ] ? link.$[ keyAction ]() : link.$[ 'on' + keyAction ]();
 | 
						|
 | 
						|
						return false;
 | 
						|
				}
 | 
						|
 | 
						|
				return true;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} );
 | 
						|
 | 
						|
} )();
 | 
						|
 | 
						|
/**
 | 
						|
 * Fired when a panel is added to the document.
 | 
						|
 *
 | 
						|
 * @event ariaWidget
 | 
						|
 * @member CKEDITOR
 | 
						|
 * @param {Object} data The element wrapping the panel.
 | 
						|
 */
 |