--- /dev/null
+/*\r
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
+For licensing, see LICENSE.html or http://ckeditor.com/license\r
+*/\r
+\r
+CKEDITOR.plugins.add( 'panel',\r
+{\r
+ beforeInit : function( editor )\r
+ {\r
+ editor.ui.addHandler( CKEDITOR.UI_PANEL, CKEDITOR.ui.panel.handler );\r
+ }\r
+});\r
+\r
+/**\r
+ * Panel UI element.\r
+ * @constant\r
+ * @example\r
+ */\r
+CKEDITOR.UI_PANEL = 2;\r
+\r
+CKEDITOR.ui.panel = function( document, definition )\r
+{\r
+ // Copy all definition properties to this object.\r
+ if ( definition )\r
+ CKEDITOR.tools.extend( this, definition );\r
+\r
+ // Set defaults.\r
+ CKEDITOR.tools.extend( this,\r
+ {\r
+ className : '',\r
+ css : []\r
+ });\r
+\r
+ this.id = CKEDITOR.tools.getNextId();\r
+ this.document = document;\r
+\r
+ this._ =\r
+ {\r
+ blocks : {}\r
+ };\r
+};\r
+\r
+/**\r
+ * Transforms a rich combo definition in a {@link CKEDITOR.ui.richCombo}\r
+ * instance.\r
+ * @type Object\r
+ * @example\r
+ */\r
+CKEDITOR.ui.panel.handler =\r
+{\r
+ create : function( definition )\r
+ {\r
+ return new CKEDITOR.ui.panel( definition );\r
+ }\r
+};\r
+\r
+CKEDITOR.ui.panel.prototype =\r
+{\r
+ renderHtml : function( editor )\r
+ {\r
+ var output = [];\r
+ this.render( editor, output );\r
+ return output.join( '' );\r
+ },\r
+\r
+ /**\r
+ * Renders the combo.\r
+ * @param {CKEDITOR.editor} editor The editor instance which this button is\r
+ * to be used by.\r
+ * @param {Array} output The output array to which append the HTML relative\r
+ * to this button.\r
+ * @example\r
+ */\r
+ render : function( editor, output )\r
+ {\r
+ var id = this.id;\r
+\r
+ output.push(\r
+ '<div class="', editor.skinClass ,'"' +\r
+ ' lang="', editor.langCode, '"' +\r
+ ' role="presentation"' +\r
+ // iframe loading need sometime, keep the panel hidden(#4186).\r
+ ' style="display:none;z-index:' + ( editor.config.baseFloatZIndex + 1 ) + '">' +\r
+ '<div' +\r
+ ' id=', id,\r
+ ' dir=', editor.lang.dir,\r
+ ' role="presentation"' +\r
+ ' class="cke_panel cke_', editor.lang.dir );\r
+\r
+ if ( this.className )\r
+ output.push( ' ', this.className );\r
+\r
+ output.push(\r
+ '">' );\r
+\r
+ if ( this.forceIFrame || this.css.length )\r
+ {\r
+ output.push(\r
+ '<iframe id="', id, '_frame"' +\r
+ ' frameborder="0"' +\r
+ ' role="application" src="javascript:void(' );\r
+\r
+ output.push(\r
+ // Support for custom document.domain in IE.\r
+ CKEDITOR.env.isCustomDomain() ?\r
+ '(function(){' +\r
+ 'document.open();' +\r
+ 'document.domain=\'' + document.domain + '\';' +\r
+ 'document.close();' +\r
+ '})()'\r
+ :\r
+ '0' );\r
+\r
+ output.push(\r
+ ')"></iframe>' );\r
+ }\r
+\r
+ output.push(\r
+ '</div>' +\r
+ '</div>' );\r
+\r
+ return id;\r
+ },\r
+\r
+ getHolderElement : function()\r
+ {\r
+ var holder = this._.holder;\r
+\r
+ if ( !holder )\r
+ {\r
+ if ( this.forceIFrame || this.css.length )\r
+ {\r
+ var iframe = this.document.getById( this.id + '_frame' ),\r
+ parentDiv = iframe.getParent(),\r
+ dir = parentDiv.getAttribute( 'dir' ),\r
+ className = parentDiv.getParent().getAttribute( 'class' ),\r
+ langCode = parentDiv.getParent().getAttribute( 'lang' ),\r
+ doc = iframe.getFrameDocument();\r
+\r
+ var onLoad = CKEDITOR.tools.addFunction( CKEDITOR.tools.bind( function( ev )\r
+ {\r
+ this.isLoaded = true;\r
+ if ( this.onLoad )\r
+ this.onLoad();\r
+ }, this ) );\r
+\r
+ var data =\r
+ '<!DOCTYPE html>' +\r
+ '<html dir="' + dir + '" class="' + className + '_container" lang="' + langCode + '">' +\r
+ '<head>' +\r
+ '<style>.' + className + '_container{visibility:hidden}</style>' +\r
+ '</head>' +\r
+ '<body class="cke_' + dir + ' cke_panel_frame ' + CKEDITOR.env.cssClass + '" style="margin:0;padding:0"' +\r
+ ' onload="( window.CKEDITOR || window.parent.CKEDITOR ).tools.callFunction(' + onLoad + ');"></body>' +\r
+ // It looks strange, but for FF2, the styles must go\r
+ // after <body>, so it (body) becames immediatelly\r
+ // available. (#3031)\r
+ CKEDITOR.tools.buildStyleHtml( this.css ) +\r
+ '<\/html>';\r
+\r
+ doc.write( data );\r
+\r
+ var win = doc.getWindow();\r
+\r
+ // Register the CKEDITOR global.\r
+ win.$.CKEDITOR = CKEDITOR;\r
+\r
+ // Arrow keys for scrolling is only preventable with 'keypress' event in Opera (#4534).\r
+ doc.on( 'key' + ( CKEDITOR.env.opera? 'press':'down' ), function( evt )\r
+ {\r
+ var keystroke = evt.data.getKeystroke(),\r
+ dir = this.document.getById( this.id ).getAttribute( 'dir' );\r
+\r
+ // Delegate key processing to block.\r
+ if ( this._.onKeyDown && this._.onKeyDown( keystroke ) === false )\r
+ {\r
+ evt.data.preventDefault();\r
+ return;\r
+ }\r
+\r
+ // ESC/ARROW-LEFT(ltr) OR ARROW-RIGHT(rtl)\r
+ if ( keystroke == 27 || keystroke == ( dir == 'rtl' ? 39 : 37 ) )\r
+ {\r
+ if ( this.onEscape && this.onEscape( keystroke ) === false )\r
+ evt.data.preventDefault();\r
+ }\r
+ },\r
+ this );\r
+\r
+ holder = doc.getBody();\r
+ holder.unselectable();\r
+ CKEDITOR.env.air && CKEDITOR.tools.callFunction( onLoad );\r
+ }\r
+ else\r
+ holder = this.document.getById( this.id );\r
+\r
+ this._.holder = holder;\r
+ }\r
+\r
+ return holder;\r
+ },\r
+\r
+ addBlock : function( name, block )\r
+ {\r
+ block = this._.blocks[ name ] = block instanceof CKEDITOR.ui.panel.block ? block\r
+ : new CKEDITOR.ui.panel.block( this.getHolderElement(), block );\r
+\r
+ if ( !this._.currentBlock )\r
+ this.showBlock( name );\r
+\r
+ return block;\r
+ },\r
+\r
+ getBlock : function( name )\r
+ {\r
+ return this._.blocks[ name ];\r
+ },\r
+\r
+ showBlock : function( name )\r
+ {\r
+ var blocks = this._.blocks,\r
+ block = blocks[ name ],\r
+ current = this._.currentBlock,\r
+ holder = this.forceIFrame ?\r
+ this.document.getById( this.id + '_frame' )\r
+ : this._.holder;\r
+\r
+ // Disable context menu for block panel.\r
+ holder.getParent().getParent().disableContextMenu();\r
+\r
+ if ( current )\r
+ {\r
+ // Clean up the current block's effects on holder.\r
+ holder.removeAttributes( current.attributes );\r
+ current.hide();\r
+ }\r
+\r
+ this._.currentBlock = block;\r
+\r
+ holder.setAttributes( block.attributes );\r
+ CKEDITOR.fire( 'ariaWidget', holder );\r
+\r
+ // Reset the focus index, so it will always go into the first one.\r
+ block._.focusIndex = -1;\r
+\r
+ this._.onKeyDown = block.onKeyDown && CKEDITOR.tools.bind( block.onKeyDown, block );\r
+\r
+ block.onMark = function( item )\r
+ {\r
+ holder.setAttribute( 'aria-activedescendant', item.getId() + '_option' );\r
+ };\r
+\r
+ block.onUnmark = function()\r
+ {\r
+ holder.removeAttribute( 'aria-activedescendant' );\r
+ };\r
+\r
+ block.show();\r
+\r
+ return block;\r
+ },\r
+\r
+ destroy : function()\r
+ {\r
+ this.element && this.element.remove();\r
+ }\r
+};\r
+\r
+CKEDITOR.ui.panel.block = CKEDITOR.tools.createClass(\r
+{\r
+ $ : function( blockHolder, blockDefinition )\r
+ {\r
+ this.element = blockHolder.append(\r
+ blockHolder.getDocument().createElement( 'div',\r
+ {\r
+ attributes :\r
+ {\r
+ 'tabIndex' : -1,\r
+ 'class' : 'cke_panel_block',\r
+ 'role' : 'presentation'\r
+ },\r
+ styles :\r
+ {\r
+ display : 'none'\r
+ }\r
+ }) );\r
+\r
+ // Copy all definition properties to this object.\r
+ if ( blockDefinition )\r
+ CKEDITOR.tools.extend( this, blockDefinition );\r
+\r
+ if ( !this.attributes.title )\r
+ this.attributes.title = this.attributes[ 'aria-label' ];\r
+\r
+ this.keys = {};\r
+\r
+ this._.focusIndex = -1;\r
+\r
+ // Disable context menu for panels.\r
+ this.element.disableContextMenu();\r
+ },\r
+\r
+ _ : {\r
+\r
+ /**\r
+ * Mark the item specified by the index as current activated.\r
+ */\r
+ markItem: function( index )\r
+ {\r
+ if ( index == -1 )\r
+ return;\r
+ var links = this.element.getElementsByTag( 'a' );\r
+ var item = links.getItem( this._.focusIndex = index );\r
+\r
+ // Safari need focus on the iframe window first(#3389), but we need\r
+ // lock the blur to avoid hiding the panel.\r
+ if ( CKEDITOR.env.webkit || CKEDITOR.env.opera )\r
+ item.getDocument().getWindow().focus();\r
+ item.focus();\r
+\r
+ this.onMark && this.onMark( item );\r
+ }\r
+ },\r
+\r
+ proto :\r
+ {\r
+ show : function()\r
+ {\r
+ this.element.setStyle( 'display', '' );\r
+ },\r
+\r
+ hide : function()\r
+ {\r
+ if ( !this.onHide || this.onHide.call( this ) !== true )\r
+ this.element.setStyle( 'display', 'none' );\r
+ },\r
+\r
+ onKeyDown : function( keystroke )\r
+ {\r
+ var keyAction = this.keys[ keystroke ];\r
+ switch ( keyAction )\r
+ {\r
+ // Move forward.\r
+ case 'next' :\r
+ var index = this._.focusIndex,\r
+ links = this.element.getElementsByTag( 'a' ),\r
+ link;\r
+\r
+ while ( ( link = links.getItem( ++index ) ) )\r
+ {\r
+ // Move the focus only if the element is marked with\r
+ // the _cke_focus and it it's visible (check if it has\r
+ // width).\r
+ if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth )\r
+ {\r
+ this._.focusIndex = index;\r
+ link.focus();\r
+ break;\r
+ }\r
+ }\r
+ return false;\r
+\r
+ // Move backward.\r
+ case 'prev' :\r
+ index = this._.focusIndex;\r
+ links = this.element.getElementsByTag( 'a' );\r
+\r
+ while ( index > 0 && ( link = links.getItem( --index ) ) )\r
+ {\r
+ // Move the focus only if the element is marked with\r
+ // the _cke_focus and it it's visible (check if it has\r
+ // width).\r
+ if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth )\r
+ {\r
+ this._.focusIndex = index;\r
+ link.focus();\r
+ break;\r
+ }\r
+ }\r
+ return false;\r
+\r
+ case 'click' :\r
+ index = this._.focusIndex;\r
+ link = index >= 0 && this.element.getElementsByTag( 'a' ).getItem( index );\r
+\r
+ if ( link )\r
+ link.$.click ? link.$.click() : link.$.onclick();\r
+\r
+ return false;\r
+ }\r
+\r
+ return true;\r
+ }\r
+ }\r
+});\r
+\r
+/**\r
+ * Fired when a panel is added to the document\r
+ * @name CKEDITOR#ariaWidget\r
+ * @event\r
+ * @param {Object} holder The element wrapping the panel\r
+ */\r