<p style="text-align:justify;"></p><ul style="margin-top:0cm;" type="disc"><li style="text-align:justify;tab-stops:list 36.0pt;"><span lang="EN-GB" style="font-family:'Arial','sans-serif';"><ul sizcache07677761204452295="38 16 10" sizset="false"><li sizcache07677761204452295="38 16 10" sizset="false" value="0">We want to save just the structural formatting (H1-6/UL/OL/LI/STRONG/EM/A/IMG) with no styling (having removed the font/size/colour tools)
Thanks
10 Answers, 1 is accepted
The Sizzle attributes should not be included at any time in the DOM. Please list the steps required to get them in the editor content.
Regarding the full format cleaning, you can post-process the editor content, removing the style attributes (whenever you get the editor value). The most robust way to do so is through HTML parsing (rather than regex matching), for example $(editor.value()).find("[style]").removeAttr("style").html();
Regards,Alex Gyoshev
Telerik
I have now got my Clean Format command working which does a depth first parse of the DOM cleaning each node as it comes back up the tree:
var cleanCommand = Command.extend({ exec: function () { Editor.cleanDOM(this.editor.body); }});EditorUtils.registerTool("clean", new Editor.Tool({ command: cleanCommand, template: new Editor.ToolTemplate({ template: EditorUtils.buttonTemplate, title: 'Clean Formatting' }) }));Editor.cleanDOM = function (node) { var next = null, depth = 0, toClean = false; if (node.childNodes.length > 0) do { if (!toClean && (next = node.firstChild)) { depth++; } else { toClean = false; if (!(next = node.nextSibling)) { next = node.parentNode; depth--; toClean = true; }; cleanNode(node); } node = next; } while (depth > 0);}paste: function (e) { var Editor = kendo.ui.editor; placeholder = Editor.Dom.create(document, 'div', { innerHTML: e.html }); Editor.cleanDOM(placeholder); e.html = placeholder.innerHTML;}Editor.DOMCleaner = Cleaner.extend({ applicable: function (html) { return true; }, clean: function (html) { placeholder = Editor.Dom.create(document, 'div', { innerHTML: html }); cleanDOM(placeholder); return placeholder.innerHTML; }});Thanks
See the following jsBin for an example on how to register the cleaner. The key takeaway is the line
editor.clipboard.cleaners.push(new DOMCleaner());
This functionality is not documented, as we have not many requests for custom cleaners; if such need arises, we will introduce a public API to do this. I don't expect much breaking changes for this code, but please keep in mind that it is not official.
All the best,Alex Gyoshev
Telerik
I am testing with the latest internal build (2013.2.912) and am wondering if there has been any further development (additions) on this with regard to the built in cleaner. I am attempting to accomplish the same thing in this post (clean style attributes from html tags).
Is there a better way to do this now or is the provided example still the best way to accomplish this?
Thank you for any info you can provide.
Adam
There has been no further development with this regard. You can use the code shown in the jsbin demo.
Regards,Atanas Korchev
Telerik
I'm looking for something quite what you are describing here. A paste-handler that strips anything but the structural formatting and if possible a custom button that does the same task. How did your final solution end up? Would it be possible for you to share the actual cleaning code (the cleanNode function)?
Best regards,
/Jan
Here is what I am currently using - please share any updates/improvements or comments back here :-)
(function ($, undefined) { /* CLEAN FORMATTING */ if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (elt /*, from*/) { $.inArray(elt, this); }; } var Editor = kendo.ui.editor, Command = Editor.Command, Cleaner = Editor.Cleaner, EditorUtils = Editor.EditorUtils; var cleanCommand = Command.extend({ exec: function () { Editor.cleanDOM(this.editor.body); } }); EditorUtils.registerTool("clean", new Editor.Tool({ command: cleanCommand, template: new Editor.ToolTemplate({ template: EditorUtils.buttonTemplate, title: 'Clean Formatting' }) })); Editor.cleanDOM = function (node, allow) { var next = null, depth = 0, toClean = false; if (node && node.childNodes.length > 0) do { if (!toClean && (next = node.firstChild)) { depth++; } else { toClean = false; if (!(next = node.nextSibling)) { next = node.parentNode; depth--; toClean = true; }; cleanNode(node, allow || allowWhiteList); } node = next; } while (depth > 0); } // White List Of Allowed Tags + Attributes var allowWhiteList = { tags: 'h1,h2,h3,h4,h5,h6,blockquote,ol,ul,li,p,br,b,strong,em,i,u,span,del,a,img,table,thead,tbody,tfoot,tr,th,td'.split(','), attr: { 'img': ['src', 'alt', 'height', 'width', 'align'], 'a': ['href', 'target', 'name'], 'table': ['class', 'contentEditable'], 'th': ['contentEditable', 'colspan', 'rowspan'], 'td': ['contentEditable', 'colspan', 'rowspan'], 'span': ['class'] }, style: { 'span': ['text-decoration'] }, empty: ['img', 'th', 'td', 'br'] }; function cleanNode(node, allow) { var parent = node.parentNode; if (node.nodeType == 1) { // Remove If Not In White List var tag = node.nodeName.toLowerCase(); if (allow.tags.indexOf(tag) === -1) { while (node.childNodes.length > 0) { parent.insertBefore(node.childNodes[0], node); } parent.removeChild(node); return; }; // Remove Empty Tags if (node.childNodes.length == 0 && allow.empty.indexOf(tag) == -1) { parent.removeChild(node); return; } // Clean Attributes var attrs = allow.attr[tag] || []; for (var x = node.attributes.length - 1; x >= 0; x--) { var attr = node.attributes[x].name.toLowerCase(); if (attr == 'style') { // Clean Style var allowed = allow.style[tag] || []; if (allowed.length == 0) node.removeAttribute('style'); else { var cssText = ''; for (var i = 0; i < allowed.length; i++) { var defined = node.style[allowed[i]]; if (defined) cssText += allowed[i] + ':' + defined + ';'; } if (cssText) node.style.cssText = cssText; else node.removeAttribute('style'); }; } else if (attrs.indexOf(attr) === -1) { node.removeAttribute(node.attributes[x].name); } }; // Remove Span With No Attributes (Keeping Children) if (tag == 'span' && node.attributes.length == 0) { while (node.childNodes.length > 0) { parent.insertBefore(node.childNodes[0], node); } parent.removeChild(node); return; }; // Combine Adjacent Text Sub-Nodes node.normalize(); // Remove Adjacent Breaks if (tag == 'p') { // Remove Trailing Breaks And Split On Double Breaks var lastChild = true; var lineBreaks = 0; for (var i = node.childNodes.length - 1; i >= 0; i--) { var child = node.childNodes[i]; if (child.nodeName == 'BR') { if (lastChild) node.removeChild(child); else { lineBreaks++; } } else { lastChild = false; if (lineBreaks > 1) { var newNode = parent.insertBefore(document.createElement('P'), node.nextSibling); var skip = true; while (child.nextSibling) if (skip && child.nextSibling.nodeName == 'BR') node.removeChild(child.nextSibling) else { newNode.insertBefore(child.nextSibling); skip = false; } lastChild = true; } lineBreaks = 0; } } // Remove Breaks At Start Of Paragraph while (node.childNodes.length > 0 && node.childNodes[0].nodeName == 'BR') node.removeChild(node.firstChild); // If Only Breaks Remove Paragraph var onlyBreaks = true; for (var i = 0; i < node.childNodes.length; i++) if (node.childNodes[i].nodeName != 'BR') onlyBreaks = false; if (onlyBreaks) parent.removeChild(node); } } else if (node.nodeType != 3) { // Not An Element Or Text Node parent.removeChild(node); } else { node.nodeValue = node.nodeValue.replace(/\xA0/g, ' ').replace(/\n/g, " ").replace(/\s{2,}/g, ' '); // Remove Whitespace Only Text Nodes if (!/\S/.test(node.nodeValue)) parent.removeChild(node); }; };})(window.kendo.jQuery);This is a text with a blue word.
- and "blue" is marked blue, then effectively the space between blue and word will be removed as well when pasting.
Not a big issue, I just removed the last line in the clean method. But thanks a million - this is a great help.
Another issue is that I cant seem to add a text-only button the way you create the clean button. I end up with a button with a "random" portion of the icon sprite as background - not a regular text-based button. But then again, the users doesn't need that, so I've skipped it :o) None the less - I would like to know how its done, should you know it.
Best regards
Jan
.k-clean { background-image: url('/img/editor/clean.png') !important; background-size: 24px 24px;}/Jan