PHP Classes

File: public/js/tinymce/src/core/src/main/js/fmt/RemoveFormat.js

Recommend this page to a friend!
  Classes of Abed Nego Ragil Putra   GoLavaCMS   public/js/tinymce/src/core/src/main/js/fmt/RemoveFormat.js   Download  
File: public/js/tinymce/src/core/src/main/js/fmt/RemoveFormat.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: GoLavaCMS
Publish content on Web pages with SEO support
Author: By
Last change:
Date: 6 years ago
Size: 19,186 bytes
 

Contents

Class file image Download
/** * RemoveFormat.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.fmt.RemoveFormat', [ 'tinymce.core.dom.Bookmarks', 'tinymce.core.dom.NodeType', 'tinymce.core.dom.TreeWalker', 'tinymce.core.fmt.CaretFormat', 'tinymce.core.fmt.ExpandRange', 'tinymce.core.fmt.FormatUtils', 'tinymce.core.fmt.MatchFormat', 'tinymce.core.selection.RangeWalk', 'tinymce.core.util.Tools' ], function (Bookmarks, NodeType, TreeWalker, CaretFormat, ExpandRange, FormatUtils, MatchFormat, RangeWalk, Tools) { var MCE_ATTR_RE = /^(src|href|style)$/; var each = Tools.each; var isEq = FormatUtils.isEq; var isTableCell = function (node) { return /^(TH|TD)$/.test(node.nodeName); }; var getContainer = function (ed, rng, start) { var container, offset, lastIdx; container = rng[start ? 'startContainer' : 'endContainer']; offset = rng[start ? 'startOffset' : 'endOffset']; if (NodeType.isElement(container)) { lastIdx = container.childNodes.length - 1; if (!start && offset) { offset--; } container = container.childNodes[offset > lastIdx ? lastIdx : offset]; } // If start text node is excluded then walk to the next node if (NodeType.isText(container) && start && offset >= container.nodeValue.length) { container = new TreeWalker(container, ed.getBody()).next() || container; } // If end text node is excluded then walk to the previous node if (NodeType.isText(container) && !start && offset === 0) { container = new TreeWalker(container, ed.getBody()).prev() || container; } return container; }; var wrap = function (dom, node, name, attrs) { var wrapper = dom.create(name, attrs); node.parentNode.insertBefore(wrapper, node); wrapper.appendChild(node); return wrapper; }; /** * Checks if the specified nodes name matches the format inline/block or selector. * * @private * @param {Node} node Node to match against the specified format. * @param {Object} format Format object o match with. * @return {boolean} true/false if the format matches. */ var matchName = function (dom, node, format) { // Check for inline match if (isEq(node, format.inline)) { return true; } // Check for block match if (isEq(node, format.block)) { return true; } // Check for selector match if (format.selector) { return NodeType.isElement(node) && dom.is(node, format.selector); } }; var isColorFormatAndAnchor = function (node, format) { return format.links && node.tagName === 'A'; }; var find = function (dom, node, next, inc) { node = FormatUtils.getNonWhiteSpaceSibling(node, next, inc); return !node || (node.nodeName === 'BR' || dom.isBlock(node)); }; /** * Removes the node and wrap it's children in paragraphs before doing so or * appends BR elements to the beginning/end of the block element if forcedRootBlocks is disabled. * * If the div in the node below gets removed: * text<div>text</div>text * * Output becomes: * text<div><br />text<br /></div>text * * So when the div is removed the result is: * text<br />text<br />text * * @private * @param {Node} node Node to remove + apply BR/P elements to. * @param {Object} format Format rule. * @return {Node} Input node. */ var removeNode = function (ed, node, format) { var parentNode = node.parentNode, rootBlockElm; var dom = ed.dom, forcedRootBlock = ed.settings.forced_root_block; if (format.block) { if (!forcedRootBlock) { // Append BR elements if needed before we remove the block if (dom.isBlock(node) && !dom.isBlock(parentNode)) { if (!find(dom, node, false) && !find(dom, node.firstChild, true, 1)) { node.insertBefore(dom.create('br'), node.firstChild); } if (!find(dom, node, true) && !find(dom, node.lastChild, false, 1)) { node.appendChild(dom.create('br')); } } } else { // Wrap the block in a forcedRootBlock if we are at the root of document if (parentNode === dom.getRoot()) { if (!format.list_block || !isEq(node, format.list_block)) { each(Tools.grep(node.childNodes), function (node) { if (FormatUtils.isValid(ed, forcedRootBlock, node.nodeName.toLowerCase())) { if (!rootBlockElm) { rootBlockElm = wrap(dom, node, forcedRootBlock); dom.setAttribs(rootBlockElm, ed.settings.forced_root_block_attrs); } else { rootBlockElm.appendChild(node); } } else { rootBlockElm = 0; } }); } } } } // Never remove nodes that isn't the specified inline element if a selector is specified too if (format.selector && format.inline && !isEq(format.inline, node)) { return; } dom.remove(node, 1); }; /** * Removes the specified format for the specified node. It will also remove the node if it doesn't have * any attributes if the format specifies it to do so. * * @private * @param {Object} format Format object with items to remove from node. * @param {Object} vars Name/value object with variables to apply to format. * @param {Node} node Node to remove the format styles on. * @param {Node} compareNode Optional compare node, if specified the styles will be compared to that node. * @return {Boolean} True/false if the node was removed or not. */ var removeFormat = function (ed, format, vars, node, compareNode) { var i, attrs, stylesModified, dom = ed.dom; // Check if node matches format if (!matchName(dom, node, format) && !isColorFormatAndAnchor(node, format)) { return false; } // Should we compare with format attribs and styles if (format.remove !== 'all') { // Remove styles each(format.styles, function (value, name) { value = FormatUtils.normalizeStyleValue(dom, FormatUtils.replaceVars(value, vars), name); // Indexed array if (typeof name === 'number') { name = value; compareNode = 0; } if (format.remove_similar || (!compareNode || isEq(FormatUtils.getStyle(dom, compareNode, name), value))) { dom.setStyle(node, name, ''); } stylesModified = 1; }); // Remove style attribute if it's empty if (stylesModified && dom.getAttrib(node, 'style') === '') { node.removeAttribute('style'); node.removeAttribute('data-mce-style'); } // Remove attributes each(format.attributes, function (value, name) { var valueOut; value = FormatUtils.replaceVars(value, vars); // Indexed array if (typeof name === 'number') { name = value; compareNode = 0; } if (!compareNode || isEq(dom.getAttrib(compareNode, name), value)) { // Keep internal classes if (name === 'class') { value = dom.getAttrib(node, name); if (value) { // Build new class value where everything is removed except the internal prefixed classes valueOut = ''; each(value.split(/\s+/), function (cls) { if (/mce\-\w+/.test(cls)) { valueOut += (valueOut ? ' ' : '') + cls; } }); // We got some internal classes left if (valueOut) { dom.setAttrib(node, name, valueOut); return; } } } // IE6 has a bug where the attribute doesn't get removed correctly if (name === "class") { node.removeAttribute('className'); } // Remove mce prefixed attributes if (MCE_ATTR_RE.test(name)) { node.removeAttribute('data-mce-' + name); } node.removeAttribute(name); } }); // Remove classes each(format.classes, function (value) { value = FormatUtils.replaceVars(value, vars); if (!compareNode || dom.hasClass(compareNode, value)) { dom.removeClass(node, value); } }); // Check for non internal attributes attrs = dom.getAttribs(node); for (i = 0; i < attrs.length; i++) { var attrName = attrs[i].nodeName; if (attrName.indexOf('_') !== 0 && attrName.indexOf('data-') !== 0) { return false; } } } // Remove the inline child if it's empty for example <b> or <span> if (format.remove !== 'none') { removeNode(ed, node, format); return true; } }; var findFormatRoot = function (editor, container, name, vars, similar) { var formatRoot; // Find format root each(FormatUtils.getParents(editor.dom, container.parentNode).reverse(), function (parent) { var format; // Find format root element if (!formatRoot && parent.id !== '_start' && parent.id !== '_end') { // Is the node matching the format we are looking for format = MatchFormat.matchNode(editor, parent, name, vars, similar); if (format && format.split !== false) { formatRoot = parent; } } }); return formatRoot; }; var wrapAndSplit = function (editor, formatList, formatRoot, container, target, split, format, vars) { var parent, clone, lastClone, firstClone, i, formatRootParent, dom = editor.dom; // Format root found then clone formats and split it if (formatRoot) { formatRootParent = formatRoot.parentNode; for (parent = container.parentNode; parent && parent !== formatRootParent; parent = parent.parentNode) { clone = dom.clone(parent, false); for (i = 0; i < formatList.length; i++) { if (removeFormat(editor, formatList[i], vars, clone, clone)) { clone = 0; break; } } // Build wrapper node if (clone) { if (lastClone) { clone.appendChild(lastClone); } if (!firstClone) { firstClone = clone; } lastClone = clone; } } // Never split block elements if the format is mixed if (split && (!format.mixed || !dom.isBlock(formatRoot))) { container = dom.split(formatRoot, container); } // Wrap container in cloned formats if (lastClone) { target.parentNode.insertBefore(lastClone, target); firstClone.appendChild(target); } } return container; }; var remove = function (ed, name, vars, node, similar) { var formatList = ed.formatter.get(name), format = formatList[0]; var bookmark, rng, contentEditable = true, dom = ed.dom, selection = ed.selection; var splitToFormatRoot = function (container) { var formatRoot = findFormatRoot(ed, container, name, vars, similar); return wrapAndSplit(ed, formatList, formatRoot, container, container, true, format, vars); }; // Merges the styles for each node var process = function (node) { var children, i, l, lastContentEditable, hasContentEditableState; // Node has a contentEditable value if (NodeType.isElement(node) && dom.getContentEditable(node)) { lastContentEditable = contentEditable; contentEditable = dom.getContentEditable(node) === "true"; hasContentEditableState = true; // We don't want to wrap the container only it's children } // Grab the children first since the nodelist might be changed children = Tools.grep(node.childNodes); // Process current node if (contentEditable && !hasContentEditableState) { for (i = 0, l = formatList.length; i < l; i++) { if (removeFormat(ed, formatList[i], vars, node, node)) { break; } } } // Process the children if (format.deep) { if (children.length) { for (i = 0, l = children.length; i < l; i++) { process(children[i]); } if (hasContentEditableState) { contentEditable = lastContentEditable; // Restore last contentEditable state from stack } } } }; var unwrap = function (start) { var node = dom.get(start ? '_start' : '_end'), out = node[start ? 'firstChild' : 'lastChild']; // If the end is placed within the start the result will be removed // So this checks if the out node is a bookmark node if it is it // checks for another more suitable node if (Bookmarks.isBookmarkNode(out)) { out = out[start ? 'firstChild' : 'lastChild']; } // Since dom.remove removes empty text nodes then we need to try to find a better node if (NodeType.isText(out) && out.data.length === 0) { out = start ? node.previousSibling || node.nextSibling : node.nextSibling || node.previousSibling; } dom.remove(node, true); return out; }; var removeRngStyle = function (rng) { var startContainer, endContainer; var commonAncestorContainer = rng.commonAncestorContainer; rng = ExpandRange.expandRng(ed, rng, formatList, true); if (format.split) { startContainer = getContainer(ed, rng, true); endContainer = getContainer(ed, rng); if (startContainer !== endContainer) { // WebKit will render the table incorrectly if we wrap a TH or TD in a SPAN // so let's see if we can use the first child instead // This will happen if you triple click a table cell and use remove formatting if (/^(TR|TH|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) { if (startContainer.nodeName === "TR") { startContainer = startContainer.firstChild.firstChild || startContainer; } else { startContainer = startContainer.firstChild || startContainer; } } // Try to adjust endContainer as well if cells on the same row were selected - bug #6410 if (commonAncestorContainer && /^T(HEAD|BODY|FOOT|R)$/.test(commonAncestorContainer.nodeName) && isTableCell(endContainer) && endContainer.firstChild) { endContainer = endContainer.firstChild || endContainer; } if (dom.isChildOf(startContainer, endContainer) && startContainer !== endContainer && !dom.isBlock(endContainer) && !isTableCell(startContainer) && !isTableCell(endContainer)) { startContainer = wrap(dom, startContainer, 'span', { id: '_start', 'data-mce-type': 'bookmark' }); splitToFormatRoot(startContainer); startContainer = unwrap(true); return; } // Wrap start/end nodes in span element since these might be cloned/moved startContainer = wrap(dom, startContainer, 'span', { id: '_start', 'data-mce-type': 'bookmark' }); endContainer = wrap(dom, endContainer, 'span', { id: '_end', 'data-mce-type': 'bookmark' }); // Split start/end splitToFormatRoot(startContainer); splitToFormatRoot(endContainer); // Unwrap start/end to get real elements again startContainer = unwrap(true); endContainer = unwrap(); } else { startContainer = endContainer = splitToFormatRoot(startContainer); } // Update range positions since they might have changed after the split operations rng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer; rng.startOffset = dom.nodeIndex(startContainer); rng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer; rng.endOffset = dom.nodeIndex(endContainer) + 1; } // Remove items between start/end RangeWalk.walk(dom, rng, function (nodes) { each(nodes, function (node) { process(node); // Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined. if (NodeType.isElement(node) && ed.dom.getStyle(node, 'text-decoration') === 'underline' && node.parentNode && FormatUtils.getTextDecoration(dom, node.parentNode) === 'underline') { removeFormat(ed, { 'deep': false, 'exact': true, 'inline': 'span', 'styles': { 'textDecoration': 'underline' } }, null, node); } }); }); }; // Handle node if (node) { if (node.nodeType) { rng = dom.createRng(); rng.setStartBefore(node); rng.setEndAfter(node); removeRngStyle(rng); } else { removeRngStyle(node); } return; } if (dom.getContentEditable(selection.getNode()) === "false") { node = selection.getNode(); for (var i = 0, l = formatList.length; i < l; i++) { if (formatList[i].ceFalseOverride) { if (removeFormat(ed, formatList[i], vars, node, node)) { break; } } } return; } if (!selection.isCollapsed() || !format.inline || dom.select('td[data-mce-selected],th[data-mce-selected]').length) { bookmark = selection.getBookmark(); removeRngStyle(selection.getRng(true)); selection.moveToBookmark(bookmark); // Check if start element still has formatting then we are at: "<b>text|</b>text" // and need to move the start into the next text node if (format.inline && MatchFormat.match(ed, name, vars, selection.getStart())) { FormatUtils.moveStart(dom, selection, selection.getRng(true)); } ed.nodeChanged(); } else { CaretFormat.removeCaretFormat(ed, name, vars, similar); } }; return { removeFormat: removeFormat, remove: remove }; } );