380 lines
13 KiB
JavaScript
380 lines
13 KiB
JavaScript
|
/**
|
|||
|
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
|||
|
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
|||
|
*/
|
|||
|
|
|||
|
( function() {
|
|||
|
var floatSpaceTpl = CKEDITOR.addTemplate( 'floatcontainer', '<div' +
|
|||
|
' id="cke_{name}"' +
|
|||
|
' class="cke {id} cke_reset_all cke_chrome cke_editor_{name} cke_float cke_{langDir} ' + CKEDITOR.env.cssClass + '"' +
|
|||
|
' dir="{langDir}"' +
|
|||
|
' title="' + ( CKEDITOR.env.gecko ? ' ' : '' ) + '"' +
|
|||
|
' lang="{langCode}"' +
|
|||
|
' role="application"' +
|
|||
|
' style="{style}"' +
|
|||
|
' aria-labelledby="cke_{name}_arialbl"' +
|
|||
|
'>' +
|
|||
|
'<span id="cke_{name}_arialbl" class="cke_voice_label">{voiceLabel}</span>' +
|
|||
|
'<div class="cke_inner">' +
|
|||
|
'<div id="{topId}" class="cke_top" role="presentation">{content}</div>' +
|
|||
|
'</div>' +
|
|||
|
'</div>' ),
|
|||
|
win = CKEDITOR.document.getWindow(),
|
|||
|
pixelate = CKEDITOR.tools.cssLength;
|
|||
|
|
|||
|
CKEDITOR.plugins.add( 'floatingspace', {
|
|||
|
init: function( editor ) {
|
|||
|
// Add listener with lower priority than that in themedui creator.
|
|||
|
// Thereby floatingspace will be created only if themedui wasn't used.
|
|||
|
editor.on( 'loaded', function() {
|
|||
|
attach( this );
|
|||
|
}, null, null, 20 );
|
|||
|
}
|
|||
|
} );
|
|||
|
|
|||
|
function scrollOffset( side ) {
|
|||
|
var pageOffset = side == 'left' ? 'pageXOffset' : 'pageYOffset',
|
|||
|
docScrollOffset = side == 'left' ? 'scrollLeft' : 'scrollTop';
|
|||
|
|
|||
|
return ( pageOffset in win.$ ) ?
|
|||
|
win.$[ pageOffset ]
|
|||
|
:
|
|||
|
CKEDITOR.document.$.documentElement[ docScrollOffset ];
|
|||
|
}
|
|||
|
|
|||
|
function attach( editor ) {
|
|||
|
var config = editor.config,
|
|||
|
|
|||
|
// Get the HTML for the predefined spaces.
|
|||
|
topHtml = editor.fire( 'uiSpace', { space: 'top', html: '' } ).html,
|
|||
|
|
|||
|
// Re-positioning of the space.
|
|||
|
layout = ( function() {
|
|||
|
// Mode indicates the vertical aligning mode.
|
|||
|
var mode, editable,
|
|||
|
spaceRect, editorRect, viewRect, spaceHeight, pageScrollX,
|
|||
|
|
|||
|
// Allow minor adjustments of the float space from custom configs.
|
|||
|
dockedOffsetX = config.floatSpaceDockedOffsetX || 0,
|
|||
|
dockedOffsetY = config.floatSpaceDockedOffsetY || 0,
|
|||
|
pinnedOffsetX = config.floatSpacePinnedOffsetX || 0,
|
|||
|
pinnedOffsetY = config.floatSpacePinnedOffsetY || 0;
|
|||
|
|
|||
|
// Update the float space position.
|
|||
|
function updatePos( pos, prop, val ) {
|
|||
|
floatSpace.setStyle( prop, pixelate( val ) );
|
|||
|
floatSpace.setStyle( 'position', pos );
|
|||
|
}
|
|||
|
|
|||
|
// Change the current mode and update float space position accordingly.
|
|||
|
function changeMode( newMode ) {
|
|||
|
var editorPos = editable.getDocumentPosition();
|
|||
|
|
|||
|
switch ( newMode ) {
|
|||
|
case 'top':
|
|||
|
updatePos( 'absolute', 'top', editorPos.y - spaceHeight - dockedOffsetY );
|
|||
|
break;
|
|||
|
case 'pin':
|
|||
|
updatePos( 'fixed', 'top', pinnedOffsetY );
|
|||
|
break;
|
|||
|
case 'bottom':
|
|||
|
updatePos( 'absolute', 'top', editorPos.y + ( editorRect.height || editorRect.bottom - editorRect.top ) + dockedOffsetY );
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
mode = newMode;
|
|||
|
}
|
|||
|
|
|||
|
return function( evt ) {
|
|||
|
// #10112 Do not fail on editable-less editor.
|
|||
|
if ( !( editable = editor.editable() ) )
|
|||
|
return;
|
|||
|
|
|||
|
// Show up the space on focus gain.
|
|||
|
evt && evt.name == 'focus' && floatSpace.show();
|
|||
|
|
|||
|
// Reset the horizontal position for below measurement.
|
|||
|
floatSpace.removeStyle( 'left' );
|
|||
|
floatSpace.removeStyle( 'right' );
|
|||
|
|
|||
|
// Compute the screen position from the TextRectangle object would
|
|||
|
// be very simple, even though the "width"/"height" property is not
|
|||
|
// available for all, it's safe to figure that out from the rest.
|
|||
|
|
|||
|
// http://help.dottoro.com/ljgupwlp.php
|
|||
|
spaceRect = floatSpace.getClientRect();
|
|||
|
editorRect = editable.getClientRect();
|
|||
|
viewRect = win.getViewPaneSize();
|
|||
|
spaceHeight = spaceRect.height;
|
|||
|
pageScrollX = scrollOffset( 'left' );
|
|||
|
|
|||
|
// We initialize it as pin mode.
|
|||
|
if ( !mode ) {
|
|||
|
mode = 'pin';
|
|||
|
changeMode( 'pin' );
|
|||
|
// Call for a refresh to the actual layout.
|
|||
|
layout( evt );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// +------------------------ Viewport -+ \
|
|||
|
// | | |-> floatSpaceDockedOffsetY
|
|||
|
// | ................................. | /
|
|||
|
// | |
|
|||
|
// | +------ Space -+ |
|
|||
|
// | | | |
|
|||
|
// | +--------------+ |
|
|||
|
// | +------------------ Editor -+ |
|
|||
|
// | | | |
|
|||
|
//
|
|||
|
if ( spaceHeight + dockedOffsetY <= editorRect.top )
|
|||
|
changeMode( 'top' );
|
|||
|
|
|||
|
// +- - - - - - - - - Editor -+
|
|||
|
// | |
|
|||
|
// +------------------------ Viewport -+ \
|
|||
|
// | | | | |-> floatSpacePinnedOffsetY
|
|||
|
// | ................................. | /
|
|||
|
// | +------ Space -+ | |
|
|||
|
// | | | | |
|
|||
|
// | +--------------+ | |
|
|||
|
// | | | |
|
|||
|
// | +---------------------------+ |
|
|||
|
// +-----------------------------------+
|
|||
|
//
|
|||
|
else if ( spaceHeight + dockedOffsetY > viewRect.height - editorRect.bottom )
|
|||
|
changeMode( 'pin' );
|
|||
|
|
|||
|
// +- - - - - - - - - Editor -+
|
|||
|
// | |
|
|||
|
// +------------------------ Viewport -+ \
|
|||
|
// | | | | |-> floatSpacePinnedOffsetY
|
|||
|
// | ................................. | /
|
|||
|
// | | | |
|
|||
|
// | | | |
|
|||
|
// | +---------------------------+ |
|
|||
|
// | +------ Space -+ |
|
|||
|
// | | | |
|
|||
|
// | +--------------+ |
|
|||
|
//
|
|||
|
else
|
|||
|
changeMode( 'bottom' );
|
|||
|
|
|||
|
var mid = viewRect.width / 2,
|
|||
|
alignSide =
|
|||
|
( editorRect.left > 0 && editorRect.right < viewRect.width && editorRect.width > spaceRect.width ) ?
|
|||
|
( editor.config.contentsLangDirection == 'rtl' ? 'right' : 'left' )
|
|||
|
:
|
|||
|
( mid - editorRect.left > editorRect.right - mid ? 'left' : 'right' ),
|
|||
|
offset;
|
|||
|
|
|||
|
// (#9769) If viewport width is less than space width,
|
|||
|
// make sure space never cross the left boundary of the viewport.
|
|||
|
// In other words: top-left corner of the space is always visible.
|
|||
|
if ( spaceRect.width > viewRect.width ) {
|
|||
|
alignSide = 'left';
|
|||
|
offset = 0;
|
|||
|
}
|
|||
|
else {
|
|||
|
if ( alignSide == 'left' ) {
|
|||
|
// If the space rect fits into viewport, align it
|
|||
|
// to the left edge of editor:
|
|||
|
//
|
|||
|
// +------------------------ Viewport -+
|
|||
|
// | |
|
|||
|
// | +------------- Space -+ |
|
|||
|
// | | | |
|
|||
|
// | +---------------------+ |
|
|||
|
// | +------------------ Editor -+ |
|
|||
|
// | | | |
|
|||
|
//
|
|||
|
if ( editorRect.left > 0 )
|
|||
|
offset = editorRect.left;
|
|||
|
|
|||
|
// If the left part of the editor is cut off by the left
|
|||
|
// edge of the viewport, stick the space to the viewport:
|
|||
|
//
|
|||
|
// +------------------------ Viewport -+
|
|||
|
// | |
|
|||
|
// +---------------- Space -+ |
|
|||
|
// | | |
|
|||
|
// +------------------------+ |
|
|||
|
// +----|------------- Editor -+ |
|
|||
|
// | | | |
|
|||
|
//
|
|||
|
else
|
|||
|
offset = 0;
|
|||
|
}
|
|||
|
else {
|
|||
|
// If the space rect fits into viewport, align it
|
|||
|
// to the right edge of editor:
|
|||
|
//
|
|||
|
// +------------------------ Viewport -+
|
|||
|
// | |
|
|||
|
// | +------------- Space -+ |
|
|||
|
// | | | |
|
|||
|
// | +---------------------+ |
|
|||
|
// | +------------------ Editor -+ |
|
|||
|
// | | | |
|
|||
|
//
|
|||
|
if ( editorRect.right < viewRect.width )
|
|||
|
offset = viewRect.width - editorRect.right;
|
|||
|
|
|||
|
// If the right part of the editor is cut off by the right
|
|||
|
// edge of the viewport, stick the space to the viewport:
|
|||
|
//
|
|||
|
// +------------------------ Viewport -+
|
|||
|
// | |
|
|||
|
// | +------------- Space -+
|
|||
|
// | | |
|
|||
|
// | +---------------------+
|
|||
|
// | +-----------------|- Editor -+
|
|||
|
// | | | |
|
|||
|
//
|
|||
|
else
|
|||
|
offset = 0;
|
|||
|
}
|
|||
|
|
|||
|
// (#9769) Finally, stick the space to the opposite side of
|
|||
|
// the viewport when it's cut off horizontally on the left/right
|
|||
|
// side like below.
|
|||
|
//
|
|||
|
// This trick reveals cut off space in some edge cases and
|
|||
|
// hence it improves accessibility.
|
|||
|
//
|
|||
|
// +------------------------ Viewport -+
|
|||
|
// | |
|
|||
|
// | +--------------------|-- Space -+
|
|||
|
// | | | |
|
|||
|
// | +--------------------|----------+
|
|||
|
// | +------- Editor -+ |
|
|||
|
// | | | |
|
|||
|
//
|
|||
|
// becomes:
|
|||
|
//
|
|||
|
// +------------------------ Viewport -+
|
|||
|
// | |
|
|||
|
// | +----------------------- Space -+
|
|||
|
// | | |
|
|||
|
// | +-------------------------------+
|
|||
|
// | +------- Editor -+ |
|
|||
|
// | | | |
|
|||
|
//
|
|||
|
if ( offset + spaceRect.width > viewRect.width ) {
|
|||
|
alignSide = alignSide == 'left' ? 'right' : 'left';
|
|||
|
offset = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Pin mode is fixed, so don't include scroll-x.
|
|||
|
// (#9903) For mode is "top" or "bottom", add opposite scroll-x for right-aligned space.
|
|||
|
var scroll = mode == 'pin' ?
|
|||
|
0
|
|||
|
:
|
|||
|
alignSide == 'left' ? pageScrollX : -pageScrollX;
|
|||
|
|
|||
|
floatSpace.setStyle( alignSide, pixelate( ( mode == 'pin' ? pinnedOffsetX : dockedOffsetX ) + offset + scroll ) );
|
|||
|
};
|
|||
|
} )();
|
|||
|
|
|||
|
if ( topHtml ) {
|
|||
|
var floatSpace = CKEDITOR.document.getBody().append( CKEDITOR.dom.element.createFromHtml( floatSpaceTpl.output( {
|
|||
|
content: topHtml,
|
|||
|
id: editor.id,
|
|||
|
langDir: editor.lang.dir,
|
|||
|
langCode: editor.langCode,
|
|||
|
name: editor.name,
|
|||
|
style: 'display:none;z-index:' + ( config.baseFloatZIndex - 1 ),
|
|||
|
topId: editor.ui.spaceId( 'top' ),
|
|||
|
voiceLabel: editor.lang.editorPanel + ', ' + editor.name
|
|||
|
} ) ) ),
|
|||
|
|
|||
|
// Use event buffers to reduce CPU load when tons of events are fired.
|
|||
|
changeBuffer = CKEDITOR.tools.eventsBuffer( 500, layout ),
|
|||
|
uiBuffer = CKEDITOR.tools.eventsBuffer( 100, layout );
|
|||
|
|
|||
|
// There's no need for the floatSpace to be selectable.
|
|||
|
floatSpace.unselectable();
|
|||
|
|
|||
|
// Prevent clicking on non-buttons area of the space from blurring editor.
|
|||
|
floatSpace.on( 'mousedown', function( evt ) {
|
|||
|
evt = evt.data;
|
|||
|
if ( !evt.getTarget().hasAscendant( 'a', 1 ) )
|
|||
|
evt.preventDefault();
|
|||
|
} );
|
|||
|
|
|||
|
editor.on( 'focus', function( evt ) {
|
|||
|
layout( evt );
|
|||
|
editor.on( 'change', changeBuffer.input );
|
|||
|
win.on( 'scroll', uiBuffer.input );
|
|||
|
win.on( 'resize', uiBuffer.input );
|
|||
|
} );
|
|||
|
|
|||
|
editor.on( 'blur', function() {
|
|||
|
floatSpace.hide();
|
|||
|
editor.removeListener( 'change', changeBuffer.input );
|
|||
|
win.removeListener( 'scroll', uiBuffer.input );
|
|||
|
win.removeListener( 'resize', uiBuffer.input );
|
|||
|
} );
|
|||
|
|
|||
|
editor.on( 'destroy', function() {
|
|||
|
win.removeListener( 'scroll', uiBuffer.input );
|
|||
|
win.removeListener( 'resize', uiBuffer.input );
|
|||
|
floatSpace.clearCustomData();
|
|||
|
floatSpace.remove();
|
|||
|
} );
|
|||
|
|
|||
|
// Handle initial focus.
|
|||
|
if ( editor.focusManager.hasFocus )
|
|||
|
floatSpace.show();
|
|||
|
|
|||
|
// Register this UI space to the focus manager.
|
|||
|
editor.focusManager.add( floatSpace, 1 );
|
|||
|
}
|
|||
|
}
|
|||
|
} )();
|
|||
|
|
|||
|
/**
|
|||
|
* Along with {@link #floatSpaceDockedOffsetY} it defines the
|
|||
|
* amount of offset (in pixels) between float space and the editable left/right
|
|||
|
* boundaries when space element is docked at either side of the editable.
|
|||
|
*
|
|||
|
* config.floatSpaceDockedOffsetX = 10;
|
|||
|
*
|
|||
|
* @cfg {Number} [floatSpaceDockedOffsetX=0]
|
|||
|
* @member CKEDITOR.config
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* Along with {@link #floatSpaceDockedOffsetX} it defines the
|
|||
|
* amount of offset (in pixels) between float space and the editable top/bottom
|
|||
|
* boundaries when space element is docked at either side of the editable.
|
|||
|
*
|
|||
|
* config.floatSpaceDockedOffsetY = 10;
|
|||
|
*
|
|||
|
* @cfg {Number} [floatSpaceDockedOffsetY=0]
|
|||
|
* @member CKEDITOR.config
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* Along with {@link #floatSpacePinnedOffsetY} it defines the
|
|||
|
* amount of offset (in pixels) between float space and the view port boundaries
|
|||
|
* when space element is pinned.
|
|||
|
*
|
|||
|
* config.floatSpacePinnedOffsetX = 20;
|
|||
|
*
|
|||
|
* @cfg {Number} [floatSpacePinnedOffsetX=0]
|
|||
|
* @member CKEDITOR.config
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* Along with {@link #floatSpacePinnedOffsetX} it defines the
|
|||
|
* amount of offset (in pixels) between float space and the view port boundaries
|
|||
|
* when space element is pinned.
|
|||
|
*
|
|||
|
* config.floatSpacePinnedOffsetY = 20;
|
|||
|
*
|
|||
|
* @cfg {Number} [floatSpacePinnedOffsetY=0]
|
|||
|
* @member CKEDITOR.config
|
|||
|
*/
|