JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.4.2
[ckeditor.git] / _source / plugins / contextmenu / plugin.js
1 /*\r
2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
3 For licensing, see LICENSE.html or http://ckeditor.com/license\r
4 */\r
5 \r
6 CKEDITOR.plugins.add( 'contextmenu',\r
7 {\r
8         requires : [ 'menu' ],\r
9 \r
10         beforeInit : function( editor )\r
11         {\r
12                 editor.contextMenu = new CKEDITOR.plugins.contextMenu( editor );\r
13 \r
14                 editor.addCommand( 'contextMenu',\r
15                         {\r
16                                 exec : function()\r
17                                         {\r
18                                                 editor.contextMenu.show( editor.document.getBody() );\r
19                                         }\r
20                         });\r
21         }\r
22 });\r
23 \r
24 CKEDITOR.plugins.contextMenu = CKEDITOR.tools.createClass(\r
25 {\r
26         $ : function( editor )\r
27         {\r
28                 this.id = CKEDITOR.tools.getNextId();\r
29                 this.editor = editor;\r
30                 this._.listeners = [];\r
31                 this._.functionId = CKEDITOR.tools.addFunction( function( commandName )\r
32                         {\r
33                                 this._.panel.hide();\r
34                                 editor.focus();\r
35                                 editor.execCommand( commandName );\r
36                         },\r
37                         this);\r
38 \r
39                 this.definition =\r
40                 {\r
41                         panel:\r
42                         {\r
43                                 className : editor.skinClass + ' cke_contextmenu',\r
44                                 attributes :\r
45                                 {\r
46                                         'aria-label' : editor.lang.contextmenu.options\r
47                                 }\r
48                         }\r
49                 };\r
50         },\r
51 \r
52         _ :\r
53         {\r
54                 onMenu : function( offsetParent, corner, offsetX, offsetY )\r
55                 {\r
56                         var menu = this._.menu,\r
57                                 editor = this.editor;\r
58 \r
59                         if ( menu )\r
60                         {\r
61                                 menu.hide();\r
62                                 menu.removeAll();\r
63                         }\r
64                         else\r
65                         {\r
66                                 menu = this._.menu = new CKEDITOR.menu( editor, this.definition );\r
67                                 menu.onClick = CKEDITOR.tools.bind( function( item )\r
68                                 {\r
69                                         menu.hide();\r
70 \r
71                                         if ( item.onClick )\r
72                                                 item.onClick();\r
73                                         else if ( item.command )\r
74                                                 editor.execCommand( item.command );\r
75 \r
76                                 }, this );\r
77 \r
78                                 menu.onEscape = function( keystroke )\r
79                                 {\r
80                                         var parent = this.parent;\r
81                                         // 1. If it's sub-menu, restore the last focused item\r
82                                         // of upper level menu.\r
83                                         // 2. In case of a top-menu, close it.\r
84                                         if ( parent )\r
85                                         {\r
86                                                 parent._.panel.hideChild();\r
87                                                 // Restore parent block item focus.\r
88                                                 var parentBlock = parent._.panel._.panel._.currentBlock,\r
89                                                         parentFocusIndex =  parentBlock._.focusIndex;\r
90                                                 parentBlock._.markItem( parentFocusIndex );\r
91                                         }\r
92                                         else if ( keystroke == 27 )\r
93                                         {\r
94                                                 this.hide();\r
95                                                 editor.focus();\r
96                                         }\r
97                                         return false;\r
98                                 };\r
99                         }\r
100 \r
101                         var listeners = this._.listeners,\r
102                                 includedItems = [];\r
103 \r
104                         var selection = this.editor.getSelection(),\r
105                                 element = selection && selection.getStartElement();\r
106 \r
107                         menu.onHide = CKEDITOR.tools.bind( function()\r
108                                 {\r
109                                         menu.onHide = null;\r
110 \r
111                                         if ( CKEDITOR.env.ie )\r
112                                         {\r
113                                                 var selection = editor.getSelection();\r
114                                                 selection && selection.unlock();\r
115                                         }\r
116 \r
117                                         this.onHide && this.onHide();\r
118                                 },\r
119                                 this );\r
120 \r
121                         // Call all listeners, filling the list of items to be displayed.\r
122                         for ( var i = 0 ; i < listeners.length ; i++ )\r
123                         {\r
124                                 var listenerItems = listeners[ i ]( element, selection );\r
125 \r
126                                 if ( listenerItems )\r
127                                 {\r
128                                         for ( var itemName in listenerItems )\r
129                                         {\r
130                                                 var item = this.editor.getMenuItem( itemName );\r
131 \r
132                                                 if ( item )\r
133                                                 {\r
134                                                         item.state = listenerItems[ itemName ];\r
135                                                         menu.add( item );\r
136                                                 }\r
137                                         }\r
138                                 }\r
139                         }\r
140 \r
141                         // Don't show context menu with zero items.\r
142                         menu.items.length && menu.show( offsetParent, corner || ( editor.lang.dir == 'rtl' ? 2 : 1 ), offsetX, offsetY );\r
143                 }\r
144         },\r
145 \r
146         proto :\r
147         {\r
148                 addTarget : function( element, nativeContextMenuOnCtrl )\r
149                 {\r
150 \r
151                         // For browsers (Opera <=10a) that doesn't  support 'contextmenu' event, we have duo approaches employed here:\r
152                         // 1. Inherit the 'button override' hack we introduced in v2 (#4530) (In Opera browser, this require the\r
153                         //  option 'Allow script to detect context menu/right click events' to be always turned on).\r
154                         // 2. Considering the fact that ctrl/meta key is not been occupied\r
155                         //  for multiple range selecting (like Gecko), we use this key\r
156                         //  combination as a fallback for triggering context-menu. (#4530)\r
157                         if ( CKEDITOR.env.opera && !( 'oncontextmenu' in document.body ) )\r
158                         {\r
159                                 var contextMenuOverrideButton;\r
160                                 element.on( 'mousedown', function( evt )\r
161                                 {\r
162                                         evt = evt.data;\r
163                                         if ( evt.$.button != 2 )\r
164                                         {\r
165                                                 if ( evt.getKeystroke() == CKEDITOR.CTRL + 1 )\r
166                                                         element.fire( 'contextmenu', evt );\r
167                                                 return;\r
168                                         }\r
169 \r
170                                         if ( nativeContextMenuOnCtrl\r
171                                                  && ( CKEDITOR.env.mac ? evt.$.metaKey : evt.$.ctrlKey ) )\r
172                                                 return;\r
173 \r
174                                         var target = evt.getTarget();\r
175 \r
176                                         if ( !contextMenuOverrideButton )\r
177                                         {\r
178                                                 var ownerDoc =  target.getDocument();\r
179                                                 contextMenuOverrideButton = ownerDoc.createElement( 'input' ) ;\r
180                                                 contextMenuOverrideButton.$.type = 'button' ;\r
181                                                 ownerDoc.getBody().append( contextMenuOverrideButton ) ;\r
182                                         }\r
183 \r
184                                         contextMenuOverrideButton.setAttribute( 'style', 'position:absolute;top:' + ( evt.$.clientY - 2 ) +\r
185                                                 'px;left:' + ( evt.$.clientX - 2 ) +\r
186                                                 'px;width:5px;height:5px;opacity:0.01' );\r
187 \r
188                                 } );\r
189 \r
190                                 element.on( 'mouseup', function ( evt )\r
191                                 {\r
192                                         if ( contextMenuOverrideButton )\r
193                                         {\r
194                                                 contextMenuOverrideButton.remove();\r
195                                                 contextMenuOverrideButton = undefined;\r
196                                                 // Simulate 'contextmenu' event.\r
197                                                 element.fire( 'contextmenu', evt.data );\r
198                                         }\r
199                                 } );\r
200                         }\r
201 \r
202                         element.on( 'contextmenu', function( event )\r
203                                 {\r
204                                         var domEvent = event.data;\r
205 \r
206                                         if ( nativeContextMenuOnCtrl &&\r
207                                              // Safari on Windows always show 'ctrlKey' as true in 'contextmenu' event,\r
208                                                 // which make this property unreliable. (#4826)\r
209                                              ( CKEDITOR.env.webkit ? holdCtrlKey : ( CKEDITOR.env.mac ? domEvent.$.metaKey : domEvent.$.ctrlKey ) ) )\r
210                                                 return;\r
211 \r
212 \r
213                                         // Cancel the browser context menu.\r
214                                         domEvent.preventDefault();\r
215 \r
216                                         var offsetParent = domEvent.getTarget().getDocument().getDocumentElement(),\r
217                                                 offsetX = domEvent.$.clientX,\r
218                                                 offsetY = domEvent.$.clientY;\r
219 \r
220                                         CKEDITOR.tools.setTimeout( function()\r
221                                                 {\r
222                                                         this.show( offsetParent, null, offsetX, offsetY );\r
223                                                 },\r
224                                                 0, this );\r
225                                 },\r
226                                 this );\r
227 \r
228                         if ( CKEDITOR.env.opera )\r
229                         {\r
230                                 // 'contextmenu' event triggered by Windows menu key is unpreventable,\r
231                                 // cancel the key event itself. (#6534)\r
232                                 element.on( 'keypress' , function ( evt )\r
233                                 {\r
234                                         var domEvent = evt.data;\r
235 \r
236                                         if ( domEvent.$.keyCode === 0 )\r
237                                                 domEvent.preventDefault();\r
238                                 });\r
239                         }\r
240 \r
241                         if ( CKEDITOR.env.webkit )\r
242                         {\r
243                                 var holdCtrlKey,\r
244                                         onKeyDown = function( event )\r
245                                         {\r
246                                                 holdCtrlKey = CKEDITOR.env.mac ? event.data.$.metaKey : event.data.$.ctrlKey ;\r
247                                         },\r
248                                         resetOnKeyUp = function()\r
249                                         {\r
250                                                 holdCtrlKey = 0;\r
251                                         };\r
252 \r
253                                 element.on( 'keydown', onKeyDown );\r
254                                 element.on( 'keyup', resetOnKeyUp );\r
255                                 element.on( 'contextmenu', resetOnKeyUp );\r
256                         }\r
257                 },\r
258 \r
259                 addListener : function( listenerFn )\r
260                 {\r
261                         this._.listeners.push( listenerFn );\r
262                 },\r
263 \r
264                 show : function( offsetParent, corner, offsetX, offsetY )\r
265                 {\r
266                         this.editor.focus();\r
267 \r
268                         // Selection will be unavailable after context menu shows up\r
269                         // in IE, lock it now.\r
270                         if ( CKEDITOR.env.ie )\r
271                         {\r
272                                 var selection = this.editor.getSelection();\r
273                                 selection && selection.lock();\r
274                         }\r
275 \r
276                         this._.onMenu( offsetParent || CKEDITOR.document.getDocumentElement(), corner, offsetX || 0, offsetY || 0 );\r
277                 }\r
278         }\r
279 });\r
280 \r
281 /**\r
282  * Whether to show the browser native context menu when the CTRL or the\r
283  * META (Mac) key is pressed while opening the context menu.\r
284  * @name CKEDITOR.config.browserContextMenuOnCtrl\r
285  * @since 3.0.2\r
286  * @type Boolean\r
287  * @default true\r
288  * @example\r
289  * config.browserContextMenuOnCtrl = false;\r
290  */\r