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