1 /*
  2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
  3 For licensing, see LICENSE.html or http://ckeditor.com/license
  4 */
  5
  6 CKEDITOR.plugins.add( 'panel',
  7 {
  8 	beforeInit : function( editor )
  9 	{
 10 		editor.ui.addHandler( CKEDITOR.UI_PANEL, CKEDITOR.ui.panel.handler );
 11 	}
 12 });
 13
 14 /**
 15  * Panel UI element.
 16  * @constant
 17  * @example
 18  */
 19 CKEDITOR.UI_PANEL = 2;
 20
 21 CKEDITOR.ui.panel = function( document, definition )
 22 {
 23 	// Copy all definition properties to this object.
 24 	if ( definition )
 25 		CKEDITOR.tools.extend( this, definition );
 26
 27 	// Set defaults.
 28 	CKEDITOR.tools.extend( this,
 29 		{
 30 			className : '',
 31 			css : []
 32 		});
 33
 34 	this.id = CKEDITOR.tools.getNextNumber();
 35 	this.document = document;
 36
 37 	this._ =
 38 	{
 39 		blocks : {}
 40 	};
 41 };
 42
 43 /**
 44  * Transforms a rich combo definition in a {@link CKEDITOR.ui.richCombo}
 45  * instance.
 46  * @type Object
 47  * @example
 48  */
 49 CKEDITOR.ui.panel.handler =
 50 {
 51 	create : function( definition )
 52 	{
 53 		return new CKEDITOR.ui.panel( definition );
 54 	}
 55 };
 56
 57 CKEDITOR.ui.panel.prototype =
 58 {
 59 	renderHtml : function( editor )
 60 	{
 61 		var output = [];
 62 		this.render( editor, output );
 63 		return output.join( '' );
 64 	},
 65
 66 	/**
 67 	 * Renders the combo.
 68 	 * @param {CKEDITOR.editor} editor The editor instance which this button is
 69 	 *		to be used by.
 70 	 * @param {Array} output The output array to which append the HTML relative
 71 	 *		to this button.
 72 	 * @example
 73 	 */
 74 	render : function( editor, output )
 75 	{
 76 		var id = 'cke_' + this.id;
 77
 78 		output.push(
 79 			'<div class="', editor.skinClass ,'"' +
 80 				' lang="', editor.langCode, '"' +
 81 				' style="z-index:' + ( editor.config.baseFloatZIndex + 1 ) + '">' +
 82 				'<div' +
 83 					' id=', id,
 84 					' dir=', editor.lang.dir,
 85 					' class="cke_panel cke_', editor.lang.dir );
 86
 87 		if ( this.className )
 88 			output.push( ' ', this.className );
 89
 90 		output.push(
 91 				'">' );
 92
 93 		if ( this.forceIFrame || this.css.length )
 94 		{
 95 			output.push(
 96 						'<iframe id="', id, '_frame"' +
 97 							' frameborder="0"' +
 98 							' src="javascript:void(' );
 99
100 			output.push(
101 							// Support for custom document.domain in IE.
102 							CKEDITOR.env.isCustomDomain() ?
103 								'(function(){' +
104 									'document.open();' +
105 									'document.domain=\'' + document.domain + '\';' +
106 									'document.close();' +
107 								'})()'
108 							:
109 								'0' );
110
111 			output.push(
112 						')"></iframe>' );
113 		}
114
115 		output.push(
116 				'</div>' +
117 			'</div>' );
118
119 		return id;
120 	},
121
122 	getHolderElement : function()
123 	{
124 		var holder = this._.holder;
125
126 		if ( !holder )
127 		{
128 			if ( this.forceIFrame || this.css.length )
129 			{
130 				var iframe = this.document.getById( 'cke_' + this.id + '_frame' ),
131 					parentDiv = iframe.getParent(),
132 					dir = parentDiv.getAttribute( 'dir' ),
133 					className = parentDiv.getParent().getAttribute( 'class' ),
134 					langCode = parentDiv.getParent().getAttribute( 'lang' ),
135 					doc = iframe.getFrameDocument();
136
137 				// Initialize the IFRAME document body.
138 				doc.$.open();
139
140 				// Support for custom document.domain in IE.
141 				if ( CKEDITOR.env.isCustomDomain() )
142 					doc.$.domain = document.domain;
143
144 				doc.$.write(
145 					'<!DOCTYPE html>' +
146 					'<html dir="' + dir + '" class="' + className + '_container" lang="' + langCode + '">' +
147 						'<head>' +
148 							'<style>.' + className + '_container{visibility:hidden}</style>' +
149 						'</head>' +
150 						'<body class="cke_' + dir + ' cke_panel_frame ' + CKEDITOR.env.cssClass + '" style="margin:0;padding:0">' +
151 						'</body>' +
152 						// It looks strange, but for FF2, the styles must go
153 						// after <body>, so it (body) becames immediatelly
154 						// available. (#3031)
155 						'<link type="text/css" rel=stylesheet href="' + this.css.join( '"><link type="text/css" rel="stylesheet" href="' ) + '">' +
156 					'<\/html>' );
157 				doc.$.close();
158
159 				var win = doc.getWindow();
160
161 				// Register the CKEDITOR global.
162 				win.$.CKEDITOR = CKEDITOR;
163
164 				win.on( 'load', function( ev )
165 					{
166 						this.isLoaded = true;
167 						if ( this.onLoad )
168 							this.onLoad();
169 					},
170 					this);
171
172 				doc.on( 'keydown', function( evt )
173 					{
174 						var keystroke = evt.data.getKeystroke();
175
176 						// Delegate key processing to block.
177 						if ( this._.onKeyDown && this._.onKeyDown( keystroke ) === false )
178 						{
179 							evt.data.preventDefault();
180 							return;
181 						}
182
183 						if ( keystroke == 27 )		// ESC
184 							this.onEscape && this.onEscape();
185 					},
186 					this );
187
188 				holder = doc.getBody();
189 			}
190 			else
191 				holder = this.document.getById( 'cke_' + this.id );
192
193 			this._.holder = holder;
194 		}
195
196 		return holder;
197 	},
198
199 	addBlock : function( name, block )
200 	{
201 		block = this._.blocks[ name ] = block || new CKEDITOR.ui.panel.block( this.getHolderElement() );
202
203 		if ( !this._.currentBlock )
204 			this.showBlock( name );
205
206 		return block;
207 	},
208
209 	getBlock : function( name )
210 	{
211 		return this._.blocks[ name ];
212 	},
213
214 	showBlock : function( name )
215 	{
216 		var blocks = this._.blocks,
217 			block = blocks[ name ],
218 			current = this._.currentBlock;
219
220 		if ( current )
221 			current.hide();
222
223 		this._.currentBlock = block;
224
225 		// Reset the focus index, so it will always go into the first one.
226 		block._.focusIndex = -1;
227
228 		this._.onKeyDown = block.onKeyDown && CKEDITOR.tools.bind( block.onKeyDown, block );
229
230 		block.show();
231
232 		return block;
233 	}
234 };
235
236 CKEDITOR.ui.panel.block = CKEDITOR.tools.createClass(
237 {
238 	$ : function( blockHolder )
239 	{
240 		this.element = blockHolder.append(
241 			blockHolder.getDocument().createElement( 'div',
242 				{
243 					attributes :
244 					{
245 						'class' : 'cke_panel_block'
246 					},
247 					styles :
248 					{
249 						display : 'none'
250 					}
251 				}) );
252
253 		this.keys = {};
254
255 		this._.focusIndex = -1;
256 	},
257
258 	_ : {},
259
260 	proto :
261 	{
262 		show : function()
263 		{
264 			this.element.setStyle( 'display', '' );
265 		},
266
267 		hide : function()
268 		{
269 			if ( !this.onHide || this.onHide.call( this )  !== true )
270 				this.element.setStyle( 'display', 'none' );
271 		},
272
273 		onKeyDown : function( keystroke )
274 		{
275 			var keyAction = this.keys[ keystroke ];
276 			switch ( keyAction )
277 			{
278 				// Move forward.
279 				case 'next' :
280 					var index = this._.focusIndex,
281 						links = this.element.getElementsByTag( 'a' ),
282 						link;
283
284 					while ( ( link = links.getItem( ++index ) ) )
285 					{
286 						// Move the focus only if the element is marked with
287 						// the _cke_focus and it it's visible (check if it has
288 						// width).
289 						if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth )
290 						{
291 							this._.focusIndex = index;
292 							link.focus();
293 							break;
294 						}
295 					}
296 					return false;
297
298 				// Move backward.
299 				case 'prev' :
300 					index = this._.focusIndex;
301 					links = this.element.getElementsByTag( 'a' );
302
303 					while ( index > 0 && ( link = links.getItem( --index ) ) )
304 					{
305 						// Move the focus only if the element is marked with
306 						// the _cke_focus and it it's visible (check if it has
307 						// width).
308 						if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth )
309 						{
310 							this._.focusIndex = index;
311 							link.focus();
312 							break;
313 						}
314 					}
315 					return false;
316
317 				case 'click' :
318 					index = this._.focusIndex;
319 					link = index >= 0 && this.element.getElementsByTag( 'a' ).getItem( index );
320
321 					if ( link )
322 						link.$.click ? link.$.click() : link.$.onclick();
323
324 					return false;
325 			}
326
327 			return true;
328 		}
329 	}
330 });
331