Discuz!在6.0版的时候推出了自己的编辑器,除了它常用的编辑功能之外有一个很有特色的功能就是附件无上传插入. 用这个这个功能人都会发现在编辑时只要在本地选择了文件附件无需上传即可将它插入到帖子内容中,图文并排在第一次发贴的时候就可以实现,而不像一些传统程序必须上传了附件才可以插入到编辑器中. 作为开发者的我对这个功能实现的方式非常感兴趣,花了一点时间来研读了一下Discuz!编辑器此功能的代码. 本文分析的Discuz!版本为6.0,涉及的javascript和php文件包括 include/javascript/post_attach.js include/javascript/post_editor.js include/javascript/bbcode.js include/newthread.inc.php 代码版权归康盛创想公司所有
首先我们来分析 include/javascript/post_attach.js 中的 插入附件操作 实现的过程是大体这样的: 1.获得File表单域的值,也就是本地文件路径 2.检查文件和浏览器类型,如果是IE切附件是图片格式才开始 3.用Dom+IE AlphaImageLoader滤镜把图片加载到一个隐藏的图片中. 4.算得图片的高宽后再根据系统设定的缩略图计算图片在编辑器中的大小. 5.将得到值写入全局变量中存储,并生成HTML代码插入到编辑器中 计算图片宽高和生成插入到编辑器可视化内容的HTML操作函数 insertAttach() /*
Discuz!编辑器附件添加原理不完全研究
作者:朦朧中的罪惡
博客:http://be-evil.org
*/
function insertAttach(id) {
var localimgpreview = '';
//根据附件序号获得对应file对象的值(本地文件地址)
var path = $('attach_' + id).value;
//获取文件后缀
var ext = path.lastIndexOf('.') == -1 ? '' : path.substr(path.lastIndexOf('.') + 1, path.length).toLowerCase();
//正则表达式验证文件后缀
var re = new RegExp("(^|\\s|,)" + ext + "($|\\s|,)", "ig");
//获得文件名
var localfile = $('attach_' + id).value.substr($('attach_' + id).value.replace(/\\/g, '/').lastIndexOf('/') + 1);
//如果路径为空则跳出
if(path == '') {
return;
}
//判断系统允许附件设置是否加载
if(extensions != '' && (re.exec(extensions) == null || ext == '')) {
alert(lang['post_attachment_ext_notallowed']);
return;
}
//判断当前浏览器是否为IE且文件是图片
attachexts[id] = is_ie && in_array(ext, ['gif', 'jpeg', 'jpg', 'png', 'bmp']) ? 2 : 1;
//浏览器是IE且文件是图片
if(attachexts[id] == 2) {
/*
* img_hidden 是一个隐藏的 img 位于 id 为 posteditor_bottom 的div 中
*/
//设定图片 alt 属性为附件序号
$('img_hidden').alt = id;
//设定img的滤镜 AlphaImageLoader 的 sizingMethod 属性为 image (增大或减小对象的尺寸边界以适应图片的尺寸)
$('img_hidden').filters.item("DXImageTransform.Microsoft.AlphaImageLoader").sizingMethod = 'image';
try {
//尝试将本地图片用滤镜加载到 img_hidden 中
$('img_hidden').filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = $('attach_' + id).value;
} catch (e) {
//如果失败则提示附件错误,并删除当前的文件上传表单
alert(lang['post_attachment_img_invalid']);
delAttach(id);
return;
}
//获得img_hidden本身的高和宽
var wh = {'w' : $('img_hidden').offsetWidth, 'h' : $('img_hidden').offsetHeight};
//获得图片序号
var aid = $('img_hidden').alt;
//如果图片高宽超过了系统设定的缩略图高宽则获得系统设定的缩略图高宽
if(wh['w'] >= thumbwidth || wh['h'] >= thumbheight) {
wh = attachthumbImg(wh['w'], wh['h']);
}
//附件高宽数组索引赋值
attachwh[id] = wh;
//调整img_hidden的高宽
$('img_hidden').style.width = wh['w']
$('img_hidden').style.height = wh['h'];
//设定img的滤镜 AlphaImageLoader 的 sizingMethod 属性为 scale (缩放图片以适应对象的尺寸边界)
$('img_hidden').filters.item("DXImageTransform.Microsoft.AlphaImageLoader").sizingMethod = 'scale';
//生成一个查看缩略图的 Div
div = document.createElement('div');
//根据附件编号设定其id
div.id = 'localimgpreview_' + id + '_menu';
//设定该层为不可见
div.style.display = 'none';
//设定层的样式
div.style.marginLeft = '20px';
div.className = 'popupmenu_popup';
//插入该层到body中
document.body.appendChild(div);
//将图片插入到层中
div.innerHTML = '<img style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=\'scale\',src=\'' +$('attach_' + id).value+'\');width:'+wh['w']+';height:'+wh['h']+'" src=\'images/common/none.gif\' border="0" aid="attach_'+ aid +'" alt="" />';
}
//生成附件后的 [删除] [插入] [图片序号] [图片名称] 按钮
$('localfile_' + id).innerHTML = '<a href="###delAttach"_"> + id + ')">[' + lang['post_attachment_deletelink'] + ']</a> <a href="###insertAttach" title="' + lang['post_attachment_insert'] + '"_"> + id + ');return false;">[' +lang['post_attachment_insertlink'] + ']</a> ' +
(attachexts[id] == 2 ? '<span id="localimgpreview_' + id + '"> <span class="smalltxt">['+id + ']</span> <a href="###attachment"_"> + id + ');return false;">' + localfile + '</a></span>' : '<span class="smalltxt">[' + id + ']</span> ' + localfile);
//隐藏当前的file对象
$('attach_' + id).style.display = 'none';
//生成一个新的file对象
addAttach();
} 插入到编辑器的操作函数 insertAttachtext() //插入编辑器操作 @id 附件序号
function insertAttachtext(id) {
//根据传入的id判断全局数组索引中是否有该对象
if(!attachexts[id]) {
return;
}
//检查该对象是否在IE下产生
if(attachexts[id] == 2) {
/*
*
* 检查编辑器是discuz代码插入模式还是所见即所得模式
* 所见即所得模式则往编辑器中插入隐藏层中的img HTML代码
* 代码插入模式则插入 "[localimg=高度,宽度]附件序号[/localimg]" 代码
*
*/
bbinsert && wysiwyg ? insertText($('localimgpreview_' + id + '_menu').innerHTML, false) : AddText('[localimg=' + attachwh[id]['w'] +',' + attachwh[id]['h'] + ']' + id + '[/localimg]');
} else {
//在其他浏览器下直接插入 "[local]附件序号[/local]" 代码
bbinsert && wysiwyg ? insertText('[local]' + id + '[/local]', false) : AddText('[local]' + id + '[/local]');
}
} 读到这里我产生了一个疑问:
在所见即所得模式下插入的是HTML代码,并未插入Discuz!代码,同样,在Discuz!代码模式下插入的是Discuz!代码而没有插入HTML代码.但是我点击切换模式后对应的代码却会出现在编辑器中,这是如何实现的?
经过分析,原来编辑器在切换模式的时候会对内容代码使用正则表达式进行替换操作
在include/javascript/bbcode.js 第96行 是将 Discuz!代码转换为HTML代码的操作
str = str.replace(/\[localimg=(\d{1,4}),(\d{1,4})\](\d+)\[\/localimg\]/ig, function ($1, $2, $3, $4) {if($('attach_' + $4)) {var src = $('attach_' +$4).value; if(src != '') return '<img style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=\'scale\',src=\'' + src+ '\');width:' + $2 + ';height=' + $3 + '" src=\'images/common/none.gif\' border="0" aid="attach_' + $4 + '" alt="" />';}});
将HTML代码转换为Discuz!代码在277-305行 function imgtag(attributes) {
var width = '';
var height = '';
re = /src=(["']?)([\s\S]*?)(\1)/i;
var matches = re.exec(attributes);
if(matches != null) {
var src = matches[2];
} else {
return '';
}
re = /width\s?:\s?(\d{1,4})(px)?/ig;
var matches = re.exec(attributes);
if(matches != null) {
width = matches[1];
}
re = /height\s?:\s?(\d{1,4})(px)?/ig;
var matches = re.exec(attributes);
if(matches != null) {
height = matches[1];
}
if(!width || !height) {
re = /width=(["']?)(\d+)(\1)/i;
var matches = re.exec(attributes);
if(matches != null) {
width = matches[2];
}
re = /height=(["']?)(\d+)(\1)/i;
var matches = re.exec(attributes);
if(matches != null) {
height = matches[2];
}
}
re = /aid=(["']?)attach_(\d+)(\1)/i;
var matches = re.exec(attributes);
var imgtag = 'img';
if(matches != null) {
imgtag = 'localimg';
src = matches[2];
}
re = /aid=(["']?)attachimg_(\d+)(\1)/i;
var matches = re.exec(attributes);
if(matches != null) {
return '[attachimg]' + matches[2] + '[/attachimg]';
}
return width > 0 && height > 0 ?
'[' + imgtag + '=' + width + ',' + height + ']' + src + '[/' + imgtag + ']' :
' ';
}
虽然这个功能只有IE Only。但是的确能够提高不少的用户体验,同时我非常佩服Discuz!团队的解决问题的思想。
|