import { Arr } from '@ephox/katamari';
import DOMUtils from '../api/dom/DOMUtils';
import Editor from '../api/Editor';
import Tools from '../api/util/Tools';
import * as NodeType from '../dom/NodeType';
import { ApplyFormat, FormatVars } from './FormatTypes';
import * as FormatUtils from './FormatUtils';
import * as MatchFormat from './MatchFormat';
import { applyStyle, clearChildStyles, hasStyle, isElementNode, mergeSiblings, processChildElements } from './MergeUtils';
import * as RemoveFormat from './RemoveFormat';
const each = Tools.each;
const mergeTextDecorationsAndColor = (dom: DOMUtils, format: ApplyFormat, vars: FormatVars | undefined, node: Node): void => {
const processTextDecorationsAndColor = (n: Node) => {
if (NodeType.isElement(n) && NodeType.isElement(n.parentNode) && FormatUtils.isEditable(n)) {
const parentTextDecoration = FormatUtils.getTextDecoration(dom, n.parentNode);
if (dom.getStyle(n, 'color') && parentTextDecoration) {
dom.setStyle(n, 'text-decoration', parentTextDecoration);
} else if (dom.getStyle(n, 'text-decoration') === parentTextDecoration) {
dom.setStyle(n, 'text-decoration', null);
}
}
};
// Colored nodes should be underlined so that the color of the underline matches the text color.
if (format.styles && (format.styles.color || format.styles.textDecoration)) {
Tools.walk(node, processTextDecorationsAndColor, 'childNodes');
processTextDecorationsAndColor(node);
}
};
const mergeBackgroundColorAndFontSize = (dom: DOMUtils, format: ApplyFormat, vars: FormatVars | undefined, node: Node): void => {
// nodes with font-size should have their own background color as well to fit the line-height (see TINY-882)
if (format.styles && format.styles.backgroundColor) {
const hasFontSize = hasStyle(dom, 'fontSize');
processChildElements(node,
(elm) => hasFontSize(elm) && FormatUtils.isEditable(elm),
applyStyle(dom, 'backgroundColor', FormatUtils.replaceVars(format.styles.backgroundColor, vars))
);
}
};
const mergeSubSup = (dom: DOMUtils, format: ApplyFormat, vars: FormatVars | undefined, node: Node): void => {
// Remove font size on all descendants of a sub/sup and remove the inverse elements
if (FormatUtils.isInlineFormat(format) && (format.inline === 'sub' || format.inline === 'sup')) {
const hasFontSize = hasStyle(dom, 'fontSize');
processChildElements(node,
(elm) => hasFontSize(elm) && FormatUtils.isEditable(elm),
applyStyle(dom, 'fontSize', '')
);
const inverseTagDescendants = Arr.filter(dom.select(format.inline === 'sup' ? 'sub' : 'sup', node), FormatUtils.isEditable);
dom.remove(inverseTagDescendants, true);
}
};
const mergeWithChildren = (editor: Editor, formatList: ApplyFormat[], vars: FormatVars | undefined, node: Node): void => {
// Remove/merge children
// Note: RemoveFormat.removeFormat will not remove formatting from noneditable nodes
each(formatList, (format) => {
// Merge all children of similar type will move styles from child to parent
// this: text
// will become: text
if (FormatUtils.isInlineFormat(format)) {
each(editor.dom.select(format.inline, node), (child) => {
if (isElementNode(child)) {
RemoveFormat.removeFormat(editor, format, vars, child, format.exact ? child : null);
}
});
}
clearChildStyles(editor.dom, format, node);
});
};
const mergeWithParents = (editor: Editor, format: ApplyFormat, name: string, vars: FormatVars | undefined, node: Node): void => {
// Remove format if direct parent already has the same format
// Note: RemoveFormat.removeFormat will not remove formatting from noneditable nodes
const parentNode = node.parentNode;
if (MatchFormat.matchNode(editor, parentNode, name, vars)) {
if (RemoveFormat.removeFormat(editor, format, vars, node)) {
return;
}
}
// Remove format if any ancestor already has the same format
if (format.merge_with_parents && parentNode) {
editor.dom.getParent(parentNode, (parent) => {
if (MatchFormat.matchNode(editor, parent, name, vars)) {
RemoveFormat.removeFormat(editor, format, vars, node);
return true;
} else {
return false;
}
});
}
};
export {
mergeWithChildren,
mergeTextDecorationsAndColor,
mergeBackgroundColorAndFontSize,
mergeSubSup,
mergeSiblings,
mergeWithParents
};