Feature #23832 » rtehtmlarea_feature_16118.patch
typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js (copie de travail) | ||
---|---|---|
reservedClassNames : /htmlarea/,
|
||
RE_email : /([0-9a-z]+([a-z0-9_-]*[0-9a-z])*){1}(\.[0-9a-z]+([a-z0-9_-]*[0-9a-z])*)*@([0-9a-z]+([a-z0-9_-]*[0-9a-z])*\.)+[a-z]{2,9}/i,
|
||
RE_url : /(([^:/?#]+):\/\/)?(([a-z0-9_]+:[a-z0-9_]+@)?[a-z0-9_-]{2,}(\.[a-z0-9_-]{2,})+\.[a-z]{2,5}(:[0-9]+)?(\/\S+)*)/i,
|
||
RE_blockTags : /^(body|p|h1|h2|h3|h4|h5|h6|ul|ol|pre|dl|dt|dd|div|noscript|blockquote|form|hr|table|caption|fieldset|address|td|tr|th|li|tbody|thead|tfoot|iframe)$/,
|
||
RE_closingTags : /^(p|blockquote|a|li|ol|ul|dl|dt|td|th|tr|tbody|thead|tfoot|caption|colgroup|table|div|b|bdo|big|cite|code|del|dfn|em|i|ins|kbd|label|q|samp|small|span|strike|strong|sub|sup|tt|u|var|abbr|acronym|font|center|object|embed|style|script|title|head)$/,
|
||
RE_noClosingTag : /^(img|br|hr|col|input|area|base|link|meta|param)$/,
|
||
RE_blockTags : /^(body|p|h1|h2|h3|h4|h5|h6|ul|ol|pre|dl|dt|dd|div|noscript|blockquote|form|hr|table|caption|fieldset|address|td|tr|th|li|tbody|thead|tfoot|iframe)$/i,
|
||
RE_closingTags : /^(p|blockquote|a|li|ol|ul|dl|dt|td|th|tr|tbody|thead|tfoot|caption|colgroup|table|div|b|bdo|big|cite|code|del|dfn|em|i|ins|kbd|label|q|samp|small|span|strike|strong|sub|sup|tt|u|var|abbr|acronym|font|center|object|embed|style|script|title|head)$/i,
|
||
RE_noClosingTag : /^(img|br|hr|col|input|area|base|link|meta|param)$/i,
|
||
RE_numberOrPunctuation : /[0-9.(),;:!¡?¿%#$'"_+=\\\/-]*/g
|
||
});
|
||
/***************************************************
|
||
... | ... | |
this.enableMozillaExtension = true;
|
||
this.disableEnterParagraphs = false;
|
||
this.disableObjectResizing = false;
|
||
this.removeTrailingBR = false;
|
||
this.removeTrailingBR = true;
|
||
// style included in the iframe document
|
||
this.editedContentStyle = HTMLArea.editedContentCSS;
|
||
// content style
|
||
this.pageStyle = "";
|
||
// remove tags (these have to be a regexp, or null if this functionality is not desired)
|
||
this.htmlRemoveTags = null;
|
||
// remove tags and any contents (these have to be a regexp, or null if this functionality is not desired)
|
||
this.htmlRemoveTagsAndContents = null;
|
||
// remove comments
|
||
// Remove tags (must be a regular expression)
|
||
this.htmlRemoveTags = /none/i;
|
||
// Remove tags and their contents (must be a regular expression)
|
||
this.htmlRemoveTagsAndContents = /none/i;
|
||
// Remove comments
|
||
this.htmlRemoveComments = false;
|
||
// custom tags (these have to be a regexp, or null if this functionality is not desired)
|
||
this.customTags = null;
|
||
// Custom tags (must be a regular expression)
|
||
this.customTags = /none/i;
|
||
// BaseURL to be included in the iframe document
|
||
this.baseURL = document.baseURI || document.URL;
|
||
if (this.baseURL && this.baseURL.match(/(.*\:\/\/.*\/)[^\/]*/)) {
|
||
... | ... | |
}
|
||
});
|
||
this.config = this.getEditor().config;
|
||
this.htmlRenderer = new HTMLArea.DOM.Walker({
|
||
keepComments: !this.config.htmlRemoveComments,
|
||
removeTags: this.config.htmlRemoveTags,
|
||
removeTagsAndContents: this.config.htmlRemoveTagsAndContents
|
||
});
|
||
if (!this.config.showStatusBar) {
|
||
this.addClass('noStatusBar');
|
||
}
|
||
... | ... | |
}
|
||
},
|
||
/*
|
||
* Instance of DOM walker
|
||
*/
|
||
htmlRenderer: {},
|
||
/*
|
||
* Get the HTML content of the iframe
|
||
*/
|
||
getHTML: function () {
|
||
return HTMLArea.getHTML(this.document.body, false, this.getEditor());
|
||
return this.htmlRenderer.render(this.document.body, false);
|
||
},
|
||
/*
|
||
* Start listening to things happening in the iframe
|
||
... | ... | |
},
|
||
/*
|
||
* Retrieve the HTML
|
||
* In the case of the wysiwyg mode, the html content is parsed
|
||
* In the case of the wysiwyg mode, the html content is rendered from the DOM tree
|
||
*
|
||
* @return string the textual html content from the current editing mode
|
||
*/
|
||
... | ... | |
str = str.replace(/\x22/g, """); // \x22 means '"'
|
||
return str;
|
||
};
|
||
/*
|
||
* Retrieve the HTML code from the given node.
|
||
* This is a replacement for getting innerHTML, using standard DOM calls.
|
||
* Wrapper catches a Mozilla-Exception with non well-formed html source code.
|
||
***********************************************
|
||
* THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.5 *
|
||
***********************************************
|
||
*/
|
||
HTMLArea.getHTML = function(root, outputRoot, editor){
|
||
try {
|
||
return HTMLArea.getHTMLWrapper(root,outputRoot,editor);
|
||
return editor.iframe.htmlRenderer.render(root, outputRoot);
|
||
} catch(e) {
|
||
HTMLArea._appendToLog('[HTMLArea::getHTML]: The HTML document is not well-formed.');
|
||
if (!HTMLArea.enableDebugMode) {
|
||
... | ... | |
});
|
||
return editor.document.body.innerHTML;
|
||
} else {
|
||
return HTMLArea.getHTMLWrapper(root,outputRoot,editor);
|
||
return editor.iframe.htmlRenderer.render(root, outputRoot);
|
||
}
|
||
}
|
||
};
|
||
HTMLArea.getHTMLWrapper = function(root, outputRoot, editor) {
|
||
var html = "";
|
||
if(!root) return html;
|
||
switch (root.nodeType) {
|
||
case 1: // ELEMENT_NODE
|
||
case 11: // DOCUMENT_FRAGMENT_NODE
|
||
case 9: // DOCUMENT_NODE
|
||
var closed, i, config = editor.config;
|
||
var root_tag = (root.nodeType == 1) ? root.tagName.toLowerCase() : '';
|
||
if (root_tag == "br" && config.removeTrailingBR && !root.nextSibling && HTMLArea.isBlockElement(root.parentNode) && (!root.previousSibling || root.previousSibling.nodeName.toLowerCase() != "br")) {
|
||
if (!root.previousSibling && root.parentNode && root.parentNode.nodeName.toLowerCase() == "p" && root.parentNode.className) html += " ";
|
||
break;
|
||
}
|
||
if (config.htmlRemoveTagsAndContents && config.htmlRemoveTagsAndContents.test(root_tag)) break;
|
||
var custom_tag = (config.customTags && config.customTags.test(root_tag));
|
||
if (outputRoot) outputRoot = !(config.htmlRemoveTags && config.htmlRemoveTags.test(root_tag));
|
||
if (outputRoot) {
|
||
if (Ext.isGecko && root.hasAttribute('_moz_editor_bogus_node')) break;
|
||
closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root) || custom_tag));
|
||
html = "<" + root_tag;
|
||
var a, name, value, attrs = root.attributes;
|
||
var n = attrs.length;
|
||
for (i = attrs.length; --i >= 0 ;) {
|
||
a = attrs.item(i);
|
||
name = a.nodeName.toLowerCase();
|
||
if ((!a.specified && name != 'value') || /_moz|contenteditable|_msh|complete/.test(name)) continue;
|
||
if (!Ext.isIE || name != "style") {
|
||
// IE5.5 reports wrong values. For this reason we extract the values directly from the root node.
|
||
// Using Gecko the values of href and src are converted to absolute links unless we get them using nodeValue()
|
||
if (typeof(root[a.nodeName]) != "undefined" && name != "href" && name != "src" && name != "style" && !/^on/.test(name)) {
|
||
value = root[a.nodeName];
|
||
} else {
|
||
value = a.nodeValue;
|
||
if (Ext.isIE && (name == "href" || name == "src") && editor.plugins.link && editor.plugins.link.instance && editor.plugins.link.instance.stripBaseURL) {
|
||
value = editor.plugins.link.instance.stripBaseURL(value);
|
||
}
|
||
}
|
||
} else { // IE fails to put style in attributes list.
|
||
value = root.style.cssText;
|
||
}
|
||
// Mozilla reports some special values; we don't need them.
|
||
if(/(_moz|^$)/.test(value)) continue;
|
||
// Strip value="0" reported by IE on all li tags
|
||
if(Ext.isIE && root_tag == "li" && name == "value" && value == 0) continue;
|
||
// Strip id generated by ExtJS
|
||
if (name === 'id' && value.substr(0, 7) === 'ext-gen') {
|
||
continue;
|
||
}
|
||
html += " " + name + '="' + HTMLArea.htmlEncode(value) + '"';
|
||
}
|
||
if (html != "") html += closed ? " />" : ">";
|
||
}
|
||
for (i = root.firstChild; i; i = i.nextSibling) {
|
||
if (/^li$/i.test(i.tagName) && !/^[ou]l$/i.test(root.tagName)) html += "<ul>" + HTMLArea.getHTMLWrapper(i, true, editor) + "</ul>";
|
||
else html += HTMLArea.getHTMLWrapper(i, true, editor);
|
||
}
|
||
if (outputRoot && !closed) html += "</" + root_tag + ">";
|
||
break;
|
||
case 3: // TEXT_NODE
|
||
html = /^(script|style)$/i.test(root.parentNode.tagName) ? root.data : HTMLArea.htmlEncode(root.data);
|
||
break;
|
||
case 8: // COMMENT_NODE
|
||
if (!editor.config.htmlRemoveComments) html = "<!--" + root.data + "-->";
|
||
break;
|
||
case 4: // Node.CDATA_SECTION_NODE
|
||
// Mozilla seems to convert CDATA into a comment when going into wysiwyg mode, don't know about IE
|
||
html += '<![CDATA[' + root.data + ']]>';
|
||
break;
|
||
case 5: // Node.ENTITY_REFERENCE_NODE
|
||
html += '&' + root.nodeValue + ';';
|
||
break;
|
||
case 7: // Node.PROCESSING_INSTRUCTION_NODE
|
||
// PI's don't seem to survive going into the wysiwyg mode, (at least in moz) so this is purely academic
|
||
html += '<?' + root.target + ' ' + root.data + ' ?>';
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return html;
|
||
};
|
||
HTMLArea.getPrevNode = function(node) {
|
||
if(!node) return null;
|
||
if(node.previousSibling) return node.previousSibling;
|
||
... | ... | |
pN.removeChild(el);
|
||
return el;
|
||
};
|
||
/*****************************************************************
|
||
* HTMLArea.DOM: Utility functions for dealing with the DOM tree *
|
||
*****************************************************************/
|
||
/***************************************************
|
||
* DOM-RELATED CONSTANTS
|
||
***************************************************/
|
||
HTMLArea.DOM = function () {
|
||
return {
|
||
// DOM node types
|
||
ELEMENT_NODE: 1,
|
||
ATTRIBUTE_NODE: 2,
|
||
TEXT_NODE: 3,
|
||
CDATA_SECTION_NODE: 4,
|
||
ENTITY_REFERENCE_NODE: 5,
|
||
ENTITY_NODE: 6,
|
||
PROCESSING_INSTRUCTION_NODE: 7,
|
||
COMMENT_NODE: 8,
|
||
DOCUMENT_NODE: 9,
|
||
DOCUMENT_TYPE_NODE: 10,
|
||
DOCUMENT_FRAGMENT_NODE: 11,
|
||
NOTATION_NODE: 12
|
||
};
|
||
}();
|
||
/***************************************************
|
||
* HTMLArea.DOM.Walker: DOM tree walk
|
||
***************************************************/
|
||
HTMLArea.DOM.Walker = function (config) {
|
||
var configDefaults = {
|
||
keepComments: false,
|
||
keepCDATASections: false,
|
||
removeTags: /none/i,
|
||
removeTagsAndContents: /none/i,
|
||
keepTags: /.*/i,
|
||
removeAttributes: /none/i,
|
||
removeTrailingBR: true
|
||
}
|
||
Ext.apply(this, config, configDefaults);
|
||
};
|
||
HTMLArea.DOM.Walker = Ext.extend(HTMLArea.DOM.Walker, {
|
||
/*
|
||
* Walk the DOM tree
|
||
*
|
||
* @param object node: the root node of the tree
|
||
* @param boolean includeNode: if set, apply callback to the node
|
||
* @param string startCallback: a function call to be evaluated on each node, before walking the children
|
||
* @param string endCallback: a function call to be evaluated on each node, after walking the children
|
||
* @param array args: array of arguments
|
||
* @return void
|
||
*/
|
||
walk: function (node, includeNode, startCallback, endCallback, args) {
|
||
if (!this.removeTagsAndContents.test(node.nodeName)) {
|
||
if (includeNode) {
|
||
eval(startCallback);
|
||
}
|
||
// Walk the children
|
||
var child = node.firstChild;
|
||
while (child) {
|
||
this.walk(child, true, startCallback, endCallback, args);
|
||
child = child.nextSibling;
|
||
}
|
||
if (includeNode) {
|
||
eval(endCallback);
|
||
}
|
||
}
|
||
},
|
||
/*
|
||
* Generate html string from DOM tree
|
||
*
|
||
* @param object node: the root node of the tree
|
||
* @param boolean includeNode: if set, apply callback to root element
|
||
* @return string rendered html code
|
||
*/
|
||
render: function (node, includeNode) {
|
||
this.html = '';
|
||
this.walk(node, includeNode, 'args[0].renderNodeStart(node)', 'args[0].renderNodeEnd(node)', [this]);
|
||
return this.html;
|
||
},
|
||
/*
|
||
* Generate html string for the start of a node
|
||
*
|
||
* @param object node: the root node of the tree
|
||
* @return string rendered html code (accumulated in this.html)
|
||
*/
|
||
renderNodeStart: function (node) {
|
||
var html = '';
|
||
switch (node.nodeType) {
|
||
case HTMLArea.DOM.ELEMENT_NODE:
|
||
if (this.keepTags.test(node.nodeName) && !this.removeTags.test(node.nodeName)) {
|
||
html += this.setOpeningTag(node);
|
||
}
|
||
break;
|
||
case HTMLArea.DOM.TEXT_NODE:
|
||
html += /^(script|style)$/i.test(node.parentNode.nodeName) ? node.data : HTMLArea.htmlEncode(node.data);
|
||
break;
|
||
case HTMLArea.DOM.ENTITY_NODE:
|
||
html += node.nodeValue;
|
||
break;
|
||
case HTMLArea.DOM.ENTITY_REFERENCE_NODE:
|
||
html += '&' + node.nodeValue + ';';
|
||
break;
|
||
case HTMLArea.DOM.COMMENT_NODE:
|
||
if (this.keepComments) {
|
||
html += '<!--' + node.data + '-->';
|
||
}
|
||
break;
|
||
case HTMLArea.DOM.CDATA_SECTION_NODE:
|
||
if (this.keepCDATASections) {
|
||
html += '<![CDATA[' + node.data + ']]>';
|
||
}
|
||
break;
|
||
default:
|
||
// Ignore all other node types
|
||
break;
|
||
}
|
||
this.html += html;
|
||
},
|
||
/*
|
||
* Generate html string for the end of a node
|
||
*
|
||
* @param object node: the root node of the tree
|
||
* @return string rendered html code (accumulated in this.html)
|
||
*/
|
||
renderNodeEnd: function (node) {
|
||
var html = '';
|
||
if (node.nodeType == HTMLArea.DOM.ELEMENT_NODE) {
|
||
if (this.keepTags.test(node.nodeName) && !this.removeTags.test(node.nodeName)) {
|
||
html += this.setClosingTag(node);
|
||
}
|
||
}
|
||
this.html += html;
|
||
},
|
||
/*
|
||
* Get the attributes of the node, filtered and cleaned-up
|
||
*
|
||
* @param object node: the node
|
||
* @return object an object with attribute name as key and attribute value as value
|
||
*/
|
||
getAttributes: function (node) {
|
||
var attributes = node.attributes;
|
||
var filterededAttributes = {};
|
||
var attribute, attributeName, attributeValue;
|
||
for (var i = attributes.length; --i >= 0 ;) {
|
||
attribute = attributes.item(i);
|
||
attributeName = attribute.nodeName.toLowerCase();
|
||
attributeValue = attribute.nodeValue;
|
||
// Ignore some attributes and those configured to be removed
|
||
if (/_moz|contenteditable|complete/.test(attributeName) || this.removeAttributes.test(attributeName)) {
|
||
continue;
|
||
}
|
||
// Ignore default values except for the value attribute
|
||
if (!attribute.specified && attributeName !== 'value') {
|
||
continue;
|
||
}
|
||
if (Ext.isIE) {
|
||
// IE fails to put style in attributes list.
|
||
if (attributeName === 'style') {
|
||
attributeValue = node.style.cssText;
|
||
// May need to strip the base url
|
||
} else if (attributeName === 'href' || attributeName === 'src') {
|
||
attributeValue = this.stripBaseURL(attributeValue);
|
||
// Ignore value="0" reported by IE on all li elements
|
||
} else if (attributeName === 'value' && /^li$/i.test(node.nodeName) && attributeValue == 0) {
|
||
continue;
|
||
}
|
||
// Ignore special values reported by Mozilla
|
||
} else if (Ext.isGecko && /(_moz|^$)/.test(attributeValue)) {
|
||
continue;
|
||
}
|
||
// Ignore id attributes generated by ExtJS
|
||
if (attributeName === 'id' && /^ext-gen/.test(attributeValue)) {
|
||
continue;
|
||
}
|
||
filterededAttributes[attributeName] = attributeValue;
|
||
}
|
||
return filterededAttributes;
|
||
},
|
||
/*
|
||
* Set opening tag for a node
|
||
*
|
||
* @param object node: the node
|
||
* @return object opening tag
|
||
*/
|
||
setOpeningTag: function (node) {
|
||
var html = '';
|
||
// Handle br oddities
|
||
if (/^br$/i.test(node.nodeName)) {
|
||
// Remove Mozilla special br node
|
||
if (Ext.isGecko && node.hasAttribute('_moz_editor_bogus_node')) {
|
||
return html;
|
||
// In Gecko, whenever some text is entered in an empty block, a trailing br tag is added by the browser.
|
||
// If the br element is a trailing br in a block element with no other content or with content other than a br, it may be configured to be removed
|
||
} else if (this.removeTrailingBR && !node.nextSibling && HTMLArea.isBlockElement(node.parentNode) && (!node.previousSibling || !/^br$/i.test(node.previousSibling.nodeName))) {
|
||
// If an empty paragraph with a class attribute, insert a non-breaking space so that RTE transform does not clean it away
|
||
if (!node.previousSibling && node.parentNode && /^p$/i.test(node.parentNode.nodeName) && node.parentNode.className) {
|
||
html += " ";
|
||
}
|
||
return html;
|
||
}
|
||
}
|
||
// Normal node
|
||
var attributes = this.getAttributes(node);
|
||
for (var attributeName in attributes) {
|
||
html += ' ' + attributeName + '="' + HTMLArea.htmlEncode(attributes[attributeName]) + '"';
|
||
}
|
||
html = '<' + node.nodeName.toLowerCase() + html + (HTMLArea.RE_noClosingTag.test(node.nodeName) ? ' />' : '>');
|
||
// Fix orphan list elements
|
||
if (/^li$/i.test(node.nodeName) && !/^[ou]l$/i.test(node.parentNode.nodeName)) {
|
||
html = '<ul>' + html;
|
||
}
|
||
return html;
|
||
},
|
||
/*
|
||
* Set closing tag for a node
|
||
*
|
||
* @param object node: the node
|
||
* @return object closing tag, if required
|
||
*/
|
||
setClosingTag: function (node) {
|
||
var html = HTMLArea.RE_noClosingTag.test(node.nodeName) ? '' : '</' + node.nodeName.toLowerCase() + '>';
|
||
// Fix orphan list elements
|
||
if (/^li$/i.test(node.nodeName) && !/^[ou]l$/i.test(node.parentNode.nodeName)) {
|
||
html += '</ul>';
|
||
}
|
||
return html;
|
||
},
|
||
/*
|
||
* Strip base url
|
||
* May be overridden by link handling plugin
|
||
*
|
||
* @param string value: value of a href or src attribute
|
||
* @return tring stripped value
|
||
*/
|
||
stripBaseURL: function (value) {
|
||
return value;
|
||
}
|
||
});
|
||
/***************************************************
|
||
* TIPS ON FORM FIELDS AND MENU ITEMS
|
||
***************************************************/
|
||
/*
|
typo3/sysext/rtehtmlarea/htmlarea/plugins/DefaultLink/default-link.js (copie de travail) | ||
---|---|---|
}
|
||
},
|
||
/*
|
||
* This function gets called when the editor is generated
|
||
*/
|
||
onGenerate: function () {
|
||
if (Ext.isIE) {
|
||
this.editor.iframe.htmlRenderer.stripBaseUrl = this.stripBaseUrl;
|
||
}
|
||
},
|
||
/*
|
||
* This function gets called when the button was pressed.
|
||
*
|
||
* @param object editor: the editor instance
|