diff --git a/CHANGES.md b/CHANGES.md
index 31084ed0019..b208bbf9247 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -35,6 +35,7 @@ Fixed Issues:
* [#10866](http://dev.ckeditor.com/ticket/10866): Fixed: Broken *Tab* key navigation in the Image2 dialog.
* [#10854](http://dev.ckeditor.com/ticket/10854): Fixed: Firefox prepends `
` to `
`, so it is stripped by the HTML data processor.
* [#10823](http://dev.ckeditor.com/ticket/10823): Fixed: Link plugin does not work with non-editable content.
+* [#10828](http://dev.ckeditor.com/ticket/10828): Magicline integration with widgets system.
## CKEditor 4.3 Beta
diff --git a/plugins/magicline/plugin.js b/plugins/magicline/plugin.js
index 85eee75135d..8e01ca0aa08 100644
--- a/plugins/magicline/plugin.js
+++ b/plugins/magicline/plugin.js
@@ -17,12 +17,6 @@
// Activates the box inside of an editor.
function initPlugin( editor ) {
-
- var enterBehaviors = {};
- enterBehaviors[ CKEDITOR.ENTER_BR ] = 'br';
- enterBehaviors[ CKEDITOR.ENTER_P ] = 'p';
- enterBehaviors[ CKEDITOR.ENTER_DIV ] = 'div';
-
// Configurables
var config = editor.config,
triggerOffset = config.magicline_triggerOffset || 30,
@@ -30,13 +24,12 @@
that = {
// Global stuff is being initialized here.
editor: editor,
- enterBehavior: enterBehaviors[ enterMode ], // A tag which is to be inserted by the magicline.
enterMode: enterMode,
triggerOffset: triggerOffset,
holdDistance: 0 | triggerOffset * ( config.magicline_holdDistance || 0.5 ),
boxColor: config.magicline_color || '#ff0000',
rtl: config.contentsLangDirection == 'rtl',
- tabuList: [ 'data-widget-wrapper' ].concat( config.magicline_tabuList || [] ),
+ tabuList: [ 'data-cke-hidden-sel' ].concat( config.magicline_tabuList || [] ),
triggers: config.magicline_everywhere ? DTD_BLOCK : { table:1,hr:1,div:1,ul:1,ol:1,dl:1,form:1,blockquote:1 }
},
scrollTimeout, checkMouseTimeoutPending, checkMouseTimeout, checkMouseTimer;
@@ -86,7 +79,8 @@
editable: editable,
inInlineMode: editable.isInline(),
doc: doc,
- win: win
+ win: win,
+ hotNode: null
}, true );
// This is the boundary of the editor. For inline the boundary is editable itself.
@@ -278,15 +272,15 @@
// Revert magicline hot node on undo/redo.
editor.on( 'loadSnapshot', function( event ) {
- var elements = editor.document.getElementsByTag( that.enterBehavior ),
+ var elements = doc.find( 'p,br,div' ),
element;
for ( var i = elements.count(); i--; ) {
- if ( ( element = elements.getItem( i ) ).hasAttribute( 'data-cke-magicline-hot' ) ) {
+ if ( ( element = elements.getItem( i ) ).data( 'cke-magicline-hot' ) ) {
// Restore hotNode
that.hotNode = element;
// Restore last access direction
- that.lastCmdDirection = element.getAttribute( 'data-cke-magicline-dir' ) === 'true' ? true : false;
+ that.lastCmdDirection = element.data( 'cke-magicline-dir' ) === 'true' ? true : false;
break;
}
}
@@ -359,6 +353,9 @@
env = CKEDITOR.env,
dtd = CKEDITOR.dtd,
+ // Global object associating enter modes with elements.
+ enterElements = {},
+
// Constant values, types and so on.
EDGE_TOP = 128,
EDGE_BOTTOM = 64,
@@ -383,6 +380,10 @@
CSS_TRIANGLE = CSS_COMMON + 'border-color:transparent;display:block;border-style:solid;',
TRIANGLE_HTML = '' + WHITE_SPACE + '';
+ enterElements[ CKEDITOR.ENTER_BR ] = 'br';
+ enterElements[ CKEDITOR.ENTER_P ] = 'p';
+ enterElements[ CKEDITOR.ENTER_DIV ] = 'div';
+
function areSiblings( that, upper, lower ) {
return isHtml( upper ) && isHtml( lower ) && lower.equals( upper.getNext( function( node ) {
return !( isEmptyTextNode( node ) || isComment( node ) || isFlowBreaker( node ) );
@@ -462,9 +463,25 @@
trigger;
if ( node && isHtml( node ) ) {
- return ( trigger = node.getAscendant( that.triggers, true ) ) &&
- !trigger.contains( that.editable ) &&
- !trigger.equals( that.editable ) ? trigger : null;
+ trigger = node.getAscendant( that.triggers, true );
+
+ // If trigger is an element, neither editable nor editable's ascendant.
+ if ( trigger && !trigger.contains( that.editable ) && !trigger.equals( that.editable ) ) {
+ // Check for closest editable limit.
+ var limit = getClosestEditableLimit( trigger, true );
+
+ // Trigger in nested editable area.
+ if ( limit.getAttribute( 'contenteditable' ) == 'true' )
+ return trigger
+ // Trigger in non-editable area.
+ else if ( limit.is( that.triggers ) )
+ return limit;
+ else
+ return null;
+
+ return trigger;
+ } else
+ return null;
}
return null;
@@ -497,6 +514,29 @@
return val > lower && val < upper;
}
+ // Returns the closest ancestor that has contenteditable attribute.
+ // Such ancestor is the limit of (non-)editable DOM branch that element
+ // belongs to. This method omits editor editable.
+ function getClosestEditableLimit( element, includeSelf ) {
+ if ( element.data( 'cke-editable' ) )
+ return null;
+
+ if ( !includeSelf )
+ element = element.getParent();
+
+ while ( element ) {
+ if ( element.data( 'cke-editable' ) )
+ return null;
+
+ if ( element.hasAttribute( 'contenteditable' ) )
+ return element;
+
+ element = element.getParent();
+ }
+
+ return null;
+ }
+
// Access space line consists of a few elements (spans):
// \-> Line wrapper.
// \-> Line.
@@ -759,9 +799,19 @@
// In other cases a regular element is used.
else {
- accessNode = new newElement( that.enterBehavior, that.doc );
+ // Use the enterMode of editable's limit or editor's
+ // enter mode if not in nested editable.
+ var limit = getClosestEditableLimit( that.element, true ),
- if ( that.enterMode != CKEDITOR.ENTER_BR ) {
+ // This is an enter mode for the context. We cannot use
+ // editor.activeEnterMode because the focused nested editable will
+ // have a different enterMode as editor but magicline will be inserted
+ // directly into editor's editable.
+ enterMode = limit && limit.data( 'cke-enter-mode' ) || that.enterMode;
+
+ accessNode = new newElement( enterElements[ enterMode ], that.doc );
+
+ if ( !accessNode.is( 'br' ) ) {
var dummy = that.doc.createText( WHITE_SPACE );
dummy.appendTo( accessNode );
}
@@ -857,7 +907,8 @@
}
return function( editor ) {
- var selected = editor.getSelection().getStartElement();
+ var selected = editor.getSelection().getStartElement(),
+ limit;
// (#9833) Go down to the closest non-inline element in DOM structure
// since inline elements don't participate in in magicline.
@@ -873,6 +924,11 @@
if ( !selected || selected.equals( that.editable ) || selected.contains( that.editable ) )
return;
+ // Executing the command directly in nested editable should
+ // access space before/after it.
+ if ( ( limit = getClosestEditableLimit( selected ) ) && limit.getAttribute( 'contenteditable' ) == 'false' )
+ selected = limit;
+
// That holds element from mouse. Replace it with the
// element under the caret.
that.element = selected;
@@ -969,7 +1025,7 @@
var isComment = CKEDITOR.dom.walker.nodeType( CKEDITOR.NODE_COMMENT );
function isPositioned( element ) {
- return !!{ absolute:1,fixed:1,relative:1 }[ element.getComputedStyle( 'position' ) ];
+ return !!{ absolute:1,fixed:1 }[ element.getComputedStyle( 'position' ) ];
}
// Is text node?
@@ -1058,7 +1114,7 @@
// Edge node according to bottomTrigger.
edgeNode = editable[ bottomTrigger ? 'getLast' : 'getFirst' ]( function( node ) {
return !( isEmptyTextNode( node ) || isComment( node ) );
- });
+ } );
// There's no edge node. Abort.
if ( !edgeNode ) {
@@ -1331,6 +1387,10 @@
return null;
}
+ // Stop searching if element is in non-editable branch of DOM.
+ if ( startElement.isReadOnly() )
+ return null;
+
trigger = verticalSearch( that,
function( current, startElement ) {
return !startElement.equals( current ); // stop when start element and the current one differ