在线html过滤,在线Html编辑器粘贴过滤技术详解(二)

作者:Tony Qu

本章我们将来说一说filterPasteData函数的实现。

上篇中提到我们采用的粘贴方式是浏览器自己提供的,只是使用了不少技巧,使得粘贴的东西不直接进入Html编辑器的iframe,所以从某种意义上讲,我们获得的粘贴数据内容都是html。根据我们之前所说的三种需求,文本、html以及Word内容,我们可以把过滤的内容分为两大类:

a. html

b. Word xml

之所以纯文本可以当做html就是因为贴进来就是当html处理了,只不过是不带标签的html。

说到过滤,自然要做匹配和替换,于是我们很自然的想到了正则表达式,先来处理比较简单的html过滤。我定义了以下一些规则:

* 移除html, body, form, doctype, head, script,style, textarea, button,select, option, input,span标签

* 移除id, name, class, language,type属性

* 移除on开头的属性,如onclick

* 移除a, table, tr,td,tbody, thead, th, img,input,iframe, div标签(我们的系统中不支持table)

* 移除换行符(\n, \r)

以上这些标签都是要移除的对象,因为它们并没有给系统带来任何好处,还会降低安全性。当然根据实际需要,你可以适当保留一些标签,比如select, button, input, img, table等 function FilterPasteText(str)

{

str = str.replace(/\r\n|\n|\r/ig, "");

//remove html body form

str = str.replace(/])[^>]*>/ig, "");

//remove doctype

str = str.replace(//ig, "");

//remove xml tags

str = str.replace(/]))[^>]*>/gi,"");

//remove head

str = str.replace(/

]*>(\n|.)*?/ig, "");

//remove

str = str.replace(//ig, "");

//remove empty span

str = str.replace(/]*?>/ig, "");

//remove ...

str = str.replace(/]*>(\n|.)*?/ig, "");

//remove table and tag, tag, tag (this can help filter unclosed tag)

str = str.replace(/]*>/ig, "");

//remove bad attributes

do {

len = str.length;

str = str.replace(/(]*\s)(?:id|name|language|type|class|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi, "$1");

} while (len != str.length);

return str;

}

这里str.replace(/]*>([\s\S]+)/ig, “$2”)的意思是保留里面的内容,但是移除标签本身。

这里str.replace(//ig, "");的意思是移除input标签。

以下是一些测试数据

替换前

替换后

abc

...

...

abc

abc

abccde

好了,接下来就来讲word xml的过滤。要想过滤word xml就必须知道当前贴进来的是word的数据,而非普通html,tinymce使用了一个技巧,如下:

function IsWordDocument(strValue)

{

var re=new RegExp(/(class=\"?Mso|style=\"[^\"]*\bmso\-|w:WordDocument)/ig);

return re.test(strValue);

}

只要是从Word贴过来的都会有这样的标记:class=”MsoNormal”或或style=”mso-font-charset:0”

一旦检测到这样的标签,就可以认为这是从Word粘贴过来的,当然如果有人特别无聊,故意做了这样的标签或样式贴进来也没关系,因为过滤器会把没有用的东西过滤掉,那些不在规则里面的不管你是Word数据还是html数据,都尽量保留。

Word的过滤会比普通的html复杂很多,主要原因还是浏览器自动贴进来的word xml保留了很多东西,通常一段十分简单的文本数据,通过word xml表示可能字符长度要达到上万。

举个例子:这里有一段很简单的Word文本,如下

4cc24fffcb34e10a733789d18f48f91c.png

它对应的粘贴结果却是下面这么一大段,是不是很恐怖?

Abcde

l  你好

l  Aaaaaaaaaaaaaa

l  Bbbbbbbbbbb

l  Ccccccccccccc

如果你原来只允许用户输入1000个字符,你在数据库里面只给了nvarchar(1000),这就意味着这东西保存进去必然会被截断,而且可能连正文部分都没保存进去,这肯定是不允许的。这也解释了为什么主流html编辑器提供了专门的Word粘贴功能。

上面那段东西其实真正有用的东西也就是下面这么点:

Abcde

l  你好

l  Aaaaaaaaaaaaaa

l  Bbbbbbbbbbb

l  Ccccccccccccc

所以这就是我们需要的过滤结果,接下来就看你JavaScript正则表达式有多牛了!

我主要设置了以下这些规则:

1. 去除\r\n, \n, \r

2. 去除文本开头的 和文本结尾的 

3. 去除Word的条件注释(如)

4. 去除script, xml:*, img, meta,link, style, *:*标签

5. 将Word的抬头转换成标签

6. 去除lang属性

7. 去除或替换所有没有意义的样式标签,如mso-padding-alt, mso-margin-top-alt,horiz-align, mso-default-width, mso-zero-height等

对应代码如下:

function FilterPasteWord(str)

{

//remove link break

str = str.replace(/\r\n|\n|\r/ig, "");

//remove   entities at the start of contents

str = str.replace(/^\s*( )+/ig,"");

//remove   entities at the end of contents

str = str.replace(/( |
]*>)+\s*$/ig,"");

// Word comments like conditional comments etc

str = str.replace(//ig, "");

// Remove comments, scripts (e.g., msoShowComment), XML tag, VML content, MS Office namespaced tags, and a few other tags

str = str.replace(/]*>.*?\s])|\/?(\?xml(:\w+)?|xml|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,"");

//convert word headers to strong

str = str.replace(/

]*class="?MsoHeading"?[^>]*>(.*?)/gi, "

$1");

//remove lang attribute

str = str.replace(/(lang)\s*=\s*([\'\"]?)[\w-]+\2/ig, "");

// Examine all styles: delete junk, transform some, and keep the rest

str = str.replace(/(]*)\sstyle="([^"]*)"/gi,function(str, tag, style) {

var n = [],

i = 0,

s = style.trim().replace(/"/gi, "'").split(";");

// Examine each style definition within the tag's style attribute

for(var i=0;i

{

v=s[i];

var name, value,

parts = v.split(":");

if (parts.length == 2) {

name = parts[0].toLowerCase();

value = parts[1].toLowerCase();

// Translate certain MS Office styles into their CSS equivalents

switch (name) {

case "mso-padding-alt":

case "mso-padding-top-alt":

case "mso-padding-right-alt":

case "mso-padding-bottom-alt":

case "mso-padding-left-alt":

case "mso-margin-alt":

case "mso-margin-top-alt":

case "mso-margin-right-alt":

case "mso-margin-bottom-alt":

case "mso-margin-left-alt":

case "mso-table-layout-alt":

case "mso-height":

case "mso-width":

case "mso-vertical-align-alt":

n[i++] = name.replace(/^mso-|-alt$/g, "") + ":" + ensureUnits(value);

continue;

case "horiz-align":

n[i++] = "text-align:" + value;

continue;

case "vert-align":

n[i++] = "vertical-align:" + value;

continue;

case "font-color":

case "mso-foreground":

n[i++] = "color:" + value;

continue;

case "mso-background":

case "mso-highlight":

n[i++] = "background:" + value;

continue;

case "mso-default-height":

n[i++] = "min-height:" + ensureUnits(value);

continue;

case "mso-default-width":

n[i++] = "min-; + ensureUnits(value);

continue;

case "mso-padding-between-alt":

n[i++] = "border-collapse:separate;border-spacing:" + ensureUnits(value);

continue;

case "text-line-through":

if ((value == "single") || (value == "double")) {

n[i++] = "text-decoration:line-through";

}

continue;

case "mso-zero-height":

if (value == "yes") {

n[i++] = "display:none";

}

continue;

}

// Eliminate all MS Office style definitions that have no CSS equivalent by examining the first characters in the name

if (/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?:align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(name)) {

continue;

}

// If it reached this point, it must be a valid CSS style

n[i++] = name + ":" + parts[1]; // Lower-case name, but keep value case

}

}

// If style attribute contained any valid styles the re-write it; otherwise delete style attribute.

if (i > 0) {

return tag + ' style="' + n.join(';') + '"';

} else {

return tag;

}

});

return str;

}

到此,你应该对这项技术有了很详细的了解了。当然具体过滤哪些东西,你还是要多实践多测试,比如前两天客户一不小心把youtube视频贴近系统里面了,于是我又过滤了object, embed, param标签。当然有了这样一个模型之后,规则的维护变得很方便,无形中降低了代码的维护成本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值