]>
Commit | Line | Data |
---|---|---|
3332bebe | 1 | /**\r |
317f8f8f | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.\r |
3332bebe IB |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license\r |
4 | */\r | |
5 | \r | |
6 | ( function() {\r | |
7 | var win = CKEDITOR.document.getWindow(),\r | |
8 | pixelate = CKEDITOR.tools.cssLength;\r | |
9 | \r | |
10 | CKEDITOR.plugins.add( 'floatingspace', {\r | |
11 | init: function( editor ) {\r | |
12 | // Add listener with lower priority than that in themedui creator.\r | |
13 | // Thereby floatingspace will be created only if themedui wasn't used.\r | |
14 | editor.on( 'loaded', function() {\r | |
15 | attach( this );\r | |
16 | }, null, null, 20 );\r | |
17 | }\r | |
18 | } );\r | |
19 | \r | |
20 | function scrollOffset( side ) {\r | |
21 | var pageOffset = side == 'left' ? 'pageXOffset' : 'pageYOffset',\r | |
22 | docScrollOffset = side == 'left' ? 'scrollLeft' : 'scrollTop';\r | |
23 | \r | |
24 | return ( pageOffset in win.$ ) ? win.$[ pageOffset ] : CKEDITOR.document.$.documentElement[ docScrollOffset ];\r | |
25 | }\r | |
26 | \r | |
27 | function attach( editor ) {\r | |
28 | var config = editor.config,\r | |
29 | \r | |
30 | // Get the HTML for the predefined spaces.\r | |
31 | topHtml = editor.fire( 'uiSpace', { space: 'top', html: '' } ).html,\r | |
32 | \r | |
33 | // Re-positioning of the space.\r | |
34 | layout = ( function() {\r | |
35 | // Mode indicates the vertical aligning mode.\r | |
36 | var mode, editable,\r | |
37 | spaceRect, editorRect, viewRect, spaceHeight, pageScrollX,\r | |
38 | \r | |
39 | // Allow minor adjustments of the float space from custom configs.\r | |
40 | dockedOffsetX = config.floatSpaceDockedOffsetX || 0,\r | |
41 | dockedOffsetY = config.floatSpaceDockedOffsetY || 0,\r | |
42 | pinnedOffsetX = config.floatSpacePinnedOffsetX || 0,\r | |
43 | pinnedOffsetY = config.floatSpacePinnedOffsetY || 0;\r | |
44 | \r | |
45 | // Update the float space position.\r | |
46 | function updatePos( pos, prop, val ) {\r | |
47 | floatSpace.setStyle( prop, pixelate( val ) );\r | |
48 | floatSpace.setStyle( 'position', pos );\r | |
49 | }\r | |
50 | \r | |
51 | // Change the current mode and update float space position accordingly.\r | |
52 | function changeMode( newMode ) {\r | |
53 | var editorPos = editable.getDocumentPosition();\r | |
54 | \r | |
55 | switch ( newMode ) {\r | |
56 | case 'top':\r | |
57 | updatePos( 'absolute', 'top', editorPos.y - spaceHeight - dockedOffsetY );\r | |
58 | break;\r | |
59 | case 'pin':\r | |
60 | updatePos( 'fixed', 'top', pinnedOffsetY );\r | |
61 | break;\r | |
62 | case 'bottom':\r | |
63 | updatePos( 'absolute', 'top', editorPos.y + ( editorRect.height || editorRect.bottom - editorRect.top ) + dockedOffsetY );\r | |
64 | break;\r | |
65 | }\r | |
66 | \r | |
67 | mode = newMode;\r | |
68 | }\r | |
69 | \r | |
70 | return function( evt ) {\r | |
317f8f8f | 71 | // http://dev.ckeditor.com/ticket/10112 Do not fail on editable-less editor.\r |
3332bebe IB |
72 | if ( !( editable = editor.editable() ) )\r |
73 | return;\r | |
74 | \r | |
75 | var show = ( evt && evt.name == 'focus' );\r | |
76 | \r | |
77 | // Show up the space on focus gain.\r | |
78 | if ( show ) {\r | |
79 | floatSpace.show();\r | |
80 | }\r | |
81 | \r | |
82 | editor.fire( 'floatingSpaceLayout', { show: show } );\r | |
83 | \r | |
84 | // Reset the horizontal position for below measurement.\r | |
85 | floatSpace.removeStyle( 'left' );\r | |
86 | floatSpace.removeStyle( 'right' );\r | |
87 | \r | |
88 | // Compute the screen position from the TextRectangle object would\r | |
89 | // be very simple, even though the "width"/"height" property is not\r | |
90 | // available for all, it's safe to figure that out from the rest.\r | |
91 | \r | |
92 | // http://help.dottoro.com/ljgupwlp.php\r | |
93 | spaceRect = floatSpace.getClientRect();\r | |
94 | editorRect = editable.getClientRect();\r | |
95 | viewRect = win.getViewPaneSize();\r | |
96 | spaceHeight = spaceRect.height;\r | |
97 | pageScrollX = scrollOffset( 'left' );\r | |
98 | \r | |
99 | // We initialize it as pin mode.\r | |
100 | if ( !mode ) {\r | |
101 | mode = 'pin';\r | |
102 | changeMode( 'pin' );\r | |
103 | // Call for a refresh to the actual layout.\r | |
104 | layout( evt );\r | |
105 | return;\r | |
106 | }\r | |
107 | \r | |
108 | // +------------------------ Viewport -+ \\r | |
109 | // | | |-> floatSpaceDockedOffsetY\r | |
110 | // | ................................. | /\r | |
111 | // | |\r | |
112 | // | +------ Space -+ |\r | |
113 | // | | | |\r | |
114 | // | +--------------+ |\r | |
115 | // | +------------------ Editor -+ |\r | |
116 | // | | | |\r | |
117 | //\r | |
118 | if ( spaceHeight + dockedOffsetY <= editorRect.top )\r | |
119 | changeMode( 'top' );\r | |
120 | \r | |
121 | // +- - - - - - - - - Editor -+\r | |
122 | // | |\r | |
123 | // +------------------------ Viewport -+ \\r | |
124 | // | | | | |-> floatSpacePinnedOffsetY\r | |
125 | // | ................................. | /\r | |
126 | // | +------ Space -+ | |\r | |
127 | // | | | | |\r | |
128 | // | +--------------+ | |\r | |
129 | // | | | |\r | |
130 | // | +---------------------------+ |\r | |
131 | // +-----------------------------------+\r | |
132 | //\r | |
133 | else if ( spaceHeight + dockedOffsetY > viewRect.height - editorRect.bottom )\r | |
134 | changeMode( 'pin' );\r | |
135 | \r | |
136 | // +- - - - - - - - - Editor -+\r | |
137 | // | |\r | |
138 | // +------------------------ Viewport -+ \\r | |
139 | // | | | | |-> floatSpacePinnedOffsetY\r | |
140 | // | ................................. | /\r | |
141 | // | | | |\r | |
142 | // | | | |\r | |
143 | // | +---------------------------+ |\r | |
144 | // | +------ Space -+ |\r | |
145 | // | | | |\r | |
146 | // | +--------------+ |\r | |
147 | //\r | |
148 | else\r | |
149 | changeMode( 'bottom' );\r | |
150 | \r | |
151 | var mid = viewRect.width / 2,\r | |
152 | alignSide, offset;\r | |
153 | \r | |
154 | if ( config.floatSpacePreferRight ) {\r | |
155 | alignSide = 'right';\r | |
156 | } else if ( editorRect.left > 0 && editorRect.right < viewRect.width && editorRect.width > spaceRect.width ) {\r | |
157 | alignSide = config.contentsLangDirection == 'rtl' ? 'right' : 'left';\r | |
158 | } else {\r | |
159 | alignSide = mid - editorRect.left > editorRect.right - mid ? 'left' : 'right';\r | |
160 | }\r | |
161 | \r | |
317f8f8f | 162 | // (http://dev.ckeditor.com/ticket/9769) If viewport width is less than space width,\r |
3332bebe IB |
163 | // make sure space never cross the left boundary of the viewport.\r |
164 | // In other words: top-left corner of the space is always visible.\r | |
165 | if ( spaceRect.width > viewRect.width ) {\r | |
166 | alignSide = 'left';\r | |
167 | offset = 0;\r | |
168 | }\r | |
169 | else {\r | |
170 | if ( alignSide == 'left' ) {\r | |
171 | // If the space rect fits into viewport, align it\r | |
172 | // to the left edge of editor:\r | |
173 | //\r | |
174 | // +------------------------ Viewport -+\r | |
175 | // | |\r | |
176 | // | +------------- Space -+ |\r | |
177 | // | | | |\r | |
178 | // | +---------------------+ |\r | |
179 | // | +------------------ Editor -+ |\r | |
180 | // | | | |\r | |
181 | //\r | |
182 | if ( editorRect.left > 0 )\r | |
183 | offset = editorRect.left;\r | |
184 | \r | |
185 | // If the left part of the editor is cut off by the left\r | |
186 | // edge of the viewport, stick the space to the viewport:\r | |
187 | //\r | |
188 | // +------------------------ Viewport -+\r | |
189 | // | |\r | |
190 | // +---------------- Space -+ |\r | |
191 | // | | |\r | |
192 | // +------------------------+ |\r | |
193 | // +----|------------- Editor -+ |\r | |
194 | // | | | |\r | |
195 | //\r | |
196 | else\r | |
197 | offset = 0;\r | |
198 | }\r | |
199 | else {\r | |
200 | // If the space rect fits into viewport, align it\r | |
201 | // to the right edge of editor:\r | |
202 | //\r | |
203 | // +------------------------ Viewport -+\r | |
204 | // | |\r | |
205 | // | +------------- Space -+ |\r | |
206 | // | | | |\r | |
207 | // | +---------------------+ |\r | |
208 | // | +------------------ Editor -+ |\r | |
209 | // | | | |\r | |
210 | //\r | |
211 | if ( editorRect.right < viewRect.width )\r | |
212 | offset = viewRect.width - editorRect.right;\r | |
213 | \r | |
214 | // If the right part of the editor is cut off by the right\r | |
215 | // edge of the viewport, stick the space to the viewport:\r | |
216 | //\r | |
217 | // +------------------------ Viewport -+\r | |
218 | // | |\r | |
219 | // | +------------- Space -+\r | |
220 | // | | |\r | |
221 | // | +---------------------+\r | |
222 | // | +-----------------|- Editor -+\r | |
223 | // | | | |\r | |
224 | //\r | |
225 | else\r | |
226 | offset = 0;\r | |
227 | }\r | |
228 | \r | |
317f8f8f | 229 | // (http://dev.ckeditor.com/ticket/9769) Finally, stick the space to the opposite side of\r |
3332bebe IB |
230 | // the viewport when it's cut off horizontally on the left/right\r |
231 | // side like below.\r | |
232 | //\r | |
233 | // This trick reveals cut off space in some edge cases and\r | |
234 | // hence it improves accessibility.\r | |
235 | //\r | |
236 | // +------------------------ Viewport -+\r | |
237 | // | |\r | |
238 | // | +--------------------|-- Space -+\r | |
239 | // | | | |\r | |
240 | // | +--------------------|----------+\r | |
241 | // | +------- Editor -+ |\r | |
242 | // | | | |\r | |
243 | //\r | |
244 | // becomes:\r | |
245 | //\r | |
246 | // +------------------------ Viewport -+\r | |
247 | // | |\r | |
248 | // | +----------------------- Space -+\r | |
249 | // | | |\r | |
250 | // | +-------------------------------+\r | |
251 | // | +------- Editor -+ |\r | |
252 | // | | | |\r | |
253 | //\r | |
254 | if ( offset + spaceRect.width > viewRect.width ) {\r | |
255 | alignSide = alignSide == 'left' ? 'right' : 'left';\r | |
256 | offset = 0;\r | |
257 | }\r | |
258 | }\r | |
259 | \r | |
260 | // Pin mode is fixed, so don't include scroll-x.\r | |
317f8f8f | 261 | // (http://dev.ckeditor.com/ticket/9903) For mode is "top" or "bottom", add opposite scroll-x for right-aligned space.\r |
3332bebe IB |
262 | var scroll = mode == 'pin' ? 0 : alignSide == 'left' ? pageScrollX : -pageScrollX;\r |
263 | \r | |
264 | floatSpace.setStyle( alignSide, pixelate( ( mode == 'pin' ? pinnedOffsetX : dockedOffsetX ) + offset + scroll ) );\r | |
265 | };\r | |
266 | } )();\r | |
267 | \r | |
268 | if ( topHtml ) {\r | |
269 | var floatSpaceTpl = new CKEDITOR.template(\r | |
270 | '<div' +\r | |
271 | ' id="cke_{name}"' +\r | |
272 | ' class="cke {id} cke_reset_all cke_chrome cke_editor_{name} cke_float cke_{langDir} ' + CKEDITOR.env.cssClass + '"' +\r | |
273 | ' dir="{langDir}"' +\r | |
274 | ' title="' + ( CKEDITOR.env.gecko ? ' ' : '' ) + '"' +\r | |
275 | ' lang="{langCode}"' +\r | |
276 | ' role="application"' +\r | |
277 | ' style="{style}"' +\r | |
278 | ( editor.title ? ' aria-labelledby="cke_{name}_arialbl"' : ' ' ) +\r | |
279 | '>' +\r | |
280 | ( editor.title ? '<span id="cke_{name}_arialbl" class="cke_voice_label">{voiceLabel}</span>' : ' ' ) +\r | |
281 | '<div class="cke_inner">' +\r | |
282 | '<div id="{topId}" class="cke_top" role="presentation">{content}</div>' +\r | |
283 | '</div>' +\r | |
284 | '</div>' ),\r | |
285 | floatSpace = CKEDITOR.document.getBody().append( CKEDITOR.dom.element.createFromHtml( floatSpaceTpl.output( {\r | |
286 | content: topHtml,\r | |
287 | id: editor.id,\r | |
288 | langDir: editor.lang.dir,\r | |
289 | langCode: editor.langCode,\r | |
290 | name: editor.name,\r | |
291 | style: 'display:none;z-index:' + ( config.baseFloatZIndex - 1 ),\r | |
292 | topId: editor.ui.spaceId( 'top' ),\r | |
293 | voiceLabel: editor.title\r | |
294 | } ) ) ),\r | |
295 | \r | |
296 | // Use event buffers to reduce CPU load when tons of events are fired.\r | |
297 | changeBuffer = CKEDITOR.tools.eventsBuffer( 500, layout ),\r | |
298 | uiBuffer = CKEDITOR.tools.eventsBuffer( 100, layout );\r | |
299 | \r | |
300 | // There's no need for the floatSpace to be selectable.\r | |
301 | floatSpace.unselectable();\r | |
302 | \r | |
303 | // Prevent clicking on non-buttons area of the space from blurring editor.\r | |
304 | floatSpace.on( 'mousedown', function( evt ) {\r | |
305 | evt = evt.data;\r | |
306 | if ( !evt.getTarget().hasAscendant( 'a', 1 ) )\r | |
307 | evt.preventDefault();\r | |
308 | } );\r | |
309 | \r | |
310 | editor.on( 'focus', function( evt ) {\r | |
311 | layout( evt );\r | |
312 | editor.on( 'change', changeBuffer.input );\r | |
313 | win.on( 'scroll', uiBuffer.input );\r | |
314 | win.on( 'resize', uiBuffer.input );\r | |
315 | } );\r | |
316 | \r | |
317 | editor.on( 'blur', function() {\r | |
318 | floatSpace.hide();\r | |
319 | editor.removeListener( 'change', changeBuffer.input );\r | |
320 | win.removeListener( 'scroll', uiBuffer.input );\r | |
321 | win.removeListener( 'resize', uiBuffer.input );\r | |
322 | } );\r | |
323 | \r | |
324 | editor.on( 'destroy', function() {\r | |
325 | win.removeListener( 'scroll', uiBuffer.input );\r | |
326 | win.removeListener( 'resize', uiBuffer.input );\r | |
327 | floatSpace.clearCustomData();\r | |
328 | floatSpace.remove();\r | |
329 | } );\r | |
330 | \r | |
331 | // Handle initial focus.\r | |
332 | if ( editor.focusManager.hasFocus )\r | |
333 | floatSpace.show();\r | |
334 | \r | |
335 | // Register this UI space to the focus manager.\r | |
336 | editor.focusManager.add( floatSpace, 1 );\r | |
337 | }\r | |
338 | }\r | |
339 | } )();\r | |
340 | \r | |
341 | /**\r | |
342 | * Along with {@link #floatSpaceDockedOffsetY} it defines the\r | |
343 | * amount of offset (in pixels) between the float space and the editable left/right\r | |
344 | * boundaries when the space element is docked on either side of the editable.\r | |
345 | *\r | |
346 | * config.floatSpaceDockedOffsetX = 10;\r | |
347 | *\r | |
348 | * @cfg {Number} [floatSpaceDockedOffsetX=0]\r | |
349 | * @member CKEDITOR.config\r | |
350 | */\r | |
351 | \r | |
352 | /**\r | |
353 | * Along with {@link #floatSpaceDockedOffsetX} it defines the\r | |
354 | * amount of offset (in pixels) between the float space and the editable top/bottom\r | |
355 | * boundaries when the space element is docked on either side of the editable.\r | |
356 | *\r | |
357 | * config.floatSpaceDockedOffsetY = 10;\r | |
358 | *\r | |
359 | * @cfg {Number} [floatSpaceDockedOffsetY=0]\r | |
360 | * @member CKEDITOR.config\r | |
361 | */\r | |
362 | \r | |
363 | /**\r | |
364 | * Along with {@link #floatSpacePinnedOffsetY} it defines the\r | |
365 | * amount of offset (in pixels) between the float space and the viewport boundaries\r | |
366 | * when the space element is pinned.\r | |
367 | *\r | |
368 | * config.floatSpacePinnedOffsetX = 20;\r | |
369 | *\r | |
370 | * @cfg {Number} [floatSpacePinnedOffsetX=0]\r | |
371 | * @member CKEDITOR.config\r | |
372 | */\r | |
373 | \r | |
374 | /**\r | |
375 | * Along with {@link #floatSpacePinnedOffsetX} it defines the\r | |
376 | * amount of offset (in pixels) between the float space and the viewport boundaries\r | |
377 | * when the space element is pinned.\r | |
378 | *\r | |
379 | * config.floatSpacePinnedOffsetY = 20;\r | |
380 | *\r | |
381 | * @cfg {Number} [floatSpacePinnedOffsetY=0]\r | |
382 | * @member CKEDITOR.config\r | |
383 | */\r | |
384 | \r | |
385 | /**\r | |
386 | * Indicates that the float space should be aligned to the right side\r | |
387 | * of the editable area rather than to the left (if possible).\r | |
388 | *\r | |
389 | * config.floatSpacePreferRight = true;\r | |
390 | *\r | |
391 | * @since 4.5\r | |
392 | * @cfg {Boolean} [floatSpacePreferRight=false]\r | |
393 | * @member CKEDITOR.config\r | |
394 | */\r | |
395 | \r | |
396 | /**\r | |
397 | * Fired when the viewport or editor parameters change and the floating space needs to check and\r | |
398 | * eventually update its position and dimensions.\r | |
399 | *\r | |
400 | * @since 4.5\r | |
401 | * @event floatingSpaceLayout\r | |
402 | * @member CKEDITOR.editor\r | |
403 | * @param {CKEDITOR.editor} editor The editor instance.\r | |
404 | * @param data\r | |
405 | * @param {Boolean} data.show True if the float space should show up as a result of this event.\r | |
406 | */\r |