mirror of
https://github.com/processwire/processwire.git
synced 2025-08-09 16:26:59 +02:00
InputfieldTinyMCE: Add support for new "remove all style attributes" option to the Markup Toggle settings. Plus refactoring of the pasteFilter JS in attempt to fix processwire/processwire-issues#1866 which should improve pasting from MS Word.
This commit is contained in:
@@ -598,7 +598,7 @@ var InputfieldTinyMCE = {
|
||||
|
||||
var t = InputfieldTinyMCE;
|
||||
var allow = ',' + ProcessWire.config.InputfieldTinyMCE.pasteFilter + ',';
|
||||
var regexTag = /<([a-z0-9]+)([^>]*)>/gi;
|
||||
var regexTag = /<([a-z0-9:!\[\]]+)([^>]*)>/gi;
|
||||
var regexAttr = /([-_a-z0-9]+)=["']([^"']*)["']/gi;
|
||||
var html = args.content;
|
||||
var matchTag, matchAttr;
|
||||
@@ -607,13 +607,30 @@ var InputfieldTinyMCE = {
|
||||
var replaces = [];
|
||||
var startLength = html.length;
|
||||
|
||||
allow = allow.toLowerCase();
|
||||
|
||||
/*
|
||||
* HTML for testing MS word paste
|
||||
msWordHtml =
|
||||
"<p className=MsoNormal>This is <b>bold</b> text. <o:p></o:p></p>\n\n" +
|
||||
"<h2>This is headline 2. <o:p></o:p></h2>\n\n" +
|
||||
"<p className=MsoNormal>This is <I>italic</I> text<o:p></o:p></p>\n\n" +
|
||||
"<p className=MsoListParagraphCxSpFirst style='text-indent:-.25in;mso-list:10 level1 lfo1'>" +
|
||||
"<![if !supportsLists]><span style='iso-bidi-font-family:Aptos;mso-bidi-theme-font:minor-latin'>" +
|
||||
"<span style='so-list:Ignore'>1.<span style='font:7.0pt \"Times New Roman\"'>" +
|
||||
" </span></span></span><![endif]>One <o:p></o:p></p>";
|
||||
html = msWordHtml;
|
||||
*/
|
||||
|
||||
startLength = html.length;
|
||||
|
||||
if(args.internal) {
|
||||
t.log('Skipping pasteFilter for interal copy/paste');
|
||||
return; // skip filtering for internal copy/paste operations
|
||||
}
|
||||
|
||||
if(allow === ',text,') {
|
||||
t.log('Skipping pasteFilter since paste_as_text settingw will be used');
|
||||
t.log('Skipping pasteFilter since paste_as_text settings will be used');
|
||||
return; // will be processed by paste_as_text setting
|
||||
}
|
||||
|
||||
@@ -621,17 +638,19 @@ var InputfieldTinyMCE = {
|
||||
|
||||
var tagOpen = matchTag[0]; // i.e. <strong>, <img src="..">, <h2>, etc.
|
||||
var tagName = matchTag[1]; // i.e. 'strong', 'img', 'h2', etc.
|
||||
var tagNameLower = tagName.toLowerCase();
|
||||
var tagClose = '</' + tagName + '>'; // i.e. </strong>, </h2>
|
||||
var tagAttrs = matchTag[2]; // i.e. 'src="a.jpg" alt="alt"'
|
||||
var allowAttrs = false;
|
||||
|
||||
// first see if we can match a tag replacement
|
||||
var find = ',' + tagName + '='; // i.e. ',b=strong'
|
||||
var pos = allow.indexOf(find);
|
||||
var findTagEqual = ',' + tagNameLower + '='; // i.e. ',b=strong'
|
||||
var findTagAttr = ',' + tagNameLower + '['; // i.e. ',b[...]'
|
||||
var findTagOnly = ',' + tagNameLower + ','; // i.e. ,b
|
||||
var posTagEqual = allow.indexOf(findTagEqual);
|
||||
|
||||
if(pos > -1) {
|
||||
// tag replacement
|
||||
var rule = allow.substring(pos + 1); // i.e. b=strong,and,more
|
||||
if(posTagEqual > -1) {
|
||||
var rule = allow.substring(posTagEqual + 1); // i.e. b=strong,and,more
|
||||
rule = rule.substring(0, rule.indexOf(',')); // i.e. b=strong
|
||||
rule = rule.split('=');
|
||||
var replaceTag = rule[1];
|
||||
@@ -640,12 +659,12 @@ var InputfieldTinyMCE = {
|
||||
finds.push(tagClose);
|
||||
replaces.push('</' + replaceTag + '>');
|
||||
}
|
||||
|
||||
if(allow.indexOf(',' + tagName + '[') > -1) {
|
||||
|
||||
if(allow.indexOf(findTagAttr) > -1) {
|
||||
// tag appears in whitelist with attributes
|
||||
allowAttrs = true;
|
||||
} else if(allow.indexOf(',' + tagName + ',') === -1) {
|
||||
// tag does not appear in whitelist
|
||||
} else if(posTagEqual === -1 && allow.indexOf(findTagOnly) === -1) {
|
||||
// tag does not appear in whitelist at all
|
||||
removals.push(tagOpen);
|
||||
removals.push(tagClose);
|
||||
continue;
|
||||
|
@@ -74,7 +74,7 @@ class InputfieldTinyMCE extends InputfieldTextarea implements ConfigurableModule
|
||||
return array(
|
||||
'title' => 'TinyMCE',
|
||||
'summary' => 'TinyMCE rich text editor version ' . self::mceVersion . '.',
|
||||
'version' => 617,
|
||||
'version' => 618,
|
||||
'icon' => 'keyboard-o',
|
||||
'requires' => 'ProcessWire>=3.0.200, MarkupHTMLPurifier',
|
||||
);
|
||||
@@ -89,6 +89,7 @@ class InputfieldTinyMCE extends InputfieldTextarea implements ConfigurableModule
|
||||
const toggleCleanDiv = 2; // remove <div>s
|
||||
const toggleCleanP = 4; // remove empty <p> tags
|
||||
const toggleCleanNbsp = 8; // remove entities
|
||||
const toggleRemoveStyles = 16; // remove all style attributes
|
||||
|
||||
/**
|
||||
* Default configuration for filtered paste
|
||||
|
@@ -711,6 +711,7 @@ class InputfieldTinyMCEConfigs extends InputfieldTinyMCEClass {
|
||||
$f->addOption(InputfieldTinyMCE::toggleCleanDiv, $this->_('Convert `<div>` tags to `<p>` tags on save?'));
|
||||
$f->addOption(InputfieldTinyMCE::toggleCleanP, $this->_('Remove empty `<p>` tags on save?'));
|
||||
$f->addOption(InputfieldTinyMCE::toggleCleanNbsp, $this->_('Remove non-breaking spaces on save?'));
|
||||
$f->addOption(InputfieldTinyMCE::toggleRemoveStyles, $this->_('Remove `style` attributes from all elements'));
|
||||
$f->attr('value', $this->inputfield->toggles);
|
||||
$f->collapsed = Inputfield::collapsedYes;
|
||||
$f->themeOffset = 1;
|
||||
@@ -1385,4 +1386,4 @@ class InputfieldTinyMCEConfigs extends InputfieldTinyMCEClass {
|
||||
'usePurifier',
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -162,25 +162,41 @@ class InputfieldTinyMCETools extends InputfieldTinyMCEClass {
|
||||
// convert <div> to paragraphs
|
||||
$toggles = $this->inputfield->toggles;
|
||||
if(!is_array($toggles)) return $value;
|
||||
|
||||
if(in_array(InputfieldTinyMCE::toggleCleanDiv, $toggles) && strpos($value, '<div') !== false) {
|
||||
$value = preg_replace('{\s*(</?)div[^><]*>\s*}is', '$1' . 'p>', $value);
|
||||
while(strpos($value, '<p><p>') !== false) {
|
||||
$value = str_replace(array('<p><p>', '</p></p>'), array('<p>', '</p>'), $value);
|
||||
|
||||
foreach($toggles as $toggle) {
|
||||
switch($toggle) {
|
||||
case InputfieldTinyMCE::toggleCleanDiv:
|
||||
// convert <div> to <p>
|
||||
if(strpos($value, '<div') !== false) {
|
||||
$value = preg_replace('{\s*(</?)div[^><]*>\s*}is', '$1' . 'p>', $value);
|
||||
while(strpos($value, '<p><p>') !== false) {
|
||||
$value = str_replace(array('<p><p>', '</p></p>'), array('<p>', '</p>'), $value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InputfieldTinyMCE::toggleCleanP:
|
||||
// remove empty paragraphs
|
||||
$value = str_replace(array('<p><br /></p>', '<p> </p>', "<p>\xc2\xa0</p>", '<p></p>', '<p> </p>'), '', $value);
|
||||
break;
|
||||
case InputfieldTinyMCE::toggleCleanNbsp:
|
||||
// convert non-breaking space to regular space
|
||||
$value = str_ireplace(' ', ' ', $value);
|
||||
$value = str_replace("\xc2\xa0",' ', $value);
|
||||
break;
|
||||
case InputfieldTinyMCE::toggleRemoveStyles:
|
||||
// remove all style attributes
|
||||
if(strpos($value, 'style=')) {
|
||||
if(preg_match_all('!(<.+?)\sstyle=(["\']).*?\2!i', $value, $matches)) {
|
||||
foreach($matches[0] as $key => $fullMatch) {
|
||||
$startMatch = $matches[1][$key];
|
||||
$value = str_replace($fullMatch, $startMatch, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// remove gratuitous whitespace
|
||||
if(in_array(InputfieldTinyMCE::toggleCleanP, $toggles)) {
|
||||
$value = str_replace(array('<p><br /></p>', '<p> </p>', "<p>\xc2\xa0</p>", '<p></p>', '<p> </p>'), '', $value);
|
||||
}
|
||||
|
||||
// convert non-breaking space to regular space
|
||||
if(in_array(InputfieldTinyMCE::toggleCleanNbsp, $toggles)) {
|
||||
$value = str_ireplace(' ', ' ', $value);
|
||||
$value = str_replace("\xc2\xa0",' ', $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user