442 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			442 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /**
 | |
|  * @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
 | |
|  * For licensing, see LICENSE.md or http://ckeditor.com/license
 | |
|  */
 | |
| 
 | |
| CKEDITOR.plugins.add( 'richcombo', {
 | |
| 	requires: 'floatpanel,listblock,button',
 | |
| 
 | |
| 	beforeInit: function( editor ) {
 | |
| 		editor.ui.addHandler( CKEDITOR.UI_RICHCOMBO, CKEDITOR.ui.richCombo.handler );
 | |
| 	}
 | |
| } );
 | |
| 
 | |
| ( function() {
 | |
| 	var template = '<span id="{id}"' +
 | |
| 		' class="cke_combo cke_combo__{name} {cls}"' +
 | |
| 		' role="presentation">' +
 | |
| 			'<span id="{id}_label" class="cke_combo_label">{label}</span>' +
 | |
| 			'<a class="cke_combo_button" hidefocus=true title="{title}" tabindex="-1"' +
 | |
| 			( CKEDITOR.env.gecko && CKEDITOR.env.version >= 10900 && !CKEDITOR.env.hc ? '' : '" href="javascript:void(\'{titleJs}\')"' ) +
 | |
| 			' hidefocus="true"' +
 | |
| 			' role="button"' +
 | |
| 			' aria-labelledby="{id}_label"' +
 | |
| 			' aria-haspopup="true"';
 | |
| 
 | |
| 	// Some browsers don't cancel key events in the keydown but in the
 | |
| 	// keypress.
 | |
| 	// TODO: Check if really needed for Gecko+Mac.
 | |
| 	if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
 | |
| 		template += ' onkeypress="return false;"';
 | |
| 
 | |
| 	// With Firefox, we need to force the button to redraw, otherwise it
 | |
| 	// will remain in the focus state.
 | |
| 	if ( CKEDITOR.env.gecko )
 | |
| 		template += ' onblur="this.style.cssText = this.style.cssText;"';
 | |
| 
 | |
| 	template +=
 | |
| 		' onkeydown="return CKEDITOR.tools.callFunction({keydownFn},event,this);"' +
 | |
| 		' onmousedown="return CKEDITOR.tools.callFunction({mousedownFn},event);" ' +
 | |
| 		' onfocus="return CKEDITOR.tools.callFunction({focusFn},event);" ' +
 | |
| 			( CKEDITOR.env.ie ? 'onclick="return false;" onmouseup' : 'onclick' ) + // #188
 | |
| 				'="CKEDITOR.tools.callFunction({clickFn},this);return false;">' +
 | |
| 			'<span id="{id}_text" class="cke_combo_text cke_combo_inlinelabel">{label}</span>' +
 | |
| 			'<span class="cke_combo_open">' +
 | |
| 				'<span class="cke_combo_arrow">' +
 | |
| 				// BLACK DOWN-POINTING TRIANGLE
 | |
| 	( CKEDITOR.env.hc ? '▼' : CKEDITOR.env.air ? ' ' : '' ) +
 | |
| 				'</span>' +
 | |
| 			'</span>' +
 | |
| 		'</a>' +
 | |
| 		'</span>';
 | |
| 
 | |
| 	var rcomboTpl = CKEDITOR.addTemplate( 'combo', template );
 | |
| 
 | |
| 	/**
 | |
| 	 * Button UI element.
 | |
| 	 *
 | |
| 	 * @readonly
 | |
| 	 * @property {String} [='richcombo']
 | |
| 	 * @member CKEDITOR
 | |
| 	 */
 | |
| 	CKEDITOR.UI_RICHCOMBO = 'richcombo';
 | |
| 
 | |
| 	/**
 | |
| 	 * @class
 | |
| 	 * @todo
 | |
| 	 */
 | |
| 	CKEDITOR.ui.richCombo = CKEDITOR.tools.createClass( {
 | |
| 		$: function( definition ) {
 | |
| 			// Copy all definition properties to this object.
 | |
| 			CKEDITOR.tools.extend( this, definition,
 | |
| 			// Set defaults.
 | |
| 			{
 | |
| 				// The combo won't participate in toolbar grouping.
 | |
| 				canGroup: false,
 | |
| 				title: definition.label,
 | |
| 				modes: { wysiwyg: 1 },
 | |
| 				editorFocus: 1
 | |
| 			} );
 | |
| 
 | |
| 			// We don't want the panel definition in this object.
 | |
| 			var panelDefinition = this.panel || {};
 | |
| 			delete this.panel;
 | |
| 
 | |
| 			this.id = CKEDITOR.tools.getNextNumber();
 | |
| 
 | |
| 			this.document = ( panelDefinition.parent && panelDefinition.parent.getDocument() ) || CKEDITOR.document;
 | |
| 
 | |
| 			panelDefinition.className = 'cke_combopanel';
 | |
| 			panelDefinition.block = {
 | |
| 				multiSelect: panelDefinition.multiSelect,
 | |
| 				attributes: panelDefinition.attributes
 | |
| 			};
 | |
| 			panelDefinition.toolbarRelated = true;
 | |
| 
 | |
| 			this._ = {
 | |
| 				panelDefinition: panelDefinition,
 | |
| 				items: {}
 | |
| 			};
 | |
| 		},
 | |
| 
 | |
| 		proto: {
 | |
| 			renderHtml: function( editor ) {
 | |
| 				var output = [];
 | |
| 				this.render( editor, output );
 | |
| 				return output.join( '' );
 | |
| 			},
 | |
| 
 | |
| 			/**
 | |
| 			 * 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 ) {
 | |
| 				var env = CKEDITOR.env;
 | |
| 
 | |
| 				var id = 'cke_' + this.id;
 | |
| 				var clickFn = CKEDITOR.tools.addFunction( function( el ) {
 | |
| 					// Restore locked selection in Opera.
 | |
| 					if ( selLocked ) {
 | |
| 						editor.unlockSelection( 1 );
 | |
| 						selLocked = 0;
 | |
| 					}
 | |
| 					instance.execute( el );
 | |
| 				}, this );
 | |
| 
 | |
| 				var combo = this;
 | |
| 				var instance = {
 | |
| 					id: id,
 | |
| 					combo: this,
 | |
| 					focus: function() {
 | |
| 						var element = CKEDITOR.document.getById( id ).getChild( 1 );
 | |
| 						element.focus();
 | |
| 					},
 | |
| 					execute: function( el ) {
 | |
| 						var _ = combo._;
 | |
| 
 | |
| 						if ( _.state == CKEDITOR.TRISTATE_DISABLED )
 | |
| 							return;
 | |
| 
 | |
| 						combo.createPanel( editor );
 | |
| 
 | |
| 						if ( _.on ) {
 | |
| 							_.panel.hide();
 | |
| 							return;
 | |
| 						}
 | |
| 
 | |
| 						combo.commit();
 | |
| 						var value = combo.getValue();
 | |
| 						if ( value )
 | |
| 							_.list.mark( value );
 | |
| 						else
 | |
| 							_.list.unmarkAll();
 | |
| 
 | |
| 						_.panel.showBlock( combo.id, new CKEDITOR.dom.element( el ), 4 );
 | |
| 					},
 | |
| 					clickFn: clickFn
 | |
| 				};
 | |
| 
 | |
| 				function updateState() {
 | |
| 					var state = this.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
 | |
| 
 | |
| 					if ( editor.readOnly && !this.readOnly )
 | |
| 						state = CKEDITOR.TRISTATE_DISABLED;
 | |
| 
 | |
| 					this.setState( state );
 | |
| 					this.setValue( '' );
 | |
| 
 | |
| 					// Let plugin to disable button.
 | |
| 					if ( state != CKEDITOR.TRISTATE_DISABLED && this.refresh )
 | |
| 						this.refresh();
 | |
| 				}
 | |
| 
 | |
| 				// Update status when activeFilter, mode, selection or readOnly changes.
 | |
| 				editor.on( 'activeFilterChange', updateState, this );
 | |
| 				editor.on( 'mode', updateState, this );
 | |
| 				editor.on( 'selectionChange', updateState, this );
 | |
| 				// If this combo is sensitive to readOnly state, update it accordingly.
 | |
| 				!this.readOnly && editor.on( 'readOnly', updateState, this );
 | |
| 
 | |
| 				var keyDownFn = CKEDITOR.tools.addFunction( function( ev, element ) {
 | |
| 					ev = new CKEDITOR.dom.event( ev );
 | |
| 
 | |
| 					var keystroke = ev.getKeystroke();
 | |
| 
 | |
| 					// ARROW-DOWN
 | |
| 					// This call is duplicated in plugins/toolbar/plugin.js in itemKeystroke().
 | |
| 					// Move focus to the first element after drop down was opened by the arrow down key.
 | |
| 					if ( keystroke == 40 ) {
 | |
| 						editor.once( 'panelShow', function( evt ) {
 | |
| 							evt.data._.panel._.currentBlock.onKeyDown( 40 );
 | |
| 						} );
 | |
| 					}
 | |
| 
 | |
| 					switch ( keystroke ) {
 | |
| 						case 13: // ENTER
 | |
| 						case 32: // SPACE
 | |
| 						case 40: // ARROW-DOWN
 | |
| 							// Show panel
 | |
| 							CKEDITOR.tools.callFunction( clickFn, element );
 | |
| 							break;
 | |
| 						default:
 | |
| 							// Delegate the default behavior to toolbar button key handling.
 | |
| 							instance.onkey( instance, keystroke );
 | |
| 					}
 | |
| 
 | |
| 					// Avoid subsequent focus grab on editor document.
 | |
| 					ev.preventDefault();
 | |
| 				} );
 | |
| 
 | |
| 				var focusFn = CKEDITOR.tools.addFunction( function() {
 | |
| 					instance.onfocus && instance.onfocus();
 | |
| 				} );
 | |
| 
 | |
| 				var selLocked = 0;
 | |
| 				var mouseDownFn = CKEDITOR.tools.addFunction( function() {
 | |
| 					// Opera: lock to prevent loosing editable text selection when clicking on button.
 | |
| 					if ( CKEDITOR.env.opera ) {
 | |
| 						var edt = editor.editable();
 | |
| 						if ( edt.isInline() && edt.hasFocus ) {
 | |
| 							editor.lockSelection();
 | |
| 							selLocked = 1;
 | |
| 						}
 | |
| 					}
 | |
| 				} );
 | |
| 
 | |
| 				// For clean up
 | |
| 				instance.keyDownFn = keyDownFn;
 | |
| 
 | |
| 				var params = {
 | |
| 					id: id,
 | |
| 					name: this.name || this.command,
 | |
| 					label: this.label,
 | |
| 					title: this.title,
 | |
| 					cls: this.className || '',
 | |
| 					titleJs: env.gecko && env.version >= 10900 && !env.hc ? '' : ( this.title || '' ).replace( "'", '' ),
 | |
| 					keydownFn: keyDownFn,
 | |
| 					mousedownFn: mouseDownFn,
 | |
| 					focusFn: focusFn,
 | |
| 					clickFn: clickFn
 | |
| 				};
 | |
| 
 | |
| 				rcomboTpl.output( params, output );
 | |
| 
 | |
| 				if ( this.onRender )
 | |
| 					this.onRender();
 | |
| 
 | |
| 				return instance;
 | |
| 			},
 | |
| 
 | |
| 			createPanel: function( editor ) {
 | |
| 				if ( this._.panel )
 | |
| 					return;
 | |
| 
 | |
| 				var panelDefinition = this._.panelDefinition,
 | |
| 					panelBlockDefinition = this._.panelDefinition.block,
 | |
| 					panelParentElement = panelDefinition.parent || CKEDITOR.document.getBody(),
 | |
| 					namedPanelCls = 'cke_combopanel__' + this.name,
 | |
| 					panel = new CKEDITOR.ui.floatPanel( editor, panelParentElement, panelDefinition ),
 | |
| 					list = panel.addListBlock( this.id, panelBlockDefinition ),
 | |
| 					me = this;
 | |
| 
 | |
| 				panel.onShow = function() {
 | |
| 					this.element.addClass( namedPanelCls );
 | |
| 
 | |
| 					me.setState( CKEDITOR.TRISTATE_ON );
 | |
| 
 | |
| 					me._.on = 1;
 | |
| 
 | |
| 					me.editorFocus && !editor.focusManager.hasFocus && editor.focus();
 | |
| 
 | |
| 					if ( me.onOpen )
 | |
| 						me.onOpen();
 | |
| 
 | |
| 					// The "panelShow" event is fired assinchronously, after the
 | |
| 					// onShow method call.
 | |
| 					editor.once( 'panelShow', function() {
 | |
| 						list.focus( !list.multiSelect && me.getValue() );
 | |
| 					} );
 | |
| 				};
 | |
| 
 | |
| 				panel.onHide = function( preventOnClose ) {
 | |
| 					this.element.removeClass( namedPanelCls );
 | |
| 
 | |
| 					me.setState( me.modes && me.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
 | |
| 
 | |
| 					me._.on = 0;
 | |
| 
 | |
| 					if ( !preventOnClose && me.onClose )
 | |
| 						me.onClose();
 | |
| 				};
 | |
| 
 | |
| 				panel.onEscape = function() {
 | |
| 					// Hide drop-down with focus returned.
 | |
| 					panel.hide( 1 );
 | |
| 				};
 | |
| 
 | |
| 				list.onClick = function( value, marked ) {
 | |
| 
 | |
| 					if ( me.onClick )
 | |
| 						me.onClick.call( me, value, marked );
 | |
| 
 | |
| 					panel.hide();
 | |
| 				};
 | |
| 
 | |
| 				this._.panel = panel;
 | |
| 				this._.list = list;
 | |
| 
 | |
| 				panel.getBlock( this.id ).onHide = function() {
 | |
| 					me._.on = 0;
 | |
| 					me.setState( CKEDITOR.TRISTATE_OFF );
 | |
| 				};
 | |
| 
 | |
| 				if ( this.init )
 | |
| 					this.init();
 | |
| 			},
 | |
| 
 | |
| 			setValue: function( value, text ) {
 | |
| 				this._.value = value;
 | |
| 
 | |
| 				var textElement = this.document.getById( 'cke_' + this.id + '_text' );
 | |
| 				if ( textElement ) {
 | |
| 					if ( !( value || text ) ) {
 | |
| 						text = this.label;
 | |
| 						textElement.addClass( 'cke_combo_inlinelabel' );
 | |
| 					} else
 | |
| 						textElement.removeClass( 'cke_combo_inlinelabel' );
 | |
| 
 | |
| 					textElement.setText( typeof text != 'undefined' ? text : value );
 | |
| 				}
 | |
| 			},
 | |
| 
 | |
| 			getValue: function() {
 | |
| 				return this._.value || '';
 | |
| 			},
 | |
| 
 | |
| 			unmarkAll: function() {
 | |
| 				this._.list.unmarkAll();
 | |
| 			},
 | |
| 
 | |
| 			mark: function( value ) {
 | |
| 				this._.list.mark( value );
 | |
| 			},
 | |
| 
 | |
| 			hideItem: function( value ) {
 | |
| 				this._.list.hideItem( value );
 | |
| 			},
 | |
| 
 | |
| 			hideGroup: function( groupTitle ) {
 | |
| 				this._.list.hideGroup( groupTitle );
 | |
| 			},
 | |
| 
 | |
| 			showAll: function() {
 | |
| 				this._.list.showAll();
 | |
| 			},
 | |
| 
 | |
| 			add: function( value, html, text ) {
 | |
| 				this._.items[ value ] = text || value;
 | |
| 				this._.list.add( value, html, text );
 | |
| 			},
 | |
| 
 | |
| 			startGroup: function( title ) {
 | |
| 				this._.list.startGroup( title );
 | |
| 			},
 | |
| 
 | |
| 			commit: function() {
 | |
| 				if ( !this._.committed ) {
 | |
| 					this._.list.commit();
 | |
| 					this._.committed = 1;
 | |
| 					CKEDITOR.ui.fire( 'ready', this );
 | |
| 				}
 | |
| 				this._.committed = 1;
 | |
| 			},
 | |
| 
 | |
| 			setState: function( state ) {
 | |
| 				if ( this._.state == state )
 | |
| 					return;
 | |
| 
 | |
| 				var el = this.document.getById( 'cke_' + this.id );
 | |
| 				el.setState( state, 'cke_combo' );
 | |
| 
 | |
| 				state == CKEDITOR.TRISTATE_DISABLED ?
 | |
| 					el.setAttribute( 'aria-disabled', true ) :
 | |
| 					el.removeAttribute( 'aria-disabled' );
 | |
| 
 | |
| 				this._.state = state;
 | |
| 			},
 | |
| 
 | |
| 			getState: function() {
 | |
| 				return this._.state;
 | |
| 			},
 | |
| 
 | |
| 			enable: function() {
 | |
| 				if ( this._.state == CKEDITOR.TRISTATE_DISABLED )
 | |
| 					this.setState( this._.lastState );
 | |
| 			},
 | |
| 
 | |
| 			disable: function() {
 | |
| 				if ( this._.state != CKEDITOR.TRISTATE_DISABLED ) {
 | |
| 					this._.lastState = this._.state;
 | |
| 					this.setState( CKEDITOR.TRISTATE_DISABLED );
 | |
| 				}
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Represents richCombo handler object.
 | |
| 		 *
 | |
| 		 * @class CKEDITOR.ui.richCombo.handler
 | |
| 		 * @singleton
 | |
| 		 * @extends CKEDITOR.ui.handlerDefinition
 | |
| 		 */
 | |
| 		statics: {
 | |
| 			handler: {
 | |
| 				/**
 | |
| 				 * Transforms a richCombo definition in a {@link CKEDITOR.ui.richCombo} instance.
 | |
| 				 *
 | |
| 				 * @param {Object} definition
 | |
| 				 * @returns {CKEDITOR.ui.richCombo}
 | |
| 				 */
 | |
| 				create: function( definition ) {
 | |
| 					return new CKEDITOR.ui.richCombo( definition );
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	} );
 | |
| 
 | |
| 	/**
 | |
| 	 * @param {String} name
 | |
| 	 * @param {Object} definition
 | |
| 	 * @member CKEDITOR.ui
 | |
| 	 * @todo
 | |
| 	 */
 | |
| 	CKEDITOR.ui.prototype.addRichCombo = function( name, definition ) {
 | |
| 		this.add( name, CKEDITOR.UI_RICHCOMBO, definition );
 | |
| 	};
 | |
| 
 | |
| } )();
 |