1 /*
  2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
  3 For licensing, see LICENSE.html or http://ckeditor.com/license
  4 */
  5 
  6 CKEDITOR.plugins.add( 'button',
  7 {
  8 	beforeInit : function( editor )
  9 	{
 10 		editor.ui.addHandler( CKEDITOR.UI_BUTTON, CKEDITOR.ui.button.handler );
 11 	}
 12 });
 13 
 14 /**
 15  * Button UI element.
 16  * @constant
 17  * @example
 18  */
 19 CKEDITOR.UI_BUTTON = 1;
 20 
 21 /**
 22  * Represents a button UI element. This class should not be called directly. To
 23  * create new buttons use {@link CKEDITOR.ui.prototype.addButton} instead.
 24  * @constructor
 25  * @param {Object} definition The button definition.
 26  * @example
 27  */
 28 CKEDITOR.ui.button = function( definition )
 29 {
 30 	// Copy all definition properties to this object.
 31 	CKEDITOR.tools.extend( this, definition,
 32 		// Set defaults.
 33 		{
 34 			title		: definition.label,
 35 			className	: definition.className || ( definition.command && 'cke_button_' + definition.command ) || '',
 36 			click		: definition.click || function( editor )
 37 				{
 38 					editor.execCommand( definition.command );
 39 				}
 40 		});
 41 
 42 	this._ = {};
 43 };
 44 
 45 /**
 46  * Transforms a button definition in a {@link CKEDITOR.ui.button} instance.
 47  * @type Object
 48  * @example
 49  */
 50 CKEDITOR.ui.button.handler =
 51 {
 52 	create : function( definition )
 53 	{
 54 		return new CKEDITOR.ui.button( definition );
 55 	}
 56 };
 57 
 58 CKEDITOR.ui.button.prototype =
 59 {
 60 	canGroup : true,
 61 
 62 	/**
 63 	 * Renders the button.
 64 	 * @param {CKEDITOR.editor} editor The editor instance which this button is
 65 	 *		to be used by.
 66 	 * @param {Array} output The output array to which append the HTML relative
 67 	 *		to this button.
 68 	 * @example
 69 	 */
 70 	render : function( editor, output )
 71 	{
 72 		var env = CKEDITOR.env,
 73 			id = this._.id = 'cke_' + CKEDITOR.tools.getNextNumber(),
 74 			classes = '',
 75 			command = this.command, // Get the command name.
 76 			clickFn,
 77 			index;
 78 
 79 		this._.editor = editor;
 80 
 81 		var instance =
 82 		{
 83 			id : id,
 84 			button : this,
 85 			editor : editor,
 86 			focus : function()
 87 			{
 88 				var element = CKEDITOR.document.getById( id );
 89 				element.focus();
 90 			},
 91 			execute : function()
 92 			{
 93 				this.button.click( editor );
 94 			}
 95 		};
 96 
 97 		instance.clickFn = clickFn = CKEDITOR.tools.addFunction( instance.execute, instance );
 98 
 99 		instance.index = index = CKEDITOR.ui.button._.instances.push( instance ) - 1;
100 
101 		if ( this.modes )
102 		{
103 			editor.on( 'mode', function()
104 				{
105 					this.setState( this.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
106 				}, this);
107 		}
108 		else if ( command )
109 		{
110 			// Get the command instance.
111 			command = editor.getCommand( command );
112 
113 			if ( command )
114 			{
115 				command.on( 'state', function()
116 					{
117 						this.setState( command.state );
118 					}, this);
119 
120 				classes += 'cke_' + (
121 					command.state == CKEDITOR.TRISTATE_ON ? 'on' :
122 					command.state == CKEDITOR.TRISTATE_DISABLED ? 'disabled' :
123 					'off' );
124 			}
125 		}
126 
127 		if ( !command )
128 			classes	+= 'cke_off';
129 
130 		if ( this.className )
131 			classes += ' ' + this.className;
132 
133 		output.push(
134 			'<span class="cke_button">',
135 			'<a id="', id, '"' +
136 				' class="', classes, '"',
137 				env.gecko && env.version >= 10900 && !env.hc  ? '' : '" href="javascript:void(\''+ ( this.title || '' ).replace( "'"+ '' )+ '\')"',
138 				' title="', this.title, '"' +
139 				' tabindex="-1"' +
140 				' hidefocus="true"' +
141 			    ' role="button"' +
142 				' aria-labelledby="' + id + '_label"' +
143 				( this.hasArrow ?  ' aria-haspopup="true"' : '' ) );
144 
145 		// Some browsers don't cancel key events in the keydown but in the
146 		// keypress.
147 		// TODO: Check if really needed for Gecko+Mac.
148 		if ( env.opera || ( env.gecko && env.mac ) )
149 		{
150 			output.push(
151 				' onkeypress="return false;"' );
152 		}
153 
154 		// With Firefox, we need to force the button to redraw, otherwise it
155 		// will remain in the focus state.
156 		if ( env.gecko )
157 		{
158 			output.push(
159 				' onblur="this.style.cssText = this.style.cssText;"' );
160 		}
161 
162 		output.push(
163 				' onkeydown="return CKEDITOR.ui.button._.keydown(', index, ', event);"' +
164 				' onfocus="return CKEDITOR.ui.button._.focus(', index, ', event);"' +
165 				' onclick="CKEDITOR.tools.callFunction(', clickFn, ', this); return false;">' +
166 					'<span class="cke_icon"' );
167 
168 		if ( this.icon )
169 		{
170 			var offset = ( this.iconOffset || 0 ) * -16;
171 			output.push( ' style="background-image:url(', CKEDITOR.getUrl( this.icon ), ');background-position:0 ' + offset + 'px;"' );
172 		}
173 
174 		output.push(
175 					'> </span>' +
176 					'<span id="', id, '_label" class="cke_label">', this.label, '</span>' );
177 
178 		if ( this.hasArrow )
179 		{
180 			output.push(
181 					'<span class="cke_buttonarrow">'
182 					// BLACK DOWN-POINTING TRIANGLE
183 					+ ( CKEDITOR.env.hc ? '▼' : ' ' )
184 					+ '</span>' );
185 		}
186 
187 		output.push(
188 			'</a>',
189 			'</span>' );
190 
191 		if ( this.onRender )
192 			this.onRender();
193 
194 		return instance;
195 	},
196 
197 	setState : function( state )
198 	{
199 		if ( this._.state == state )
200 			return false;
201 
202 		this._.state = state;
203 
204 		var element = CKEDITOR.document.getById( this._.id );
205 
206 		if ( element )
207 		{
208 			element.setState( state );
209 			state == CKEDITOR.TRISTATE_DISABLED ?
210 				element.setAttribute( 'aria-disabled', true ) :
211 				element.removeAttribute( 'aria-disabled' );
212 
213 			state == CKEDITOR.TRISTATE_ON ?
214 				element.setAttribute( 'aria-pressed', true ) :
215 				element.removeAttribute( 'aria-pressed' );
216 
217 			return true;
218 		}
219 		else
220 			return false;
221 	}
222 };
223 
224 /**
225  * Handles a button click.
226  * @private
227  */
228 CKEDITOR.ui.button._ =
229 {
230 	instances : [],
231 
232 	keydown : function( index, ev )
233 	{
234 		var instance = CKEDITOR.ui.button._.instances[ index ];
235 
236 		if ( instance.onkey )
237 		{
238 			ev = new CKEDITOR.dom.event( ev );
239 			return ( instance.onkey( instance, ev.getKeystroke() ) !== false );
240 		}
241 	},
242 
243 	focus : function( index, ev )
244 	{
245 		var instance = CKEDITOR.ui.button._.instances[ index ],
246 			retVal;
247 
248 		if ( instance.onfocus )
249 			retVal = ( instance.onfocus( instance, new CKEDITOR.dom.event( ev ) ) !== false );
250 
251 		// FF2: prevent focus event been bubbled up to editor container, which caused unexpected editor focus.
252 		if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 )
253 			ev.preventBubble();
254 		return retVal;
255 	}
256 };
257 
258 /**
259  * Adds a button definition to the UI elements list.
260  * @param {String} The button name.
261  * @param {Object} The button definition.
262  * @example
263  * editorInstance.ui.addButton( 'MyBold',
264  *     {
265  *         label : 'My Bold',
266  *         command : 'bold'
267  *     });
268  */
269 CKEDITOR.ui.prototype.addButton = function( name, definition )
270 {
271 	this.add( name, CKEDITOR.UI_BUTTON, definition );
272 };
273 
274 CKEDITOR.on( 'reset', function()
275 	{
276 		CKEDITOR.ui.button._.instances = [];
277 	});
278