plugin.js 14 KB

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