<
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