--- a/src/pyams_skin/resources/js/ext/tinymce/dev/classes/html/DomParser.js Sun Jul 19 02:02:20 2020 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,804 +0,0 @@
-/**
- * DomParser.js
- *
- * Copyright, Moxiecode Systems AB
- * Released under LGPL License.
- *
- * License: http://www.tinymce.com/license
- * Contributing: http://www.tinymce.com/contributing
- */
-
-/**
- * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make
- * sure that the node tree is valid according to the specified schema.
- * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p>
- *
- * @example
- * var parser = new tinymce.html.DomParser({validate: true}, schema);
- * var rootNode = parser.parse('<h1>content</h1>');
- *
- * @class tinymce.html.DomParser
- * @version 3.4
- */
-define("tinymce/html/DomParser", [
- "tinymce/html/Node",
- "tinymce/html/Schema",
- "tinymce/html/SaxParser",
- "tinymce/util/Tools"
-], function(Node, Schema, SaxParser, Tools) {
- var makeMap = Tools.makeMap, each = Tools.each, explode = Tools.explode, extend = Tools.extend;
-
- /**
- * Constructs a new DomParser instance.
- *
- * @constructor
- * @method DomParser
- * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
- * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
- */
- return function(settings, schema) {
- var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
-
- settings = settings || {};
- settings.validate = "validate" in settings ? settings.validate : true;
- settings.root_name = settings.root_name || 'body';
- self.schema = schema = schema || new Schema();
-
- function fixInvalidChildren(nodes) {
- var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i;
- var nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;
-
- nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table');
- nonEmptyElements = schema.getNonEmptyElements();
- textBlockElements = schema.getTextBlockElements();
-
- for (ni = 0; ni < nodes.length; ni++) {
- node = nodes[ni];
-
- // Already removed or fixed
- if (!node.parent || node.fixed) {
- continue;
- }
-
- // If the invalid element is a text block and the text block is within a parent LI element
- // Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
- if (textBlockElements[node.name] && node.parent.name == 'li') {
- // Move sibling text blocks after LI element
- sibling = node.next;
- while (sibling) {
- if (textBlockElements[sibling.name]) {
- sibling.name = 'li';
- sibling.fixed = true;
- node.parent.insert(sibling, node.parent);
- } else {
- break;
- }
-
- sibling = sibling.next;
- }
-
- // Unwrap current text block
- node.unwrap(node);
- continue;
- }
-
- // Get list of all parent nodes until we find a valid parent to stick the child into
- parents = [node];
- for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) &&
- !nonSplitableElements[parent.name]; parent = parent.parent) {
- parents.push(parent);
- }
-
- // Found a suitable parent
- if (parent && parents.length > 1) {
- // Reverse the array since it makes looping easier
- parents.reverse();
-
- // Clone the related parent and insert that after the moved node
- newParent = currentNode = self.filterNode(parents[0].clone());
-
- // Start cloning and moving children on the left side of the target node
- for (i = 0; i < parents.length - 1; i++) {
- if (schema.isValidChild(currentNode.name, parents[i].name)) {
- tempNode = self.filterNode(parents[i].clone());
- currentNode.append(tempNode);
- } else {
- tempNode = currentNode;
- }
-
- for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1];) {
- nextNode = childNode.next;
- tempNode.append(childNode);
- childNode = nextNode;
- }
-
- currentNode = tempNode;
- }
-
- if (!newParent.isEmpty(nonEmptyElements)) {
- parent.insert(newParent, parents[0], true);
- parent.insert(node, newParent);
- } else {
- parent.insert(node, parents[0], true);
- }
-
- // Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>
- parent = parents[0];
- if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {
- parent.empty().remove();
- }
- } else if (node.parent) {
- // If it's an LI try to find a UL/OL for it or wrap it
- if (node.name === 'li') {
- sibling = node.prev;
- if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
- sibling.append(node);
- continue;
- }
-
- sibling = node.next;
- if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
- sibling.insert(node, sibling.firstChild, true);
- continue;
- }
-
- node.wrap(self.filterNode(new Node('ul', 1)));
- continue;
- }
-
- // Try wrapping the element in a DIV
- if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
- node.wrap(self.filterNode(new Node('div', 1)));
- } else {
- // We failed wrapping it, then remove or unwrap it
- if (node.name === 'style' || node.name === 'script') {
- node.empty().remove();
- } else {
- node.unwrap();
- }
- }
- }
- }
- }
-
- /**
- * Runs the specified node though the element and attributes filters.
- *
- * @method filterNode
- * @param {tinymce.html.Node} Node the node to run filters on.
- * @return {tinymce.html.Node} The passed in node.
- */
- self.filterNode = function(node) {
- var i, name, list;
-
- // Run element filters
- if (name in nodeFilters) {
- list = matchedNodes[name];
-
- if (list) {
- list.push(node);
- } else {
- matchedNodes[name] = [node];
- }
- }
-
- // Run attribute filters
- i = attributeFilters.length;
- while (i--) {
- name = attributeFilters[i].name;
-
- if (name in node.attributes.map) {
- list = matchedAttributes[name];
-
- if (list) {
- list.push(node);
- } else {
- matchedAttributes[name] = [node];
- }
- }
- }
-
- return node;
- };
-
- /**
- * Adds a node filter function to the parser, the parser will collect the specified nodes by name
- * and then execute the callback ones it has finished parsing the document.
- *
- * @example
- * parser.addNodeFilter('p,h1', function(nodes, name) {
- * for (var i = 0; i < nodes.length; i++) {
- * console.log(nodes[i].name);
- * }
- * });
- * @method addNodeFilter
- * @method {String} name Comma separated list of nodes to collect.
- * @param {function} callback Callback function to execute once it has collected nodes.
- */
- self.addNodeFilter = function(name, callback) {
- each(explode(name), function(name) {
- var list = nodeFilters[name];
-
- if (!list) {
- nodeFilters[name] = list = [];
- }
-
- list.push(callback);
- });
- };
-
- /**
- * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes
- * and then execute the callback ones it has finished parsing the document.
- *
- * @example
- * parser.addAttributeFilter('src,href', function(nodes, name) {
- * for (var i = 0; i < nodes.length; i++) {
- * console.log(nodes[i].name);
- * }
- * });
- * @method addAttributeFilter
- * @method {String} name Comma separated list of nodes to collect.
- * @param {function} callback Callback function to execute once it has collected nodes.
- */
- self.addAttributeFilter = function(name, callback) {
- each(explode(name), function(name) {
- var i;
-
- for (i = 0; i < attributeFilters.length; i++) {
- if (attributeFilters[i].name === name) {
- attributeFilters[i].callbacks.push(callback);
- return;
- }
- }
-
- attributeFilters.push({name: name, callbacks: [callback]});
- });
- };
-
- /**
- * Parses the specified HTML string into a DOM like node tree and returns the result.
- *
- * @example
- * var rootNode = new DomParser({...}).parse('<b>text</b>');
- * @method parse
- * @param {String} html Html string to sax parse.
- * @param {Object} args Optional args object that gets passed to all filter functions.
- * @return {tinymce.html.Node} Root node containing the tree.
- */
- self.parse = function(html, args) {
- var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate;
- var blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement;
- var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements;
- var children, nonEmptyElements, rootBlockName;
-
- args = args || {};
- matchedNodes = {};
- matchedAttributes = {};
- blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
- nonEmptyElements = schema.getNonEmptyElements();
- children = schema.children;
- validate = settings.validate;
- rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
-
- whiteSpaceElements = schema.getWhiteSpaceElements();
- startWhiteSpaceRegExp = /^[ \t\r\n]+/;
- endWhiteSpaceRegExp = /[ \t\r\n]+$/;
- allWhiteSpaceRegExp = /[ \t\r\n]+/g;
- isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
-
- function addRootBlocks() {
- var node = rootNode.firstChild, next, rootBlockNode;
-
- // Removes whitespace at beginning and end of block so:
- // <p> x </p> -> <p>x</p>
- function trim(rootBlockNode) {
- if (rootBlockNode) {
- node = rootBlockNode.firstChild;
- if (node && node.type == 3) {
- node.value = node.value.replace(startWhiteSpaceRegExp, '');
- }
-
- node = rootBlockNode.lastChild;
- if (node && node.type == 3) {
- node.value = node.value.replace(endWhiteSpaceRegExp, '');
- }
- }
- }
-
- // Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root
- if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
- return;
- }
-
- while (node) {
- next = node.next;
-
- if (node.type == 3 || (node.type == 1 && node.name !== 'p' &&
- !blockElements[node.name] && !node.attr('data-mce-type'))) {
- if (!rootBlockNode) {
- // Create a new root block element
- rootBlockNode = createNode(rootBlockName, 1);
- rootBlockNode.attr(settings.forced_root_block_attrs);
- rootNode.insert(rootBlockNode, node);
- rootBlockNode.append(node);
- } else {
- rootBlockNode.append(node);
- }
- } else {
- trim(rootBlockNode);
- rootBlockNode = null;
- }
-
- node = next;
- }
-
- trim(rootBlockNode);
- }
-
- function createNode(name, type) {
- var node = new Node(name, type), list;
-
- if (name in nodeFilters) {
- list = matchedNodes[name];
-
- if (list) {
- list.push(node);
- } else {
- matchedNodes[name] = [node];
- }
- }
-
- return node;
- }
-
- function removeWhitespaceBefore(node) {
- var textNode, textVal, sibling;
-
- for (textNode = node.prev; textNode && textNode.type === 3;) {
- textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
-
- if (textVal.length > 0) {
- textNode.value = textVal;
- textNode = textNode.prev;
- } else {
- sibling = textNode.prev;
- textNode.remove();
- textNode = sibling;
- }
- }
- }
-
- function cloneAndExcludeBlocks(input) {
- var name, output = {};
-
- for (name in input) {
- if (name !== 'li' && name != 'p') {
- output[name] = input[name];
- }
- }
-
- return output;
- }
-
- parser = new SaxParser({
- validate: validate,
- allow_script_urls: settings.allow_script_urls,
- allow_conditional_comments: settings.allow_conditional_comments,
-
- // Exclude P and LI from DOM parsing since it's treated better by the DOM parser
- self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
-
- cdata: function(text) {
- node.append(createNode('#cdata', 4)).value = text;
- },
-
- text: function(text, raw) {
- var textNode;
-
- // Trim all redundant whitespace on non white space elements
- if (!isInWhiteSpacePreservedElement) {
- text = text.replace(allWhiteSpaceRegExp, ' ');
-
- if (node.lastChild && blockElements[node.lastChild.name]) {
- text = text.replace(startWhiteSpaceRegExp, '');
- }
- }
-
- // Do we need to create the node
- if (text.length !== 0) {
- textNode = createNode('#text', 3);
- textNode.raw = !!raw;
- node.append(textNode).value = text;
- }
- },
-
- comment: function(text) {
- node.append(createNode('#comment', 8)).value = text;
- },
-
- pi: function(name, text) {
- node.append(createNode(name, 7)).value = text;
- removeWhitespaceBefore(node);
- },
-
- doctype: function(text) {
- var newNode;
-
- newNode = node.append(createNode('#doctype', 10));
- newNode.value = text;
- removeWhitespaceBefore(node);
- },
-
- start: function(name, attrs, empty) {
- var newNode, attrFiltersLen, elementRule, attrName, parent;
-
- elementRule = validate ? schema.getElementRule(name) : {};
- if (elementRule) {
- newNode = createNode(elementRule.outputName || name, 1);
- newNode.attributes = attrs;
- newNode.shortEnded = empty;
-
- node.append(newNode);
-
- // Check if node is valid child of the parent node is the child is
- // unknown we don't collect it since it's probably a custom element
- parent = children[node.name];
- if (parent && children[newNode.name] && !parent[newNode.name]) {
- invalidChildren.push(newNode);
- }
-
- attrFiltersLen = attributeFilters.length;
- while (attrFiltersLen--) {
- attrName = attributeFilters[attrFiltersLen].name;
-
- if (attrName in attrs.map) {
- list = matchedAttributes[attrName];
-
- if (list) {
- list.push(newNode);
- } else {
- matchedAttributes[attrName] = [newNode];
- }
- }
- }
-
- // Trim whitespace before block
- if (blockElements[name]) {
- removeWhitespaceBefore(newNode);
- }
-
- // Change current node if the element wasn't empty i.e not <br /> or <img />
- if (!empty) {
- node = newNode;
- }
-
- // Check if we are inside a whitespace preserved element
- if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
- isInWhiteSpacePreservedElement = true;
- }
- }
- },
-
- end: function(name) {
- var textNode, elementRule, text, sibling, tempNode;
-
- elementRule = validate ? schema.getElementRule(name) : {};
- if (elementRule) {
- if (blockElements[name]) {
- if (!isInWhiteSpacePreservedElement) {
- // Trim whitespace of the first node in a block
- textNode = node.firstChild;
- if (textNode && textNode.type === 3) {
- text = textNode.value.replace(startWhiteSpaceRegExp, '');
-
- // Any characters left after trim or should we remove it
- if (text.length > 0) {
- textNode.value = text;
- textNode = textNode.next;
- } else {
- sibling = textNode.next;
- textNode.remove();
- textNode = sibling;
-
- // Remove any pure whitespace siblings
- while (textNode && textNode.type === 3) {
- text = textNode.value;
- sibling = textNode.next;
-
- if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
- textNode.remove();
- textNode = sibling;
- }
-
- textNode = sibling;
- }
- }
- }
-
- // Trim whitespace of the last node in a block
- textNode = node.lastChild;
- if (textNode && textNode.type === 3) {
- text = textNode.value.replace(endWhiteSpaceRegExp, '');
-
- // Any characters left after trim or should we remove it
- if (text.length > 0) {
- textNode.value = text;
- textNode = textNode.prev;
- } else {
- sibling = textNode.prev;
- textNode.remove();
- textNode = sibling;
-
- // Remove any pure whitespace siblings
- while (textNode && textNode.type === 3) {
- text = textNode.value;
- sibling = textNode.prev;
-
- if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
- textNode.remove();
- textNode = sibling;
- }
-
- textNode = sibling;
- }
- }
- }
- }
-
- // Trim start white space
- // Removed due to: #5424
- /*textNode = node.prev;
- if (textNode && textNode.type === 3) {
- text = textNode.value.replace(startWhiteSpaceRegExp, '');
-
- if (text.length > 0)
- textNode.value = text;
- else
- textNode.remove();
- }*/
- }
-
- // Check if we exited a whitespace preserved element
- if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
- isInWhiteSpacePreservedElement = false;
- }
-
- // Handle empty nodes
- if (elementRule.removeEmpty || elementRule.paddEmpty) {
- if (node.isEmpty(nonEmptyElements)) {
- if (elementRule.paddEmpty) {
- node.empty().append(new Node('#text', '3')).value = '\u00a0';
- } else {
- // Leave nodes that have a name like <a name="name">
- if (!node.attributes.map.name && !node.attributes.map.id) {
- tempNode = node.parent;
-
- if (blockElements[node.name]) {
- node.empty().remove();
- } else {
- node.unwrap();
- }
-
- node = tempNode;
- return;
- }
- }
- }
- }
-
- node = node.parent;
- }
- }
- }, schema);
-
- rootNode = node = new Node(args.context || settings.root_name, 11);
-
- parser.parse(html);
-
- // Fix invalid children or report invalid children in a contextual parsing
- if (validate && invalidChildren.length) {
- if (!args.context) {
- fixInvalidChildren(invalidChildren);
- } else {
- args.invalid = true;
- }
- }
-
- // Wrap nodes in the root into block elements if the root is body
- if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) {
- addRootBlocks();
- }
-
- // Run filters only when the contents is valid
- if (!args.invalid) {
- // Run node filters
- for (name in matchedNodes) {
- list = nodeFilters[name];
- nodes = matchedNodes[name];
-
- // Remove already removed children
- fi = nodes.length;
- while (fi--) {
- if (!nodes[fi].parent) {
- nodes.splice(fi, 1);
- }
- }
-
- for (i = 0, l = list.length; i < l; i++) {
- list[i](nodes, name, args);
- }
- }
-
- // Run attribute filters
- for (i = 0, l = attributeFilters.length; i < l; i++) {
- list = attributeFilters[i];
-
- if (list.name in matchedAttributes) {
- nodes = matchedAttributes[list.name];
-
- // Remove already removed children
- fi = nodes.length;
- while (fi--) {
- if (!nodes[fi].parent) {
- nodes.splice(fi, 1);
- }
- }
-
- for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) {
- list.callbacks[fi](nodes, list.name, args);
- }
- }
- }
- }
-
- return rootNode;
- };
-
- // Remove <br> at end of block elements Gecko and WebKit injects BR elements to
- // make it possible to place the caret inside empty blocks. This logic tries to remove
- // these elements and keep br elements that where intended to be there intact
- if (settings.remove_trailing_brs) {
- self.addNodeFilter('br', function(nodes) {
- var i, l = nodes.length, node, blockElements = extend({}, schema.getBlockElements());
- var nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
- var elementRule, textNode;
-
- // Remove brs from body element as well
- blockElements.body = 1;
-
- // Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>
- for (i = 0; i < l; i++) {
- node = nodes[i];
- parent = node.parent;
-
- if (blockElements[node.parent.name] && node === parent.lastChild) {
- // Loop all nodes to the left of the current node and check for other BR elements
- // excluding bookmarks since they are invisible
- prev = node.prev;
- while (prev) {
- prevName = prev.name;
-
- // Ignore bookmarks
- if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {
- // Found a non BR element
- if (prevName !== "br") {
- break;
- }
-
- // Found another br it's a <br><br> structure then don't remove anything
- if (prevName === 'br') {
- node = null;
- break;
- }
- }
-
- prev = prev.prev;
- }
-
- if (node) {
- node.remove();
-
- // Is the parent to be considered empty after we removed the BR
- if (parent.isEmpty(nonEmptyElements)) {
- elementRule = schema.getElementRule(parent.name);
-
- // Remove or padd the element depending on schema rule
- if (elementRule) {
- if (elementRule.removeEmpty) {
- parent.remove();
- } else if (elementRule.paddEmpty) {
- parent.empty().append(new Node('#text', 3)).value = '\u00a0';
- }
- }
- }
- }
- } else {
- // Replaces BR elements inside inline elements like <p><b><i><br></i></b></p>
- // so they become <p><b><i> </i></b></p>
- lastParent = node;
- while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {
- lastParent = parent;
-
- if (blockElements[parent.name]) {
- break;
- }
-
- parent = parent.parent;
- }
-
- if (lastParent === parent) {
- textNode = new Node('#text', 3);
- textNode.value = '\u00a0';
- node.replace(textNode);
- }
- }
- }
- });
- }
-
- // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
- if (!settings.allow_html_in_named_anchor) {
- self.addAttributeFilter('id,name', function(nodes) {
- var i = nodes.length, sibling, prevSibling, parent, node;
-
- while (i--) {
- node = nodes[i];
- if (node.name === 'a' && node.firstChild && !node.attr('href')) {
- parent = node.parent;
-
- // Move children after current node
- sibling = node.lastChild;
- do {
- prevSibling = sibling.prev;
- parent.insert(sibling, node);
- sibling = prevSibling;
- } while (sibling);
- }
- }
- });
- }
-
- if (settings.validate && schema.getValidClasses()) {
- self.addAttributeFilter('class', function(nodes) {
- var i = nodes.length, node, classList, ci, className, classValue;
- var validClasses = schema.getValidClasses(), validClassesMap, valid;
-
- while (i--) {
- node = nodes[i];
- classList = node.attr('class').split(' ');
- classValue = '';
-
- for (ci = 0; ci < classList.length; ci++) {
- className = classList[ci];
- valid = false;
-
- validClassesMap = validClasses['*'];
- if (validClassesMap && validClassesMap[className]) {
- valid = true;
- }
-
- validClassesMap = validClasses[node.name];
- if (!valid && validClassesMap && validClassesMap[className]) {
- valid = true;
- }
-
- if (valid) {
- if (classValue) {
- classValue += ' ';
- }
-
- classValue += className;
- }
- }
-
- if (!classValue.length) {
- classValue = null;
- }
-
- node.attr('class', classValue);
- }
- });
- }
- };
-});