/*\r
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
{\r
$ : function( editor )\r
{\r
- this.id = 'cke_' + CKEDITOR.tools.getNextNumber();\r
+ this.id = CKEDITOR.tools.getNextId();\r
this.editor = editor;\r
this._.listeners = [];\r
this._.functionId = CKEDITOR.tools.addFunction( function( commandName )\r
editor.execCommand( commandName );\r
},\r
this);\r
+\r
+ this.definition =\r
+ {\r
+ panel:\r
+ {\r
+ className : editor.skinClass + ' cke_contextmenu',\r
+ attributes :\r
+ {\r
+ 'aria-label' : editor.lang.contextmenu.options\r
+ }\r
+ }\r
+ };\r
},\r
\r
_ :\r
}\r
else\r
{\r
- menu = this._.menu = new CKEDITOR.menu( editor );\r
+ menu = this._.menu = new CKEDITOR.menu( editor, this.definition );\r
menu.onClick = CKEDITOR.tools.bind( function( item )\r
{\r
- var noUnlock = true;\r
menu.hide();\r
\r
- if ( CKEDITOR.env.ie )\r
- menu.onEscape();\r
-\r
if ( item.onClick )\r
item.onClick();\r
else if ( item.command )\r
editor.execCommand( item.command );\r
\r
- noUnlock = false;\r
}, this );\r
\r
- menu.onEscape = function()\r
+ menu.onEscape = function( keystroke )\r
{\r
- editor.focus();\r
-\r
- if ( CKEDITOR.env.ie )\r
- editor.getSelection().unlock( true );\r
+ var parent = this.parent;\r
+ // 1. If it's sub-menu, restore the last focused item\r
+ // of upper level menu.\r
+ // 2. In case of a top-menu, close it.\r
+ if ( parent )\r
+ {\r
+ parent._.panel.hideChild();\r
+ // Restore parent block item focus.\r
+ var parentBlock = parent._.panel._.panel._.currentBlock,\r
+ parentFocusIndex = parentBlock._.focusIndex;\r
+ parentBlock._.markItem( parentFocusIndex );\r
+ }\r
+ else if ( keystroke == 27 )\r
+ {\r
+ this.hide();\r
+ editor.focus();\r
+ }\r
+ return false;\r
};\r
}\r
\r
var selection = this.editor.getSelection(),\r
element = selection && selection.getStartElement();\r
\r
- // Lock the selection in IE, so it can be restored when closing the\r
- // menu.\r
- if ( CKEDITOR.env.ie )\r
- selection.lock();\r
-\r
menu.onHide = CKEDITOR.tools.bind( function()\r
{\r
menu.onHide = null;\r
\r
if ( CKEDITOR.env.ie )\r
- editor.getSelection().unlock();\r
+ {\r
+ var selection = editor.getSelection();\r
+ selection && selection.unlock();\r
+ }\r
\r
this.onHide && this.onHide();\r
},\r
}\r
}\r
\r
- menu.show( offsetParent, corner || ( editor.lang.dir == 'rtl' ? 2 : 1 ), offsetX, offsetY );\r
+ // Don't show context menu with zero items.\r
+ menu.items.length && menu.show( offsetParent, corner || ( editor.lang.dir == 'rtl' ? 2 : 1 ), offsetX, offsetY );\r
}\r
},\r
\r
proto :\r
{\r
- addTarget : function( element )\r
+ addTarget : function( element, nativeContextMenuOnCtrl )\r
{\r
+\r
+ // For browsers (Opera <=10a) that doesn't support 'contextmenu' event, we have duo approaches employed here:\r
+ // 1. Inherit the 'button override' hack we introduced in v2 (#4530) (In Opera browser, this require the\r
+ // option 'Allow script to detect context menu/right click events' to be always turned on).\r
+ // 2. Considering the fact that ctrl/meta key is not been occupied\r
+ // for multiple range selecting (like Gecko), we use this key\r
+ // combination as a fallback for triggering context-menu. (#4530)\r
+ if ( CKEDITOR.env.opera && !( 'oncontextmenu' in document.body ) )\r
+ {\r
+ var contextMenuOverrideButton;\r
+ element.on( 'mousedown', function( evt )\r
+ {\r
+ evt = evt.data;\r
+ if ( evt.$.button != 2 )\r
+ {\r
+ if ( evt.getKeystroke() == CKEDITOR.CTRL + 1 )\r
+ element.fire( 'contextmenu', evt );\r
+ return;\r
+ }\r
+\r
+ if ( nativeContextMenuOnCtrl\r
+ && ( CKEDITOR.env.mac ? evt.$.metaKey : evt.$.ctrlKey ) )\r
+ return;\r
+\r
+ var target = evt.getTarget();\r
+\r
+ if ( !contextMenuOverrideButton )\r
+ {\r
+ var ownerDoc = target.getDocument();\r
+ contextMenuOverrideButton = ownerDoc.createElement( 'input' ) ;\r
+ contextMenuOverrideButton.$.type = 'button' ;\r
+ ownerDoc.getBody().append( contextMenuOverrideButton ) ;\r
+ }\r
+\r
+ contextMenuOverrideButton.setAttribute( 'style', 'position:absolute;top:' + ( evt.$.clientY - 2 ) +\r
+ 'px;left:' + ( evt.$.clientX - 2 ) +\r
+ 'px;width:5px;height:5px;opacity:0.01' );\r
+\r
+ } );\r
+\r
+ element.on( 'mouseup', function ( evt )\r
+ {\r
+ if ( contextMenuOverrideButton )\r
+ {\r
+ contextMenuOverrideButton.remove();\r
+ contextMenuOverrideButton = undefined;\r
+ // Simulate 'contextmenu' event.\r
+ element.fire( 'contextmenu', evt.data );\r
+ }\r
+ } );\r
+ }\r
+\r
element.on( 'contextmenu', function( event )\r
{\r
var domEvent = event.data;\r
\r
+ if ( nativeContextMenuOnCtrl &&\r
+ // Safari on Windows always show 'ctrlKey' as true in 'contextmenu' event,\r
+ // which make this property unreliable. (#4826)\r
+ ( CKEDITOR.env.webkit ? holdCtrlKey : ( CKEDITOR.env.mac ? domEvent.$.metaKey : domEvent.$.ctrlKey ) ) )\r
+ return;\r
+\r
+\r
// Cancel the browser context menu.\r
domEvent.preventDefault();\r
\r
\r
CKEDITOR.tools.setTimeout( function()\r
{\r
- this._.onMenu( offsetParent, null, offsetX, offsetY );\r
+ this.show( offsetParent, null, offsetX, offsetY );\r
},\r
0, this );\r
},\r
this );\r
+\r
+ if ( CKEDITOR.env.opera )\r
+ {\r
+ // 'contextmenu' event triggered by Windows menu key is unpreventable,\r
+ // cancel the key event itself. (#6534)\r
+ element.on( 'keypress' , function ( evt )\r
+ {\r
+ var domEvent = evt.data;\r
+\r
+ if ( domEvent.$.keyCode === 0 )\r
+ domEvent.preventDefault();\r
+ });\r
+ }\r
+\r
+ if ( CKEDITOR.env.webkit )\r
+ {\r
+ var holdCtrlKey,\r
+ onKeyDown = function( event )\r
+ {\r
+ holdCtrlKey = CKEDITOR.env.mac ? event.data.$.metaKey : event.data.$.ctrlKey ;\r
+ },\r
+ resetOnKeyUp = function()\r
+ {\r
+ holdCtrlKey = 0;\r
+ };\r
+\r
+ element.on( 'keydown', onKeyDown );\r
+ element.on( 'keyup', resetOnKeyUp );\r
+ element.on( 'contextmenu', resetOnKeyUp );\r
+ }\r
},\r
\r
addListener : function( listenerFn )\r
show : function( offsetParent, corner, offsetX, offsetY )\r
{\r
this.editor.focus();\r
+\r
+ // Selection will be unavailable after context menu shows up\r
+ // in IE, lock it now.\r
+ if ( CKEDITOR.env.ie )\r
+ {\r
+ var selection = this.editor.getSelection();\r
+ selection && selection.lock();\r
+ }\r
+\r
this._.onMenu( offsetParent || CKEDITOR.document.getDocumentElement(), corner, offsetX || 0, offsetY || 0 );\r
}\r
}\r
});\r
\r
-// Fix the "contextmenu" event for DOM elements.\r
-// We may do this if we identify browsers that don't support the context meny\r
-// event on element directly. Leaving here for reference.\r
-//if ( <specific browsers> )\r
-//{\r
-// CKEDITOR.dom.element.prototype.on = CKEDITOR.tools.override( CKEDITOR.dom.element.prototype.on, function( originalOn )\r
-// {\r
-// return function( eventName )\r
-// {\r
-// if ( eventName != 'contextmenu' )\r
-// return originalOn.apply( this, arguments );\r
-//\r
-// // TODO : Implement the fix.\r
-// };\r
-// });\r
-//}\r
+/**\r
+ * Whether to show the browser native context menu when the CTRL or the\r
+ * META (Mac) key is pressed while opening the context menu.\r
+ * @name CKEDITOR.config.browserContextMenuOnCtrl\r
+ * @since 3.0.2\r
+ * @type Boolean\r
+ * @default true\r
+ * @example\r
+ * config.browserContextMenuOnCtrl = false;\r
+ */\r