      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