diff --git a/plugins/magicline/plugin.js b/plugins/magicline/plugin.js
index 83820499a98..386d446ea36 100644
--- a/plugins/magicline/plugin.js
+++ b/plugins/magicline/plugin.js
@@ -36,7 +36,7 @@
holdDistance: 0 | triggerOffset * ( config.magicline_holdDistance || 0.5 ),
boxColor: config.magicline_color || '#ff0000',
rtl: config.contentsLangDirection == 'rtl',
- triggers: config.magicline_everywhere ? CKEDITOR.dtd.$block : { table:1,hr:1,div:1,ul:1,ol:1,dl:1,form:1,blockquote:1 }
+ triggers: config.magicline_everywhere ? dtd.$block : { table:1,hr:1,div:1,ul:1,ol:1,dl:1,form:1,blockquote:1 }
},
scrollTimeout, hideTimeout, checkMouseTimeoutPending, checkMouseTimeout, checkMouseTimer;
@@ -72,11 +72,6 @@
&& !isFlowBreaker( node ); // -> Node can be neither floated nor positioned nor aligned.
};
- // %REMOVE_START%
- // Editor commands for accessing difficult focus spaces.
- editor.addCommand( 'accessSpaceBefore', accessSpaceCommand( that ) );
- editor.addCommand( 'accessSpaceAfter', accessSpaceCommand( that, true ) );
- // %REMOVE_END%
editor.on( 'contentDom', addListeners, this );
function addListeners() {
@@ -99,7 +94,7 @@
// Enabling the box inside of inline editable is pointless.
// There's no need to access spaces inside paragraphs, links, spans, etc.
- if ( editable.is( CKEDITOR.dtd.$inline ) )
+ if ( editable.is( dtd.$inline ) )
return;
// Handle in-line editing by setting appropriate position.
@@ -274,6 +269,15 @@
that.debug.showHidden( that.hiddenMode ); // %REMOVE_LINE%
});
+ // Editor commands for accessing difficult focus spaces.
+ editor.addCommand( 'accessPreviousSpace', accessFocusSpaceCmd( that ) );
+ editor.addCommand( 'accessNextSpace', accessFocusSpaceCmd( that, true ) );
+
+ editor.setKeystroke( [
+ [ config.magicline_keystrokePrevious, 'accessPreviousSpace' ],
+ [ config.magicline_keystrokeNext, 'accessNextSpace' ]
+ ] );
+
// This method handles mousemove mouse for box toggling.
// It uses mouse position to determine underlying element, then
// it tries to use different trigger type in order to place the box
@@ -332,8 +336,15 @@
}
}
- // Constant values, types and so on.
- var EDGE_TOP = 128,
+ // Some shorthands for common methods to save bytes
+ var extend = CKEDITOR.tools.extend,
+ newElement = CKEDITOR.dom.element,
+ newElementFromHtml = newElement.createFromHtml,
+ env = CKEDITOR.env,
+ dtd = CKEDITOR.dtd,
+
+ // Constant values, types and so on.
+ EDGE_TOP = 128,
EDGE_BOTTOM = 64,
EDGE_MIDDLE = 32,
TYPE_EDGE = 16,
@@ -342,8 +353,9 @@
LOOK_BOTTOM = 2,
LOOK_NORMAL = 1,
WHITE_SPACE = '\u00A0',
- DTD_LISTITEM = CKEDITOR.dtd.$listItem,
- DTD_TABLECONTENT = CKEDITOR.dtd.$tableContent,
+ DTD_LISTITEM = dtd.$listItem,
+ DTD_TABLECONTENT = dtd.$tableContent,
+ DTD_NONACCESSIBLE = extend( {}, dtd.$nonEditable, dtd.$empty ),
// Minimum time that must elapse between two update*Size calls.
// It prevents constant getComuptedStyle calls and improves performance.
@@ -352,42 +364,7 @@
// Shared CSS stuff for box elements
CSS_COMMON = 'width:0px;height:0px;padding:0px;margin:0px;display:block;' + 'z-index:9999;color:#fff;position:absolute;font-size: 0px;line-height:0px;',
CSS_TRIANGLE = CSS_COMMON + 'border-color:transparent;display:block;border-style:solid;',
- TRIANGLE_HTML = '' + WHITE_SPACE + '',
-
- // Some shorthands for common methods to save bytes
- extend = CKEDITOR.tools.extend,
- newElement = CKEDITOR.dom.element,
- newElementFromHtml = newElement.createFromHtml,
- env = CKEDITOR.env;
-
- // %REMOVE_START%
- // Access focus space on demand by looking for closest parent trigger
- // or using current element under the caret as reference.
- function accessSpaceCommand( that, insertAfter ) {
- return {
- canUndo: true,
- modes: { wysiwyg:1 },
- exec: function( editor ) {
- var selection = editor.getSelection(),
- selected = selection.getStartElement(),
- range = selection.getRanges()[ 0 ],
- target = getAscendantTrigger( that, selected ) || selected;
-
- if ( !isHtml( target ) )
- return;
-
- accessFocusSpace( that, function( accessNode ) {
- if ( target.equals( that.editable ) )
- range.insertNode( accessNode );
- else
- accessNode[ insertAfter ? 'insertAfter' : 'insertBefore' ]( target );
- });
-
- that.line.detach();
- }
- };
- }
- // %REMOVE_END%
+ TRIANGLE_HTML = '' + WHITE_SPACE + '';
function areSiblings( that, upper, lower ) {
return isHtml( upper ) && isHtml( lower ) && lower.equals( upper.getNext( function( node ) {
@@ -731,7 +708,7 @@
that.editor.focus();
- if( !env.ie && that.enterMode != CKEDITOR.ENTER_BR )
+ if ( !env.ie && that.enterMode != CKEDITOR.ENTER_BR )
that.hotNode.scrollIntoView();
event.data.preventDefault( true );
@@ -782,6 +759,125 @@
editor.fire( 'saveSnapshot' );
}
+ // Access focus space on demand by taking an element under the caret as a reference.
+ // The space is accessed provided the element under the caret is trigger AND:
+ //
+ // 1. First/last-child of its parent:
+ // +----------------------- Parent element -+
+ // | +------------------------------ DIV -+ | <-- Access before
+ // | | Foo^ | |
+ // | | | |
+ // | +------------------------------------+ | <-- Access after
+ // +----------------------------------------+
+ //
+ // OR
+ //
+ // 2. It has a direct sibling element, which is also a trigger:
+ // +-------------------------------- DIV#1 -+
+ // | Foo^ |
+ // | |
+ // +----------------------------------------+
+ // <-- Access here
+ // +-------------------------------- DIV#2 -+
+ // | Bar |
+ // | |
+ // +----------------------------------------+
+ //
+ // OR
+ //
+ // 3. It has a direct sibling, which is a trigger and has a valid neighbour trigger,
+ // but belongs to dtd.$.empty/nonEditable:
+ // +------------------------------------ P -+
+ // | Foo^ |
+ // | |
+ // +----------------------------------------+
+ // +----------------------------------- HR -+
+ // <-- Access here
+ // +-------------------------------- DIV#2 -+
+ // | Bar |
+ // | |
+ // +----------------------------------------+
+ //
+ function accessFocusSpaceCmd( that, insertAfter ) {
+ return {
+ canUndo: true,
+ modes: { wysiwyg: 1 },
+ exec: ( function() {
+ // Inserts line (accessNode) at the position by taking target node as a reference.
+ function doAccess( target ) {
+ accessFocusSpace( that, function( accessNode ) {
+ accessNode[ insertAfter ? 'insertAfter' : 'insertBefore' ]( target );
+ });
+
+ if( !env.ie && that.enterMode != CKEDITOR.ENTER_BR )
+ that.hotNode.scrollIntoView();
+
+ // Detach the line if was visible (previously triggered by mouse).
+ that.line.detach();
+ }
+
+ return function( editor ) {
+ var selected = editor.getSelection().getStartElement();
+
+ // That holds element from mouse. Replace it with the
+ // element under the caret.
+ that.element = selected;
+
+ // (3.) Handle the following cases where selected neighbour
+ // is a trigger inaccessible for the caret AND:
+ // - Is first/last-child
+ // OR
+ // - Has a sibling, which is also a trigger.
+ var neighbor = getNonEmptyNeighbour( that, selected, !insertAfter ),
+ neighborSibling;
+
+ // Check for a neighbour that belongs to triggers.
+ // Consider only non-accessible elements (they cannot have any children)
+ // since they cannot be given a caret inside, to run the command
+ // the regular way (1. & 2.).
+ if ( isHtml( neighbor ) && neighbor.is( that.triggers ) && neighbor.is( DTD_NONACCESSIBLE ) &&
+ (
+ // Check whether neighbor is first/last-child.
+ !getNonEmptyNeighbour( that, neighbor, !insertAfter )
+ ||
+ // Check for a sibling of a neighbour that also is a trigger.
+ (
+ ( neighborSibling = getNonEmptyNeighbour( that, neighbor, !insertAfter ) ) &&
+ isHtml( neighborSibling ) &&
+ neighborSibling.is( that.triggers )
+ )
+ )
+ ) {
+ doAccess( neighbor );
+ return;
+ }
+
+ // Look for possible target element DOWN "selected" DOM branch (towards editable)
+ // that belong to that.triggers
+ var target = getAscendantTrigger( that, selected );
+
+ // No HTML target -> no access.
+ if ( !isHtml( target ) )
+ return;
+
+ // (1.) Target is first/last child -> access.
+ if ( !getNonEmptyNeighbour( that, target, !insertAfter ) ) {
+ doAccess( target );
+ return;
+ }
+
+ var sibling = getNonEmptyNeighbour( that, target, !insertAfter );
+
+ // (2.) Target has a sibling that belongs to that.triggers -> access.
+ if ( sibling && isHtml( sibling ) && sibling.is( that.triggers ) ) {
+ doAccess( target );
+ return;
+ }
+ };
+ })()
+ };
+ }
+
function isLine( that, node ) {
if ( !( node && node.type == CKEDITOR.NODE_ELEMENT && node.$ ) )
return false;
@@ -1545,35 +1641,29 @@
* @see CKEDITOR.config#magicline_triggerOffset
*/
-// %REMOVE_START%
-
/**
- * Defines default keystroke that access the space before an element that
- * holds start of the current selection or just simply holds the caret.
+ * Defines default keystroke that access the closest unreachable focus space **before**
+ * the caret (start of the selection). If there's no any focus space, selection remains.
*
- * // Changes keystroke to CTRL + SHIFT + ,
- * CKEDITOR.config.magicline_keystrokeBefore = CKEDITOR.CTRL + CKEDITOR.SHIFT + 188;
+ * // Changes keystroke to CTRL + ,
+ * CKEDITOR.config.magicline_keystrokePrevious = CKEDITOR.CTRL + 188;
*
- * @ignore
- * @cfg {Number} [magicline_keystrokeBefore=CKEDITOR.CTRL + CKEDITOR.SHIFT + 219 (CTRL + SHIFT + [)]
+ * @cfg {Number} [magicline_keystrokePrevious=CKEDITOR.CTRL + CKEDITOR.ALT + 219 (CTRL + ALT + [)]
* @member CKEDITOR.config
*/
-// CKEDITOR.config.magicline_keystrokeBefore = CKEDITOR.CTRL + CKEDITOR.SHIFT + 219; // CTRL + SHIFT + [
+CKEDITOR.config.magicline_keystrokePrevious = CKEDITOR.CTRL + CKEDITOR.ALT + 219; // CTRL + ALT + [
/**
- * Defines default keystroke that access the space after an element that
- * holds start of the current selection or just simply holds the caret.
+ * Defines default keystroke that access the closest unreachable focus space **after**
+ * the caret (start of the selection). If there's no any focus space, selection remains.
*
- * // Changes keystroke to CTRL + SHIFT + .
- * CKEDITOR.config.magicline_keystrokeBefore = CKEDITOR.CTRL + CKEDITOR.SHIFT + 190;
+ * // Changes keystroke to CTRL + .
+ * CKEDITOR.config.magicline_keystrokeNext = CKEDITOR.CTRL + 190;
*
- * @ignore
- * @cfg {Number} [magicline_keystrokeBefore=CKEDITOR.CTRL + CKEDITOR.SHIFT + 221 (CTRK + SHIFT + ])]
+ * @cfg {Number} [magicline_keystrokeNext=CKEDITOR.CTRL + CKEDITOR.ALT + 221 (CTRL + ALT + ])]
* @member CKEDITOR.config
*/
-// CKEDITOR.config.magicline_keystrokeAfter = CKEDITOR.CTRL + CKEDITOR.SHIFT + 221; // CTRL + SHIFT + ]
-
-// %REMOVE_END%
+CKEDITOR.config.magicline_keystrokeNext = CKEDITOR.CTRL + CKEDITOR.ALT + 221; // CTRL + ALT + ]
/**
* Defines box color. The color may be adjusted to enhance readability.