Skip to content

Commit

Permalink
fix: #395 #399 #400
Browse files Browse the repository at this point in the history
fix: maintain style of span tag when pasting
add: core-managedTagsInfo, plugins-managedTags
  • Loading branch information
JiHong88 committed Jun 15, 2020
1 parent c969d3e commit 2eac388
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 68 deletions.
42 changes: 42 additions & 0 deletions sample/html/out/document-editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/prettify-jsdoc.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.min.css">
</head>

<body>
Expand Down Expand Up @@ -546,6 +547,46 @@ <h4 class="name" id="activePlugins"><span class="type-signature"></span>activePl
<dl class="details"></dl>


<h4 class="name" id="managedTagsInfo"><span class="type-signature"></span>managedTagsInfo<span
class="type-signature"></span></h4>
<div class="description">

@description Information of tags that should maintain HTML structure, style, class name, etc. (In use by "math" plugin)<br>
When inserting "html" such as paste, it is executed on the "html" to be inserted. (core.cleanHTML)<br>
Basic Editor Actions:<br>
1. All classes not starting with "__se__" or "se-" in the editor are removed.<br>
2. The style of all tags except the "span" tag is removed from the editor.<br><br>

<span style="color:#ffe4c4;">"managedTagsInfo" structure ex:</span>
<div class="markdown-body">
<div class="highlight highlight-source-js"><pre><span class="pl-en">managedTagsInfo</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos"></span> <span class="pl-kos">{</span>
query: <span class="pl-s">'.__se__xxx, se-xxx'</span>
map: <span class="pl-kos">{</span>
<span class="pl-s">'__se__xxx'</span>: <span class="pl-s1">method</span><span class="pl-kos">.</span><span class="pl-en">bind</span><span class="pl-kos">(</span><span class="pl-s1">core</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
<span class="pl-s">'se-xxx'</span>: <span class="pl-s1">method</span><span class="pl-kos">.</span><span class="pl-en">bind</span><span class="pl-kos">(</span><span class="pl-s1">core</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
<span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
</div>
<br>
<span style="color:#ffe4c4;">@example</span><br>
<span style="color:#ffe4c4;">Define in the following return format in the "managedTagInfo" function of the plugin.</span>
<div class="markdown-body">
<div class="highlight highlight-source-js"><pre><span class="pl-en">managedTagInfo</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
<span class="pl-k">return</span> <span class="pl-kos">{</span>
<span class="pl-c1">className</span>: <span class="pl-s">'string'</span><span class="pl-kos">,</span> <span class="pl-c">// Class name to identify the tag. ("__se__xxx", "se-xxx")</span>
<span class="pl-c">// Change the html of the "element". ("element" is the element found with "className".)</span>
<span class="pl-c">// "method" is executed by binding "core".</span>
<span class="pl-en">method</span>: <span class="pl-k">function</span> <span class="pl-kos">(</span><span class="pl-s1">element</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
<span class="pl-c">// this === core</span>
<span class="pl-s1">element</span><span class="pl-kos">.</span><span class="pl-c1">innerHTML</span> <span class="pl-c1">=</span> <span class="pl-c">// (rendered html);</span>
<span class="pl-kos">}</span>
<span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
</div>
</div>
<dl class="details"></dl>


<h4 class="name" id="commandMap"><span class="type-signature"></span>commandMap<span
class="type-signature"></span></h4>
<div class="description">
Expand Down Expand Up @@ -2003,6 +2044,7 @@ <h3>core</h3>
<li><a href="document-editor.html#pasteTagsWhitelistRegExp">pasteTagsWhitelistRegExp</a></li>
<li><a href="document-editor.html#hasFocus">hasFocus</a></li>
<li><a href="document-editor.html#activePlugins">activePlugins</a></li>
<li><a href="document-editor.html#managedTagsInfo">managedTagsInfo</a></li>
<li><a href="document-editor.html#commandMap">commandMap</a></li>
<li><a href="document-editor.html#_variable">_variable</a></li>
<li><b>Methods</b></li>
Expand Down
47 changes: 46 additions & 1 deletion sample/html/out/document-module.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<div id="main">
<!--<h1 class="page-title">Global</h1>-->
<section>
<section>d
<header>
<h2>Modules</h2>
</header>
Expand All @@ -32,6 +32,7 @@ <h2>Modules</h2>
<dd><span style="color: #f4b124;">- component : </span> image, video (<a style="color:#ff5565" href="../customPlugins.html#dialog_component_filemanager">example</a>)</dd>
<dd><span style="color: #f4b124;">- fileManager : </span> image, video (<a style="color:#ff5565" href="../customPlugins.html#dialog_component_filemanager">example</a>)</dd>
<dd><span style="color: #f4b124;">- resizing : </span> image, video</dd>
<dd><span style="color: #f4b124;">- fileBrowser : </span> imageGallery</dd>
</dl>
</div>
<h3 class="subsection-title"></h3>
Expand Down Expand Up @@ -324,6 +325,49 @@ <h3 class="subsection-title" id="titleResizing">resizing</h3>
<span class="pl-c"> */</span>
<span class="pl-c1">cancel_controller_resize</span><span class="pl-kos">(</span><span class="pl-s1">direction</span>: <span class="pl-smi">string</span><span class="pl-kos">)</span>: <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>

<!-- fileBrowser -->
<h3 class="subsection-title" id="titleFileBrowser">fileBrowser</h3>
<div class="highlight highlight-source-ts"><pre><span class="pl-c">/**</span>
<span class="pl-c"> * <span class="pl-k">@description</span> This is a required module of fileBrowser plugin.</span>
<span class="pl-c"> Require context properties when using fileBrowser module:</span>
<span class="pl-c"> title(@Required): "File browser window title",</span>
<span class="pl-c"> url(@Required): "File server url",</span>
<span class="pl-c"> listClass(@Required): "Class name of list div",</span>
<span class="pl-c"> itemTemplateHandler(@Required): "Function that defines the HTML of an file item",</span>
<span class="pl-c"> selectorHandler(@Required): "Function that action when item click",</span>
<span class="pl-c"> columnSize(@Option): "Number of "div.se-file-item-column" to be created (default: 4)"</span>
<span class="pl-c">*/</span>
<span class="pl-k">declare</span> <span class="pl-k">interface</span> <span class="pl-smi">fileBrowser</span> <span class="pl-k">extends</span> <span class="pl-smi">Module</span> <span class="pl-kos">{</span>
<span class="pl-c">/**</span>
<span class="pl-c"> * <span class="pl-k">@description</span> Open a file browser window</span>
<span class="pl-c"> * <span class="pl-k">@param</span> pluginName Plugin name using the file browser</span>
<span class="pl-c"> * <span class="pl-k">@param</span> selectorHandler When the function comes as an argument value, it substitutes "context.selectorHandler".</span>
<span class="pl-c"> * <span class="pl-k">@example</span> this.plugins.fileBrowser.open.call(this, 'imageGallery', (selectorHandler || null));</span>
<span class="pl-c"> */</span>
<span class="pl-c1">open</span><span class="pl-kos">(</span><span class="pl-s1">kind</span>: <span class="pl-smi">string</span><span class="pl-kos">,</span> <span class="pl-s1">update</span>: <span class="pl-smi">boolean</span><span class="pl-kos">)</span>: <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">;</span>

<span class="pl-c">/**</span>
<span class="pl-c"> * <span class="pl-k">@description</span> Define the HTML of the item to be put in "div.se-file-item-column".</span>
<span class="pl-c"> * <span class="pl-k">@param</span> item Item of the response data's array</span>
<span class="pl-c"> */</span>
<span class="pl-c1">drawItems</span>: <span class="pl-kos">(</span><span class="pl-s1">item</span>: <span class="pl-smi">object</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi">string</span><span class="pl-kos">;</span>

<span class="pl-c">/**</span>
<span class="pl-c"> * <span class="pl-k">@description</span> Close a file browser window</span>
<span class="pl-c"> * The plugin's "init" method is called.</span>
<span class="pl-c"> * <span class="pl-k">@example</span> this.plugins.fileBrowser.close.call(this);</span>
<span class="pl-c"> */</span>
<span class="pl-c1">close</span><span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">;</span>

<span class="pl-c">/**</span>
<span class="pl-c"> * <span class="pl-k">@description</span> This method is called when the file browser window is closed.</span>
<span class="pl-c"> * Initialize the properties.</span>
<span class="pl-c"> */</span>
<span class="pl-c1">init</span>?: <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>


</article>
</div>
<!-- /readme-->
Expand All @@ -342,6 +386,7 @@ <h3>Module plugins</h3>
<li><a href="document-module.html#titleComponent">component</a></li>
<li><a href="document-module.html#titleFileManager">fileManager</a></li>
<li><a href="document-module.html#titleResizing">resizing</a></li>
<li><a href="document-module.html#titleFileBrowser">fileBrowser</a></li>
</ul>
</nav>

Expand Down
130 changes: 90 additions & 40 deletions src/lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,36 @@ export default function (context, pluginCallButtons, plugins, lang, options, _ic
*/
activePlugins: null,

/**
* @description Information of tags that should maintain HTML structure, style, class name, etc. (In use by "math" plugin)
* When inserting "html" such as paste, it is executed on the "html" to be inserted. (core.cleanHTML)
* Basic Editor Actions:
* 1. All classes not starting with "__se__" or "se-" in the editor are removed.
* 2. The style of all tags except the "span" tag is removed from the editor.
* "managedTagsInfo" structure ex:
* managedTagsInfo: {
* query: '.__se__xxx, se-xxx'
* map: {
* '__se__xxx': method.bind(core),
* 'se-xxx': method.bind(core),
* }
* }
* @example
* Define in the following return format in the "managedTagInfo" function of the plugin.
* managedTagInfo() => {
* return {
* className: 'string', // Class name to identify the tag. ("__se__xxx", "se-xxx")
* // Change the html of the "element". ("element" is the element found with "className".)
* // "method" is executed by binding "core".
* method: function (element) {
* // this === core
* element.innerHTML = // (rendered html);
* }
* }
* }
*/
managedTagsInfo: null,

/**
* @description Array of "checkFileInfo" functions with the core bound
* (Plugins with "checkFileInfo" and "resetFileInfo" methods)
Expand Down Expand Up @@ -4132,7 +4162,7 @@ export default function (context, pluginCallButtons, plugins, lang, options, _ic
}
// comments
if (node.nodeType === 8 && this._allowHTMLComments) {
return '<__comment__>' + node.textContent.trim() + '</__comment__>';
return '<!--' + node.textContent.trim() + '-->';
}

return '';
Expand All @@ -4146,35 +4176,10 @@ export default function (context, pluginCallButtons, plugins, lang, options, _ic
* @returns {String}
*/
cleanHTML: function (html, whitelist) {
const dom = _d.createRange().createContextualFragment(html);
try {
util._consistencyCheckOfHTML(dom, this._htmlCheckWhitelistRegExp);
} catch (error) {
console.warn('[SUNEDITOR.cleanHTML.consistencyCheck.fail] ' + error);
}

const domTree = dom.childNodes;
let cleanHTML = '';
let requireFormat = false;

for (let i = 0, len = domTree.length, t; i < len; i++) {
t = domTree[i];
if (t.nodeType === 1 && !util.isTextStyleElement(t) && !util.isBreak(t) && !util._notAllowedTags(t)) {
requireFormat = true;
break;
}
}

for (let i = 0, len = domTree.length; i < len; i++) {
cleanHTML += this._makeLine(domTree[i], requireFormat);
}

cleanHTML = cleanHTML
html = html
.replace(/\n/g, '')
.replace(/<(script|style).*>(\n|.)*<\/(script|style)>/g, '')
.replace(this.editorTagsWhitelistRegExp, '')
.replace(/<__comment__>/g, '<!-- ')
.replace(/<\/__comment__>/g, ' -->')
.replace(/(<[a-zA-Z0-9]+)[^>]*(?=>)/g, function (m, t) {
let v = null;
const tAttr = this._attributesTagsWhitelist[t.match(/(?!<)[a-zA-Z]+/)[0].toLowerCase()];
Expand All @@ -4199,8 +4204,44 @@ export default function (context, pluginCallButtons, plugins, lang, options, _ic
return t;
}.bind(this));

cleanHTML = util.htmlRemoveWhiteSpace(cleanHTML);
const dom = _d.createRange().createContextualFragment(html);
try {
util._consistencyCheckOfHTML(dom, this._htmlCheckWhitelistRegExp);
} catch (error) {
console.warn('[SUNEDITOR.cleanHTML.consistencyCheck.fail] ' + error);
}

if (this.managedTagsInfo) {
const textCompList = dom.querySelectorAll(this.managedTagsInfo.query);
for (let i = 0, len = textCompList.length, initMethod, classList; i < len; i++) {
classList = [].slice.call(textCompList[i].classList);
for (let c in classList) {
initMethod = this.managedTagsInfo.map[classList[c]];
if (initMethod) {
initMethod(textCompList[i]);
break;
}
}
}
}

const domTree = dom.childNodes;
let cleanHTML = '';
let requireFormat = false;

for (let i = 0, len = domTree.length, t; i < len; i++) {
t = domTree[i];
if (t.nodeType === 1 && !util.isTextStyleElement(t) && !util.isBreak(t) && !util._notAllowedTags(t)) {
requireFormat = true;
break;
}
}

for (let i = 0, len = domTree.length; i < len; i++) {
cleanHTML += this._makeLine(domTree[i], requireFormat);
}

cleanHTML = util.htmlRemoveWhiteSpace(cleanHTML);
return util._tagConvertor(!cleanHTML ? html : !whitelist ? cleanHTML : cleanHTML.replace(typeof whitelist === 'string' ? util.createTagsWhitelist(whitelist) : whitelist, ''));
},

Expand All @@ -4210,29 +4251,28 @@ export default function (context, pluginCallButtons, plugins, lang, options, _ic
* @returns {String}
*/
convertContentsForEditor: function (contents) {
contents = contents
.replace(/\n/g, '')
.replace(/<(script|style).*>(\n|.)*<\/(script|style)>/g, '')
.replace(this.editorTagsWhitelistRegExp, '');

const dom = _d.createRange().createContextualFragment(contents);
try {
util._consistencyCheckOfHTML(dom, this._htmlCheckWhitelistRegExp);
} catch (error) {
console.warn('[SUNEDITOR.convertContentsForEditor.consistencyCheck.fail] ' + error);
}

let returnHTML = '';
const domTree = dom.childNodes;
let cleanHTML = '';
for (let i = 0, len = domTree.length; i < len; i++) {
returnHTML += this._makeLine(domTree[i], true);
cleanHTML += this._makeLine(domTree[i], true);
}

if (returnHTML.length === 0) return '<p><br></p>';

returnHTML = util.htmlRemoveWhiteSpace(returnHTML);
returnHTML = returnHTML
.replace(this.editorTagsWhitelistRegExp, '')
.replace(/\n/g, '')
.replace(/<__comment__>/g, '<!-- ')
.replace(/<\/__comment__>/g, ' -->');
if (cleanHTML.length === 0) return '<p><br></p>';

return util._tagConvertor(returnHTML);
cleanHTML = util.htmlRemoveWhiteSpace(cleanHTML);
return util._tagConvertor(cleanHTML);
},

/**
Expand Down Expand Up @@ -4444,7 +4484,7 @@ export default function (context, pluginCallButtons, plugins, lang, options, _ic
const defaultAttr = 'contenteditable|colspan|rowspan|target|href|src|class|type|controls|data-format|data-size|data-file-size|data-file-name|data-origin|data-align|data-image-link|data-rotate|data-proportion|data-percentage|origin-size|data-exp|data-font-size';
this._allowHTMLComments = options._editorTagsWhitelist.indexOf('//') > -1;
this._htmlCheckWhitelistRegExp = new _w.RegExp('^(' + options._editorTagsWhitelist.replace('|//', '') + ')$', 'i');
this.editorTagsWhitelistRegExp = util.createTagsWhitelist(options._editorTagsWhitelist.replace('|//', '|__comment__'));
this.editorTagsWhitelistRegExp = util.createTagsWhitelist(options._editorTagsWhitelist.replace('|//', '|<!--|-->'));
this.pasteTagsWhitelistRegExp = util.createTagsWhitelist(options.pasteTagsWhitelist);

const _attr = options.attributesWhitelist;
Expand Down Expand Up @@ -4473,6 +4513,10 @@ export default function (context, pluginCallButtons, plugins, lang, options, _ic
this._fileInfoPluginsCheck = [];
this._fileInfoPluginsReset = [];

// text components
this.managedTagsInfo = { query: '', map: {} };
const managedClass = [];

// Command and file plugins registration
this.activePlugins = [];
this._fileManager.tags = [];
Expand All @@ -4499,8 +4543,14 @@ export default function (context, pluginCallButtons, plugins, lang, options, _ic
this._fileManager.pluginMap[plugin.fileTags[tag].toLowerCase()] = key;
}
}
if (plugin.managedTags) {
const info = plugin.managedTags();
managedClass.push('.' + info.className);
this.managedTagsInfo.map[info.className] = info.method.bind(this);
}
}

this.managedTagsInfo.query = managedClass.toString();
this._fileManager.queryString = this._fileManager.tags.join(',');
this._fileManager.regExp = new _w.RegExp('^(' + this._fileManager.tags.join('|') + ')$', 'i');
this._fileManager.pluginRegExp = new _w.RegExp('^(' + (filePluginRegExp.length === 0 ? 'undefined' : filePluginRegExp.join('|')) + ')$', 'i');
Expand Down
Loading

0 comments on commit 2eac388

Please sign in to comment.