JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
6cbc3e764cabf03df978ea092bbb5df744d75ea5
[ckeditor.git] / _source / plugins / contextmenu / plugin.js
1 /*\r
2 Copyright (c) 2003-2009, 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 = 'cke_' + CKEDITOR.tools.getNextNumber();\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 \r
40         _ :\r
41         {\r
42                 onMenu : function( offsetParent, corner, offsetX, offsetY )\r
43                 {\r
44                         var menu = this._.menu,\r
45                                 editor = this.editor;\r
46 \r
47                         if ( menu )\r
48                         {\r
49                                 menu.hide();\r
50                                 menu.removeAll();\r
51                         }\r
52                         else\r
53                         {\r
54                                 menu = this._.menu = new CKEDITOR.menu( editor );\r
55                                 menu.onClick = CKEDITOR.tools.bind( function( item )\r
56                                 {\r
57                                         var noUnlock = true;\r
58                                         menu.hide();\r
59 \r
60                                         if ( CKEDITOR.env.ie )\r
61                                                 menu.onEscape();\r
62 \r
63                                         if ( item.onClick )\r
64                                                 item.onClick();\r
65                                         else if ( item.command )\r
66                                                 editor.execCommand( item.command );\r
67 \r
68                                         noUnlock = false;\r
69                                 }, this );\r
70 \r
71                                 menu.onEscape = function()\r
72                                 {\r
73                                         editor.focus();\r
74 \r
75                                         if ( CKEDITOR.env.ie )\r
76                                                 editor.getSelection().unlock( true );\r
77                                 };\r
78                         }\r
79 \r
80                         var listeners = this._.listeners,\r
81                                 includedItems = [];\r
82 \r
83                         var selection = this.editor.getSelection(),\r
84                                 element = selection && selection.getStartElement();\r
85 \r
86                         // Lock the selection in IE, so it can be restored when closing the\r
87                         // menu.\r
88                         if ( CKEDITOR.env.ie )\r
89                                 selection.lock();\r
90 \r
91                         menu.onHide = CKEDITOR.tools.bind( function()\r
92                                 {\r
93                                         menu.onHide = null;\r
94 \r
95                                         if ( CKEDITOR.env.ie )\r
96                                                 editor.getSelection().unlock();\r
97 \r
98                                         this.onHide && this.onHide();\r
99                                 },\r
100                                 this );\r
101 \r
102                         // Call all listeners, filling the list of items to be displayed.\r
103                         for ( var i = 0 ; i < listeners.length ; i++ )\r
104                         {\r
105                                 var listenerItems = listeners[ i ]( element, selection );\r
106 \r
107                                 if ( listenerItems )\r
108                                 {\r
109                                         for ( var itemName in listenerItems )\r
110                                         {\r
111                                                 var item = this.editor.getMenuItem( itemName );\r
112 \r
113                                                 if ( item )\r
114                                                 {\r
115                                                         item.state = listenerItems[ itemName ];\r
116                                                         menu.add( item );\r
117                                                 }\r
118                                         }\r
119                                 }\r
120                         }\r
121 \r
122                         // Don't show context menu with zero items.\r
123                         menu.items.length && menu.show( offsetParent, corner || ( editor.lang.dir == 'rtl' ? 2 : 1 ), offsetX, offsetY );\r
124                 }\r
125         },\r
126 \r
127         proto :\r
128         {\r
129                 addTarget : function( element, nativeContextMenuOnCtrl )\r
130                 {\r
131                         // Opera doesn't support 'contextmenu' event, we have duo approaches employed here:\r
132                         // 1. Inherit the 'button override' hack we introduced in v2 (#4530), while this require the Opera browser\r
133                         //  option 'Allow script to detect context menu/right click events' to be always turned on.\r
134                         // 2. Considering the fact that ctrl/meta key is not been occupied\r
135                         //  for multiple range selecting (like Gecko), we use this key\r
136                         //  combination as a fallback for triggering context-menu. (#4530)\r
137                         if ( CKEDITOR.env.opera )\r
138                         {\r
139                                 var contextMenuOverrideButton;\r
140                                 element.on( 'mousedown', function( evt )\r
141                                 {\r
142                                         evt = evt.data;\r
143                                         if( evt.$.button != 2 )\r
144                                         {\r
145                                                 if ( evt.getKeystroke() == CKEDITOR.CTRL + 1 )\r
146                                                         element.fire( 'contextmenu', evt );\r
147                                                 return;\r
148                                         }\r
149 \r
150                                         if ( nativeContextMenuOnCtrl\r
151                                                  && ( evt.$.ctrlKey || evt.$.metaKey ) )\r
152                                                 return;\r
153 \r
154                                         var target = evt.getTarget();\r
155 \r
156                                         if( !contextMenuOverrideButton )\r
157                                         {\r
158                                                 var ownerDoc =  target.getDocument();\r
159                                                 contextMenuOverrideButton = ownerDoc.createElement( 'input' ) ;\r
160                                                 contextMenuOverrideButton.$.type = 'button' ;\r
161                                                 ownerDoc.getBody().append( contextMenuOverrideButton ) ;\r
162                                         }\r
163 \r
164                                         contextMenuOverrideButton.setAttribute( 'style', 'position:absolute;top:' + ( evt.$.clientY - 2 ) +\r
165                                                 'px;left:' + ( evt.$.clientX - 2 ) +\r
166                                                 'px;width:5px;height:5px;opacity:0.01' );\r
167 \r
168                                 } );\r
169 \r
170                                 element.on( 'mouseup', function ( evt )\r
171                                 {\r
172                                         if ( contextMenuOverrideButton )\r
173                                         {\r
174                                                 contextMenuOverrideButton.remove();\r
175                                                 contextMenuOverrideButton = undefined;\r
176                                                 // Simulate 'contextmenu' event.\r
177                                                 element.fire( 'contextmenu', evt.data );\r
178                                         }\r
179                                 } );\r
180                         }\r
181 \r
182                         element.on( 'contextmenu', function( event )\r
183                                 {\r
184                                         var domEvent = event.data;\r
185 \r
186                                         if ( nativeContextMenuOnCtrl &&\r
187                                              // Safari on Windows always show 'ctrlKey' as true in 'contextmenu' event,\r
188                                                 // which make this property unreliable. (#4826)\r
189                                              ( CKEDITOR.env.webkit ? holdCtrlKey : domEvent.$.ctrlKey || domEvent.$.metaKey ) )\r
190                                                 return;\r
191 \r
192                                         // Cancel the browser context menu.\r
193                                         domEvent.preventDefault();\r
194 \r
195                                         var offsetParent = domEvent.getTarget().getDocument().getDocumentElement(),\r
196                                                 offsetX = domEvent.$.clientX,\r
197                                                 offsetY = domEvent.$.clientY;\r
198 \r
199                                         CKEDITOR.tools.setTimeout( function()\r
200                                                 {\r
201                                                         this._.onMenu( offsetParent, null, offsetX, offsetY );\r
202                                                 },\r
203                                                 0, this );\r
204                                 },\r
205                                 this );\r
206 \r
207                         if( CKEDITOR.env.webkit )\r
208                         {\r
209                                 var holdCtrlKey,\r
210                                         onKeyDown = function( event )\r
211                                         {\r
212                                                 holdCtrlKey = event.data.$.ctrlKey || event.data.$.metaKey;\r
213                                         },\r
214                                         resetOnKeyUp = function()\r
215                                         {\r
216                                                 holdCtrlKey = 0;\r
217                                         };\r
218 \r
219                                 element.on( 'keydown', onKeyDown );\r
220                                 element.on( 'keyup', resetOnKeyUp );\r
221                                 element.on( 'contextmenu', resetOnKeyUp );\r
222                         }\r
223                 },\r
224 \r
225                 addListener : function( listenerFn )\r
226                 {\r
227                         this._.listeners.push( listenerFn );\r
228                 },\r
229 \r
230                 show : function( offsetParent, corner, offsetX, offsetY )\r
231                 {\r
232                         this.editor.focus();\r
233                         this._.onMenu( offsetParent || CKEDITOR.document.getDocumentElement(), corner, offsetX || 0, offsetY || 0 );\r
234                 }\r
235         }\r
236 });\r
237 \r
238 /**\r
239  * Whether to show the browser native context menu when the CTRL or the\r
240  * META (Mac) key is pressed while opening the context menu.\r
241  * @name CKEDITOR.config.browserContextMenuOnCtrl\r
242  * @since 3.0.2\r
243  * @type Boolean\r
244  * @default true\r
245  * @example\r
246  * config.browserContextMenuOnCtrl = false;\r
247  */\r