/**
* InsertBr.js
*
* Released under LGPL License.
* Copyright (c) 1999-2017 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
define(
'tinymce.core.newline.InsertBr',
[
'ephox.katamari.api.Fun',
'ephox.sugar.api.dom.Insert',
'ephox.sugar.api.node.Element',
'tinymce.core.caret.CaretFinder',
'tinymce.core.caret.CaretPosition',
'tinymce.core.dom.NodeType',
'tinymce.core.dom.TreeWalker',
'tinymce.core.keyboard.BoundaryLocation',
'tinymce.core.keyboard.InlineUtils',
'tinymce.core.selection.NormalizeRange'
],
function (Fun, Insert, Element, CaretFinder, CaretPosition, NodeType, TreeWalker, BoundaryLocation, InlineUtils, NormalizeRange) {
// Walks the parent block to the right and look for BR elements
var hasRightSideContent = function (schema, container, parentBlock) {
var walker = new TreeWalker(container, parentBlock), node;
var nonEmptyElementsMap = schema.getNonEmptyElements();
while ((node = walker.next())) {
if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
return true;
}
}
};
var scrollToBr = function (dom, selection, brElm) {
// Insert temp marker and scroll to that
var marker = dom.create('span', {}, ' ');
brElm.parentNode.insertBefore(marker, brElm);
selection.scrollIntoView(marker);
dom.remove(marker);
};
var moveSelectionToBr = function (dom, selection, brElm, extraBr) {
var rng = dom.createRng();
if (!extraBr) {
rng.setStartAfter(brElm);
rng.setEndAfter(brElm);
} else {
rng.setStartBefore(brElm);
rng.setEndBefore(brElm);
}
selection.setRng(rng);
};
var insertBrAtCaret = function (editor, evt) {
// We load the current event in from EnterKey.js when appropriate to heed
// certain event-specific variations such as ctrl-enter in a list
var selection = editor.selection, dom = editor.dom;
var brElm, extraBr;
var rng = selection.getRng(true);
NormalizeRange.normalize(dom, rng).each(function (normRng) {
rng.setStart(normRng.startContainer, normRng.startOffset);
rng.setEnd(normRng.endContainer, normRng.endOffset);
});
var offset = rng.startOffset;
var container = rng.startContainer;
// Resolve node index
if (container.nodeType === 1 && container.hasChildNodes()) {
var isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
if (isAfterLastNodeInContainer && container.nodeType === 3) {
offset = container.nodeValue.length;
} else {
offset = 0;
}
}
var parentBlock = dom.getParent(container, dom.isBlock);
var containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
var containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
// Enter inside block contained within a LI then split or insert before/after LI
var isControlKey = evt && evt.ctrlKey;
if (containerBlockName === 'LI' && !isControlKey) {
parentBlock = containerBlock;
}
if (container && container.nodeType === 3 && offset >= container.nodeValue.length) {
// Insert extra BR element at the end block elements
if (!hasRightSideContent(editor.schema, container, parentBlock)) {
brElm = dom.create('br');
rng.insertNode(brElm);
rng.setStartAfter(brElm);
rng.setEndAfter(brElm);
extraBr = true;
}
}
brElm = dom.create('br');
rng.insertNode(brElm);
scrollToBr(dom, selection, brElm);
moveSelectionToBr(dom, selection, brElm, extraBr);
editor.undoManager.add();
};
var insertBrBefore = function (editor, inline) {
var br = Element.fromTag('br');
Insert.before(Element.fromDom(inline), br);
editor.undoManager.add();
};
var insertBrAfter = function (editor, inline) {
if (!hasBrAfter(editor.getBody(), inline)) {
Insert.after(Element.fromDom(inline), Element.fromTag('br'));
}
var br = Element.fromTag('br');
Insert.after(Element.fromDom(inline), br);
scrollToBr(editor.dom, editor.selection, br.dom());
moveSelectionToBr(editor.dom, editor.selection, br.dom(), false);
editor.undoManager.add();
};
var isBeforeBr = function (pos) {
return NodeType.isBr(pos.getNode());
};
var hasBrAfter = function (rootNode, startNode) {
if (isBeforeBr(CaretPosition.after(startNode))) {
return true;
} else {
return CaretFinder.nextPosition(rootNode, CaretPosition.after(startNode)).map(function (pos) {
return NodeType.isBr(pos.getNode());
}).getOr(false);
}
};
var isAnchorLink = function (elm) {
return elm && elm.nodeName === 'A' && 'href' in elm;
};
var isInsideAnchor = function (location) {
return location.fold(
Fun.constant(false),
isAnchorLink,
isAnchorLink,
Fun.constant(false)
);
};
var readInlineAnchorLocation = function (editor) {
var isInlineTarget = Fun.curry(InlineUtils.isInlineTarget, editor);
var position = CaretPosition.fromRangeStart(editor.selection.getRng());
return BoundaryLocation.readLocation(isInlineTarget, editor.getBody(), position).filter(isInsideAnchor);
};
var insertBrOutsideAnchor = function (editor, location) {
location.fold(
Fun.noop,
Fun.curry(insertBrBefore, editor),
Fun.curry(insertBrAfter, editor),
Fun.noop
);
};
var insert = function (editor, evt) {
var anchorLocation = readInlineAnchorLocation(editor);
if (anchorLocation.isSome()) {
anchorLocation.each(Fun.curry(insertBrOutsideAnchor, editor));
} else {
insertBrAtCaret(editor, evt);
}
};
return {
insert: insert
};
}
);
|