秀米集成到UEditor的配置过程
虽然秀米官方有提供文档说明如何将秀米插件集成至UEditor里(秀米官方文档),但是该文档说明模糊不清,关键配置一句话带过,即使配置成功后还有坑留在里面。所以我重新整理了这个配置过程,回顾一下过程的同时也给以后需要用到的小伙伴提供便利。
一、将秀米图标集成至工具栏,并且成功弹出秀米弹框
首先我们需要一个秀米的html文件,这个html里主要就是弹框里的内容,里面有一个iframe指向秀米的网址。
取名xiumi-ue-dialog-v5.html,放入ueditor1_4_3_3文件夹(UE的资源文件夹)下或者ueditor1_4_3_3下的dialogs文件夹里,这个只是涉及到后面引入它的路径,不是很重要,我是放在ueditor1_4_3_3文件夹下的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45<!-- ueditor1\_4\_3\_3/xiumi-ue-dialog-v5.html -->
<!-- saved from url=(0049)http://hgs.xiumi.us/uedit/xiumi-ue-dialog-v5.html -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>XIUMI connect</title>
<style>
html, body {
padding: 0;
margin: 0;
}
#xiumi {
position: absolute;
width: 100%;
height: 100%;
border: none;
box-sizing: border-box;
}
</style>
</head>
<body style="">
<iframe id="xiumi" src="//xiumi.us/studio/v5#/paper">
</iframe>
<script type="text/javascript" src="./dialogs/internal.js"></script>
<script>
var xiumi = document.getElementById('xiumi');
var xiumi_url = window.location.protocol + "//xiumi.us";
xiumi.onload = function () {
xiumi.contentWindow.postMessage('ready', xiumi_url);
};
document.addEventListener("mousewheel", function (event) {
event.preventDefault();
event.stopPropagation();
});
window.addEventListener('message', function (event) {
if (event.origin == xiumi_url) {
editor.execCommand('insertHtml', event.data);
// 这个方法用于触发UE的图片转存接口
editor.fireEvent('afterpaste');
dialog.close();
}
}, false);
</script>
</body></html>秀米的文档里使用的是UE.registerUI(‘dialog’,fn)的方式动态在UE里添加一个秀米按钮并且在按钮里添加弹出弹框事件,但是这么做可能会影响到系统其他使用UE里的地方同样出现秀米图标,无法按需加载。所以我将此改进成在UE的前端配置中的toolbars数组里添加一个”xiumi”字符串即可展示出秀米的方式。步骤如下:
添加一个秀米的图标并添加样式。在ueditor1_4_3_3/themes/default/images文件夹里放入一个秀米图标;
在ueditor1_4_3_3/themes/default/css/ueditor.css 添加俩句样式
1
2
3
4
5
6
7
8
9
10
11/* xiumi-dialog */
.edui-default .edui-for-xiumi .edui-dialog-content {
width: calc(100vw - 60px) ;
height: 90vh ;
overflow: hidden;
}
.edui-default .edui-for-xiumi .edui-icon {
background-image: url("../images/xiumi-connect-icon.png") ;
background-size: contain;
}在ueditor.all.js修改代码。在iframeUrlMap对象里添加键值对’xiumi’: ‘~/xiumi-ue-dialog-v5.html’,这个和刚刚那个html存放的地方有关。在btnCmds数组里添加一个’xiumi’字符串,在dialogBtns对象中的ok数组里添加’xiumi’字符串。
最后在UE的前端配置里toolbars数组里添加’xiumi’,即可成功展示出秀米图标与秀米的弹框。
效果如下
在ueditor.config.js的xss过滤白名单whitList配置里,修改section参数。同时将设置是否抓取远程图片catchRemoteImageEnable设置为true。
1
section:['class', 'style'],
能成功弹出秀米的弹框,并且能将秀米里的模版成功勾选到本地的UE编辑器里,说明集成就成功了一半啦。
二、秀米域名的图片转存。
要完成秀米图片的转存,首先我们要先完成UE对复制过来的网络图片的转存,图片的转存分为img标签与背景图片的抓取与转存。
设置抓取白名单catcherLocalDomain,在白名单里的地址,UE不会对其发起转存请求。可以使用两种方式设置:
- 使用UE.utils.extend方法强制在UE实例化后设置
1
2
3UE.utils.extend(editor.options, {
catcherLocalDomain: ['127.0.0.1', 'localhost', "static-alpha-engage.gridsumdissector.com", "static-beta-engage.gridsumdissector.com", "static-uat-engage.gridsumdissector.com", "static.engage-all.com"],
});
- 修改ueditor.all.js里的初始配置代码,在这个js文件大概第8100行的地方,有一个loadServerConfig的函数,可以直接在这里将catcherLocalDomain的初始值设置为我们想要过滤的白名单域名。
- 使用UE.utils.extend方法强制在UE实例化后设置
修改抓取图片的后端请求地址。UE发起所有接口请求都依赖于一个名为serverUrl的前端配置,然后通过使用serverUrl这个唯一的请求地址,通过GET参数action指定不同请求类型,比如uploadimage(执行上传图片或截图的action名称), uploadvideo(执行上传视频的action名称), catchimage(执行抓取远程图片的action名称)等等,但是后端为了方便接口的管理,一般会将接口拆分出来,当然UE也支持自定义请求地址。UE推荐的方法如下:
1
2
3
4
5
6
7
8
9
10UE.Editor.prototype._bkGetActionUrl = UE.Editor.prototype.getActionUrl;
UE.Editor.prototype.getActionUrl = function(action) {
if (action == 'uploadimage' || action == 'uploadscrawl' || action == 'uploadimage') {
return 'http://a.b.com/upload.php';
} else if (action == 'uploadvideo') {
return 'http://a.b.com/video.php';
} else {
return this._bkGetActionUrl.call(this, action);
}
}
原理就是使用一个名为_bkGetActionUrl的临时变量承载原来的getActionUrl方法,然后重写getActionUrl方法,判断action的类型,来返回不同的接口,同时也还可以执行原来的getActionUrl方法。
但是我在使用这种方法的时候,会偶尔碰到内存溢出,导致上传图片功能不可用的原因,可能是getActionUrl在其他地方也被重新赋值了,产生循环引用该函数的问题。所以我依旧在ueditor.all.js里修改了getActionUrl原始函数,大概在第8040行代码里,判断getActionUrl函数内部一个actionName的参数,来返回你想要的接口名。
到这里差不多能够抓取到img标签的src并且可以将其替换了,但需要注意俩点地方:
注意抓取后,后端返回的数据格式时候和UE里源码设置的一样,在ueditor.all.js找到UE.plugins[‘catchremoteimage’]这个函数,在其内部执行catchremoteimage的success回掉函数的地方注意获取源路径和新路径的数据路径的地方。
UE内部有一个判断是否跨域的方法,如果跨域会使用jsonp的方式请求接口,如果你是本地调试,并且后端已经对跨域crose处理,不需要使用jsonp的方式请求,你可以把它关了。同样在UE.plugins[‘catchremoteimage’]这个函数里,找到定义catchremoteimage函数的地方,将其内部ajax请求的option里,将dataType固定设置为空字符串即可。
除了img标签的src替换,背景图片也是需要进行图片抓取和替换的。其实就是依葫芦画瓢,看明白了UE是如何对img标签进行src替换的,也就明白该如何对背景图片进行替换了。
首先你需要在ueditor.all.js文件的domUtils参数里新添一个方法getElementsByTagNameStyle。通过元素的style来获取元素节点数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* 方法getElementsByTagNameStyle的封装
* @method getElementsByTagNameStyle
* @param { e } node 目标节点对象
* @param { t } tagName 需要查找的节点的tagName, 多个tagName以空格分割
* @param { i } style 节点对象的筛选条件
* @return { Array } 符合条件的节点集合
*/
getElementsByTagNameStyle: function (e, t, i) {
if (i && utils.isString(i)) {
var n = i;
i = function (e) {
for (var t, i = n.split(","), o = !0, r = e.getAttribute("style"), a = 0; t = i[a++];) if (!r || r.indexOf(t) < 0) {
o = !1;
break
}
return o
}
}
t = utils.trim(t).replace(/[ ]{2,}/g, " ").split(" ");
for (var o, r = [], a = 0; o = t[a++];) for (var s, l = e.getElementsByTagName(o), d = 0; s = l[d++];) i && !i(s) || r.push(s);
return r
},在UE.plugins[‘catchremoteimage’]下的catchRemoteImage监听函数里添加代码。首先你得的到有背景图片的的元素节点数组。使用刚刚新添的方法
1
backgroundimagestags = domUtils.getElementsByTagNameStyle(me.document, "section div p", "background,url")//抓取背景图片所在的标签
然后将该元素节点数组里的图片地址都抽取出来,放在一个存放图片地址的数组里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var backgroundimages = [];
for (var i = 0, backci; backci = backgroundimagestags[i++];) {
var bstyle = backci.style;
var backgroundimgurltag = bstyle['background-image'] || bstyle['background'] || "";
if (backgroundimgurltag != null && backgroundimgurltag != "") {
var backsrc = backgroundimgurltag.split("(")[1].split(")")[0].replace(/\"/g, "")
|| backgroundimgurltag.split("(")[1].split(")")[0].replace(/\"/g, "")
|| "";
if (backsrc != null && backsrc != "") {
if (/^(https?|ftp):/i.test(backsrc) && !test(backsrc, catcherLocalDomain)) {
backgroundimages.push(encodeURI(backsrc));
}
}
}
}最后依葫芦画瓢,对该图片地址数组循环,依次发起转存请求,并且将对应的节点内的背景图片url进行替换。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42if(backgroundimages.length) {
catchremoteimage(backgroundimages, {
//成功抓取
success: function (r) {
try {
var info = r.state !== undefined ? r:eval("(" + r.responseText + ")");
} catch (e) {
return;
}
/* 获取源路径和新路径 */
var i, j, ci, cj, oldSrc, newSrc, styleText ,list = info.data.list;
for (i = 0; ci = backgroundimagestags[i++];) {
styleText = ci.getAttribute("style");
oldSrc = styleText;
if (oldSrc.indexOf('url("') > 0) {
oldSrc = oldSrc.split('url("')[1].split('")')[0];
} else if (oldSrc.indexOf("url('") > 0) {
oldSrc = oldSrc.split("url('")[1].split("')")[0];
} else {
if (!(oldSrc.indexOf("url(") > 0)) continue;
oldSrc = oldSrc.split("url(")[1].split(")")[0];
}
if (oldSrc.indexOf("?") >= 0) {
oldSrc = oldSrc.split("?")[0];
}
for (j = 0; cj = list[j++];) {
if (oldSrc == cj.source && "SUCCESS" == cj.state) {
newSrc = catcherUrlPrefix + cj.url, styleText = styleText.replace(oldSrc, newSrc), domUtils.setAttributes(ci, { style: styleText }), domUtils.setAttributes(ci, { is_updata: "true" });
break
}
}
}
me.fireEvent('catchremotesuccess')
},
//回调失败,本次请求超时
error: function () {
me.fireEvent("catchremoteerror");
}
});
}
到这里,UE对普通的img标签及背景图片的抓取与转存已经成功了,如果想要对秀米过来的图片进行转存需要注意以下俩点:
对某些秀米图片地址的后缀进行处理,去掉?号之后类似?x-oss-process=所有的部分。这个在catchremoteimage函数里对imgs进行一步map循环,将?后面的东西去掉即可,同时注意在图片地址数组循环的过程中oldSrc?后面的内容也要去掉,否则oldSrc == cj.source && “SUCCESS” == cj.state这一步判断不会通过,就无法进行图片地址的替换了。
从秀米回到本地UE编辑器的时候不会触发图片转存请求,这也是秀米文档埋下的一个坑!解决这个问题,你需要在xiumi-ue-dialog-v5.html文件里,在
dialog.close();
代码前添加一句editor.fireEvent('afterpaste');
来主动触发afterpaste这个监听函数。
大功告成