merge plugins v1.9巫师3mod合并工具具到底怎么使用

actived.gif
handle_hover.gif
handle_hover_a.gif
handle_hover_span.gif
head_bg.gif
li_icon.gif
login_bg.jpg
login_box.gif
login_btnbg.jpg
login_computer.jpg
nav_li.gif
tree_folder3.gif
tree_folder4.gif
tree_line1.gif
tree_line2.gif
tree_line3.gif
tree_line4.gif
yeen_head.gif
yeen_left_menu_bg.gif
yeen_left_menu_bg.png
yeen_nav_bg.jpg
yeen_style.css
dialog.htm
yeen_admin.htm
yeen_ads.htm
yeen_ajax_author.htm
yeen_ajax_copyfrom.htm
yeen_ajax_upload.htm
yeen_alone.htm
yeen_alone_edit.htm
yeen_article.htm
yeen_articleflag.htm
yeen_article_edit.htm
yeen_attachs.htm
yeen_author.htm
yeen_backnav.htm
yeen_backup.htm
yeen_block.htm
yeen_comment.htm
yeen_config.htm
yeen_config_type.htm
yeen_copyfrom.htm
yeen_flag.htm
yeen_footer.htm
yeen_form.htm
yeen_friendlink.htm
yeen_guestbook.htm
yeen_header.htm
yeen_impower.htm
yeen_left.htm
yeen_log.htm
yeen_main.htm
yeen_member.htm
yeen_member_show.htm
yeen_model.htm
yeen_nav.htm
yeen_node.htm
yeen_power.htm
yeen_recycle.htm
yeen_slide.htm
yeen_special.htm
yeen_tags.htm
yeen_template.htm
yeen_top.htm
yeen_upload.htm
yeen_upload_ads.htm
yeen_user_field.htm
yeen_user_group.htm
yeen_user_level.htm
yeen_user_modify.htm
yeen_user_power.htm
yeen_user_setscore.htm
yeen_user_show.htm
yeen_user_type.htm
yeen_workflow.htm
button.png
closelabel.gif
confirm_bg.gif
error_bg.gif
forumbottombg.gif
icon_cityname.jpg
ignore.png
loading.gif
need_passwd.jpg
only_friend.jpg
only_master.jpg
regright.png
regright_big.png
regstep.png
regwrong.png
success_bg.gif
template.jpg
warning_bg.gif
box_bottom.gif
box_top.gif
failed.gif
install.css
footer.html
header.html
step-1.html
step-2.html
step-3.html
step-4.html
yeencms.sql
yeencmsdata.sql
icon_userlink.jpg
icon_user_base.jpg
icon_user_fa.jpg
icon_user_face.jpg
icon_user_follow.jpg
icon_user_model.jpg
icon_user_my.jpg
icon_user_pwd.jpg
icon_user_zl.jpg
loginbg.jpg
loginbutton.jpg
logintitle.jpg
logo_foot.jpg
menu_hover_bg.jpg
regbutton.jpg
regtitle.jpg
searchbutton.jpg
useruploadbutton.jpg
content_add.htm
content_list.htm
content_side.htm
myguestbook.htm
myinfo.htm
mylove.htm
myreply.htm
office_area.htm
photo_add.htm
photo_list.htm
reg_complete.htm
seticon.htm
setinfo.htm
setpwds.htm
showinfo.htm
user_collect.htm
user_complete.htm
user_edit.htm
user_fans.htm
user_follow.htm
user_footer.htm
user_getpwds.htm
user_header.htm
user_home.htm
user_left.htm
user_login.htm
user_manage.htm
user_menu.htm
user_reg.htm
user_side.htm
usrbar.htm
closelabel.gif
head_bg.jpg
icon_comment.jpg
icon_tel.jpg
icon_userlink.jpg
lightbox.css
nextlabel.gif
prevlabel.gif
shopmanage.jpg
tab_bg_6cd047bc.png
space_footer.htm
space_header.htm
space_home.htm
space_info.htm
space_menu.htm
space_photo.htm
space_side.htm
index_0.jpg
index_1.jpg
tg_flash_p.png
tg_flash_p2.png
index_2.jpg
index_3.jpg
pixviewer.swf
ads_002.jpg
default.css
forumbottombg.gif
forumboxbg.gif
icon_title.jpg
icon_userlink.jpg
indexline.jpg
logo_foot.jpg
mainboxbg.jpg
mainboxbg_bottom.jpg
menu_hover_bg.jpg
noticbg.jpg
scroll_001.jpg
scroll_002.jpg
scroll_003.jpg
scroll_004.jpg
tg_flash_p.png
tg_flash_p2.png
article_index.htm
article_left.htm
article_list.htm
article_show.htm
comment.htm
footer.htm
form_list.htm
form_post.htm
form_show.htm
header.htm
loading.htm
photo_index.htm
photo_left.htm
photo_list.htm
photo_show.htm
position.htm
reg_complete.htm
scroll.htm
tags_list.htm
template_index.htm
template_left.htm
template_list.htm
template_show.htm
tongji.htm
upload.htm
upload_face.htm
upload_photo.htm
upload_user.htm
usrbar.htm
yeen_upload.htm
yuicompressor-2.4.6.jar
anchor.html
attachment
fileTypeImages
icon_chm.gif
icon_default.png
icon_doc.gif
icon_exe.gif
icon_mp3.gif
icon_mv.gif
icon_pdf.gif
icon_ppt.gif
icon_psd.gif
icon_rar.gif
icon_txt.gif
icon_xls.gif
attachment.css
attachment.html
background
background.css
background.html
jxface2.gif
neweditor-tab-bg.png
emotion.css
emotion.html
highlightcode
highlightcode.html
center_focus.jpg
left_focus.jpg
none_focus.jpg
right_focus.jpg
image.html
imageUploader.swf
insertframe
insertframe.html
music.html
addimg.png
delimg.png
delimgH.png
emptyH.png
eraser.png
scaleH.png
scrawl.css
scrawl.html
searchreplace
searchreplace.html
snapscreen
snapscreen.html
spechars.html
dragicon.png
edittable.css
edittable.html
edittd.html
edittip.html
template.css
template.html
center_focus.jpg
left_focus.jpg
none_focus.jpg
right_focus.jpg
video.html
webapp.html
fClipboard_ueditor.swf
imageUploader.swf
wordimage.html
imglabel.png
localimage.png
upload.png
ueditor.css
anchor.gif
button-bg.gif
cancelbutton.gif
cursor_h.gif
cursor_h.png
cursor_v.gif
cursor_v.png
dialog-title-bg.png
filescan.png
highlighted.gif
icons-all.gif
neweditor-tab-bg.png
pagebreak.gif
spacer.gif
sparator_v.png
toolbar_bg.png
unhighlighted.gif
upload.png
videologo.gif
wordpaste.png
dialogbase.css
iframe.css
third-party
codemirror
codemirror.css
snapscreen
UEditorSnapscreen.exe
swfupload.swf
swfupload_fp9.swf
SyntaxHighlighter
shCoreDefault.css
CHANGELOG.TXT
version .txt
robots.txt
(function(){UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};
var baidu = window.baidu || {};
window.baidu =
window.UE = baidu.editor =
UE.plugins = {};
UE.commands = {};
UE.instants = {};
UE.I18N = {};
UE.version = &1.2.5.0&;
var dom = UE.dom = {};
* @name UE.browser
* @short Browser
* @desc UEditor中采用的浏览器判断模块
var browser = UE.browser = function(){
var agent = navigator.userAgent.toLowerCase(),
opera = window.opera,
browser = {
* 检测浏览器是否为IE
* @name ie
* @grammar UE.browser.ie
=& true|false
: !!window.ActiveXObject,
* 检测浏览器是否为Opera
* @name opera
* @grammar UE.browser.opera
=& true|false
opera : ( !!opera && opera.version ),
* 检测浏览器是否为webkit内核
* @name webkit
* @grammar UE.browser.webkit
=& true|false
webkit : ( agent.indexOf( ' applewebkit/' ) & -1 ),
* 检测浏览器是否为mac系统下的浏览器
* @name mac
* @grammar UE.browser.mac
=& true|false
mac : ( agent.indexOf( 'macintosh' ) & -1 ),
* 检测浏览器是否处于怪异模式
* @name quirks
* @grammar UE.browser.quirks
=& true|false
quirks : ( patMode == 'BackCompat' )
* 检测浏览器是否处为gecko内核
* @name gecko
* @grammar UE.browser.gecko
=& true|false
browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera );
var version = 0;
// Internet Explorer 6.0+
if ( browser.ie ){
version = parseFloat( agent.match( /msie (\d+)/ )[1] );
* 检测浏览器是否为 IE9 模式
* @name ie9Compat
* @grammar UE.browser.ie9Compat
=& true|false
browser.ie9Compat = document.documentMode == 9;
* 检测浏览器是否为 IE8 浏览器
* @name ie8
* @grammar
UE.browser.ie8
=& true|false
browser.ie8 = !!document.documentM
* 检测浏览器是否为 IE8 模式
* @name ie8Compat
* @grammar
UE.browser.ie8Compat
=& true|false
browser.ie8Compat = document.documentMode == 8;
* 检测浏览器是否运行在 兼容IE7模式
* @name ie7Compat
* @grammar
UE.browser.ie7Compat
=& true|false
browser.ie7Compat = ( ( version == 7 && !document.documentMode )
|| document.documentMode == 7 );
* 检测浏览器是否IE6模式或怪异模式
* @name ie6Compat
* @grammar
UE.browser.ie6Compat
=& true|false
browser.ie6Compat = ( version & 7 || browser.quirks );
if ( browser.gecko ){
var geckoRelease = agent.match( /rv:([\d\.]+)/ );
if ( geckoRelease )
geckoRelease = geckoRelease[1].split( '.' );
version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1;
* 检测浏览器是否为chrome
* @name chrome
* @grammar
UE.browser.chrome
=& true|false
if (/chrome\/(\d+\.\d)/i.test(agent)) {
browser.chrome = + RegExp['\x241'];
* 检测浏览器是否为safari
* @name safari
* @grammar
UE.browser.safari
=& true|false
if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)){
browser.safari = + (RegExp['\x241'] || RegExp['\x242']);
// Opera 9.50+
if ( browser.opera )
version = parseFloat( opera.version() );
// WebKit 522+ (Safari 3+)
if ( browser.webkit )
version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] );
* 浏览器版本判断
* IE系列返回值为5,6,7,8,9,10等
* gecko系列会返回1等.
* webkit系列会返回其build号 (如 522等).
* @name version
* @grammar
UE.browser.version
* @example
* if ( UE.browser.ie && UE.browser.version == 6 ){
alert( &Ouch!居然是万恶的IE6!& );
browser.version =
* 是否是兼容模式的浏览器
* @name isCompatible
* @grammar
UE.browser.isCompatible
=& true|false
* @example
* if ( UE.browser.isCompatible ){
alert( &你的浏览器相当不错哦!& );
browser.isCompatible =
!browser.mobile && (
( browser.ie && version &= 6 ) ||
( browser.gecko && version &= 10801 ) ||
( browser.opera && version &= 9.5 ) ||
( browser.air && version &= 1 ) ||
( browser.webkit && version &= 522 ) ||
//快捷方式
var ie = browser.ie,
webkit = browser.webkit,
gecko = browser.gecko,
opera = browser.
* @name UE.Utils
* @short Utils
* @desc UEditor封装使用的静态工具函数
* @import editor.js
var utils = UE.utils = {
* 遍历数组,对象,nodeList
* @name each
* @grammar UE.utils.each(obj,iterator,[context])
* @since 1.2.4+
* * obj 要遍历的对象
* * iterator 遍历的方法,方法的第一个是遍历的值,第二个是索引,第三个是obj
* * context
iterator的上下文
* @example
* UE.utils.each([1,2],function(v,i){
console.log(v)//值
console.log(i)//索引
* UE.utils.each(document.getElementsByTagName('*'),function(n){
console.log(n.tagName)
each : function(obj, iterator, context) {
if (obj == null)
if (obj.length === +obj.length) {
for (var i = 0, l = obj. i & i++) {
if(iterator.call(context, obj[i], i, obj) === false)
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if(iterator.call(context, obj[key], key, obj) === false)
makeInstance:function (obj) {
var noop = new Function();
noop.prototype =
noop.prototype =
* 将source对象中的属性扩展到target对象上
* @name extend
* @grammar UE.utils.extend(target,source)
//覆盖扩展
* @grammar UE.utils.extend(target,source,true)
==& Object
//保留扩展
extend:function (t, s, b) {
for (var k in s) {
if (!b || !t.hasOwnProperty(k)) {
t[k] = s[k];
* 模拟继承机制,subClass继承superClass
* @name inherits
* @grammar UE.utils.inherits(subClass,superClass) =& subClass
* @example
* function SuperClass(){
this.name = &小李&;
* SuperClass.prototype = {
hello:function(str){
console.log(this.name + str);
* function SubClass(){
this.name = &小张&;
* UE.utils.inherits(SubClass,SuperClass);
* var sub = new SubClass();
* sub.hello(&早上好!&); ==& &小张早上好!&
inherits:function (subClass, superClass) {
var oldP = subClass.prototype,
newP = utils.makeInstance(superClass.prototype);
utils.extend(newP, oldP, true);
subClass.prototype = newP;
return (newP.constructor = subClass);
* 用指定的context作为fn上下文,也就是this
* @name bind
* @grammar UE.utils.bind(fn,context)
bind:function (fn, context) {
return function () {
return fn.apply(context, arguments);
* 创建延迟delay执行的函数fn
* @name defer
* @grammar UE.utils.defer(fn,delay)
//延迟delay毫秒执行fn,返回fn
* @grammar UE.utils.defer(fn,delay,exclusion)
//延迟delay毫秒执行fn,若exclusion为真,则互斥执行fn
* @example
* function test(){
console.log(&延迟输出!&);
* //非互斥延迟执行
* var testDefer = UE.utils.defer(test,1000);
* testDefer();
&延迟输出!&;
* testDefer();
&延迟输出!&;
* //互斥延迟执行
* var testDefer1 = UE.utils.defer(test,1000,true);
* testDefer1();
//本次不执行
* testDefer1();
&延迟输出!&;
defer:function (fn, delay, exclusion) {
var timerID;
return function () {
if (exclusion) {
clearTimeout(timerID);
timerID = setTimeout(fn, delay);
* 查找元素item在数组array中的索引, 若找不到返回-1
* @name indexOf
* @grammar UE.utils.indexOf(array,item)
=& index|-1
//默认从数组开头部开始搜索
* @grammar UE.utils.indexOf(array,item,start)
=& index|-1
//start指定开始查找的位置
indexOf:function (array, item, start) {
var index = -1;
start = this.isNumber(start) ? start : 0;
this.each(array,function(v,i){
if(i &= start && v === item){
* 移除数组array中的元素item
* @name removeItem
* @grammar UE.utils.removeItem(array,item)
removeItem:function (array, item) {
for (var i = 0, l = array. i & i++) {
if (array[i] === item) {
array.splice(i, 1);
* 删除字符串str的首尾空格
* @name trim
* @grammar UE.utils.trim(str) =& String
trim:function (str) {
return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
* 将字符串list(以','分隔)或者数组list转成哈希对象
* @name listToMap
* @grammar UE.utils.listToMap(list)
//Object形如{test:1,br:1,textarea:1}
listToMap:function (list) {
if (!list)return {};
list = utils.isArray(list) ? list : list.split(',');
for (var i = 0, ci, obj = {}; ci = list[i++];) {
obj[ci.toUpperCase()] = obj[ci] = 1;
* 将str中的html符号转义,默认将转义''&&&&''四个字符,可自定义reg来确定需要转义的字符
* @name unhtml
* @grammar UE.utils.unhtml(str);
* @grammar UE.utils.unhtml(str,reg)
* @example
* var html = '&body&You say:&你好!Baidu & UEditor!&&/body&';
* UE.utils.unhtml(html);
&body&You say:&你好!Baidu & UEditor!&&/body&
* UE.utils.unhtml(html,/[&&]/g)
&body&You say:&你好!Baidu & UEditor!&&/body&
unhtml:function (str, reg) {
return str ? str.replace(reg || /[&&&&]/g, function (m) {
'&':'&',
'&':'&',
'&':'&',
'&':'&'
}) : '';
* 将str中的转义字符还原成html字符
* @name html
* @grammar UE.utils.html(str)
//详细参见&code&&a href = '#unhtml'&unhtml&/a&&/code&
html:function (str) {
return str ? str.replace(/&((g|l|quo)t|amp);/g, function (m) {
'&':'&',
'&':'&',
'&':'&',
'&':'&'
}) : '';
* 将css样式转换为驼峰的形式。如font-size =& fontSize
* @name cssStyleToDomStyle
* @grammar UE.utils.cssStyleToDomStyle(cssName)
cssStyleToDomStyle:function () {
var test = document.createElement('div').style,
'float':test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
return function (cssName) {
return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
return match.charAt(1).toUpperCase();
* 动态加载文件到doc中,并依据obj来设置属性,加载成功后执行回调函数fn
* @name loadFile
* @grammar UE.utils.loadFile(doc,obj)
* @grammar UE.utils.loadFile(doc,obj,fn)
* @example
* //指定加载到当前document中一个script文件,加载成功后执行function
* utils.loadFile( document, {
src:&test.js&,
tag:&script&,
type:&text/javascript&,
defer:&defer&
* }, function () {
console.log('加载成功!')
loadFile:function () {
var tmpList = [];
function getItem(doc,obj){
for(var i= 0,ci=tmpList[i++];){
if(ci.doc === doc && ci.url == (obj.src || obj.href)){
}catch(e){
return function (doc, obj, fn) {
var item = getItem(doc,obj);
if (item) {
if(item.ready){
fn && fn();
item.funs.push(fn)
tmpList.push({
url:obj.src||obj.href,
if (!doc.body) {
var html = [];
for(var p in obj){
if(p == 'tag')
html.push(p + '=&' + obj[p] + '&')
doc.write('&' + obj.tag + ' ' + html.join(' ') + ' &&/'+obj.tag+'&');
if (obj.id && doc.getElementById(obj.id)) {
var element = doc.createElement(obj.tag);
delete obj.
for (var p in obj) {
element.setAttribute(p, obj[p]);
element.onload = element.onreadystatechange = function () {
if (!this.readyState || /loaded|complete/.test(this.readyState)) {
item = getItem(doc,obj);
if (item.funs.length & 0) {
item.ready = 1;
for ( fi = item.funs.pop();) {
element.onload = element.onreadystatechange =
element.onerror = function(){
throw Error('The load '+(obj.href||obj.src)+' fails,check the url settings of file editor_config.js ')
doc.getElementsByTagName(&head&)[0].appendChild(element);
* 判断obj对象是否为空
* @name isEmptyObject
* @grammar UE.utils.isEmptyObject(obj)
=& true|false
* @example
* UE.utils.isEmptyObject({}) ==&true
* UE.utils.isEmptyObject([]) ==&true
* UE.utils.isEmptyObject(&&) ==&true
isEmptyObject:function (obj) {
if (obj == null)
if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
for (var key in obj) if (obj.hasOwnProperty(key))
* 统一将颜色值使用16进制形式表示
* @name fixColor
* @grammar UE.utils.fixColor(name,value) =& value
* @example
* rgb(255,255,255)
=& &#ffffff&
fixColor:function (name, value) {
if (/color/i.test(name) && /rgba?/.test(value)) {
var array = value.split(&,&);
if (array.length & 3)
return &&;
value = &#&;
for (var i = 0, color = array[i++];) {
color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
value += color.length == 1 ? &0& + color :
value = value.toUpperCase();
* 只针对border,padding,margin做了处理,因为性能问题
* @function
* @param {String}
val style字符串
optCss:function (val) {
var padding, margin,
val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function (str, key, name, val) {
if (val.split(' ').length == 1) {
switch (key) {
case 'padding':
!padding && (padding = {});
padding[name] =
return '';
case 'margin':
!margin && (margin = {});
margin[name] =
return '';
case 'border':
return val == 'initial' ? '' :
function opt(obj, name) {
if (!obj) {
return '';
var t = obj.top , b = obj.bottom, l = obj.left, r = obj.right, val = '';
if (!t || !l || !b || !r) {
for (var p in obj) {
val += ';' + name + '-' + p + ':' + obj[p] + ';';
val += ';' + name + ':' +
(t == b && b == l && l == r ? t :
t == b && l == r ? (t + ' ' + l) :
l == r ? (t + ' ' + l + ' ' + b) : (t + ' ' + r + ' ' + b + ' ' + l)) + ';'
val += opt(padding, 'padding') + opt(margin, 'margin');
return val.replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '').replace(/;([ \n\r\t]+)|\1;/g, ';')
.replace(/(&((l|g)t|quot|#39))?;{2,}/g, function (a, b) {
return b ? b + &;;& : ';'
* 深度克隆对象,从source到target
* @name clone
* @grammar UE.utils.clone(source) =& anthorObj 新的对象是完整的source的副本
* @grammar UE.utils.clone(source,target) =& target包含了source的所有内容,重名会覆盖
clone:function (source, target) {
target = target || {};
for (var i in source) {
if (source.hasOwnProperty(i)) {
tmp = source[i];
if (typeof tmp == 'object') {
target[i] = utils.isArray(tmp) ? [] : {};
utils.clone(source[i], target[i])
target[i] =
* 转换cm/pt到px
* @name transUnitToPx
* @grammar UE.utils.transUnitToPx('20pt') =& '27px'
* @grammar UE.utils.transUnitToPx('0pt') =& '0'
transUnitToPx : function(val){
if(!/(pt|cm)/.test(val)){
return val
val.replace(/([\d.]+)(\w+)/,function(str,v,u){
switch(unit){
case 'cm':
val = parseFloat(val) * 25;
case 'pt':
val = Math.round(parseFloat(val) * 96 / 72);
return val + (val?'px':'');
* DomReady方法,回调函数将在dom树ready完成后执行
* @name domReady
* @grammar UE.utils.domReady(fn)
//返回一个延迟执行的方法
domReady:function () {
var fnArr = [];
function doReady(doc) {
//确保onready只执行一次
doc.isReady =
for ( ci = fnArr.pop();ci()){}
return function (onready,win) {
win = win ||
var doc = win.
onready && fnArr.push(onready);
if (doc.readyState === &complete&) {
doReady(doc);
doc.isReady && doReady(doc);
if (browser.ie) {
(function () {
if (doc.isReady)
doc.documentElement.doScroll(&left&);
} catch (error) {
setTimeout(arguments.callee, 0);
doReady(doc);
win.attachEvent('onload', function(){
doReady(doc)
doc.addEventListener(&DOMContentLoaded&, function () {
doc.removeEventListener(&DOMContentLoaded&, arguments.callee, false);
doReady(doc);
}, false);
win.addEventListener('load', function(){doReady(doc)}, false);
* 动态添加css样式
* @name cssRule
* @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
* @grammar UE.utils.cssRule('body','body{background:#ccc}') =& null
//给body添加背景颜色
* @grammar UE.utils.cssRule('body') =&样式的字符串
//取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
* @grammar UE.utils.cssRule('body','') =&null //清空给定的key值的背景颜色
cssRule : browser.ie ? function(key,style,doc){
var indexList,
doc = doc ||
if(doc.indexList){
indexList = doc.indexL
indexList = doc.indexList =
var sheetS
if(!indexList[key]){
if(style === undefined){
return ''
sheetStyle = doc.createStyleSheet('',index = doc.styleSheets.length);
indexList[key] =
sheetStyle = doc.styleSheets[indexList[key]];
if(style === undefined){
return sheetStyle.cssText
sheetStyle.cssText = style || ''
}:function(key,style,doc){
doc = doc ||
var head = doc.getElementsByTagName('head')[0],
if(!(node = doc.getElementById(key))){
if(style === undefined){
return ''
node = doc.createElement('style');
head.appendChild(node)
if(style === undefined){
return node.innerHTML
if(style !== ''){
node.innerHTML =
head.removeChild(node)
* 判断str是否为字符串
* @name isString
* @grammar UE.utils.isString(str) =& true|false
* 判断array是否为数组
* @name isArray
* @grammar UE.utils.isArray(obj) =& true|false
* 判断obj对象是否为方法
* @name isFunction
* @grammar UE.utils.isFunction(obj)
=& true|false
* 判断obj对象是否为数字
* @name isNumber
* @grammar UE.utils.isNumber(obj)
=& true|false
utils.each(['String','Function','Array','Number','RegExp'],function(v){
UE.utils['is' + v] = function(obj){
return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
* @name UE.EventBase
* @short EventBase
* @import editor.js,core/utils.js
* @desc UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
* 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
var EventBase = UE.EventBase = function () {};
EventBase.prototype = {
* 注册事件监听器
* @name addListener
* @grammar editor.addListener(types,fn)
//types为事件名称,多个可用空格分隔
* @example
* editor.addListener('selectionchange',function(){
console.log(&选区已经变化!&);
* editor.addListener('beforegetcontent aftergetcontent',function(type){
if(type == 'beforegetcontent'){
//do something
//do something
console.log(this.getContent) // this是注册的事件的编辑器实例
addListener:function (types, listener) {
types = utils.trim(types).split(' ');
for (var i = 0, ti = types[i++];) {
getListener(this, ti, true).push(listener);
* 移除事件监听器
* @name removeListener
* @grammar editor.removeListener(types,fn)
//types为事件名称,多个可用空格分隔
* @example
* //changeCallback为方法体
* editor.removeListener(&selectionchange&,changeCallback);
removeListener:function (types, listener) {
types = utils.trim(types).split(' ');
for (var i = 0, ti = types[i++];) {
utils.removeItem(getListener(this, ti) || [], listener);
* 触发事件
* @name fireEvent
* @grammar editor.fireEvent(types)
//types为事件名称,多个可用空格分隔
* @example
* editor.fireEvent(&selectionchange&);
fireEvent:function (types) {
types = utils.trim(types).split(' ');
for (var i = 0, ti = types[i++];) {
var listeners = getListener(this, ti),
if (listeners) {
k = listeners.
while (k--) {
if(!listeners[k])
t = listeners[k].apply(this, arguments);
if(t === true){
if (t !== undefined) {
if (t = this['on' + ti.toLowerCase()]) {
r = t.apply(this, arguments);
* 获得对象所拥有监听类型的所有监听器
* @function
* @param {Object} obj
查询监听器的对象
* @param {String} type 事件类型
* @param {Boolean} force
为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
* @returns {Array} 监听器数组
function getListener(obj, type, force) {
type = type.toLowerCase();
return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) )
&& ( allListeners[type] || force && ( allListeners[type] = [] ) ) );
///import editor.js
///import core/dom/dom.js
* dtd html语义化的体现类
* @constructor
* @namespace dtd
var dtd = dom.dtd = (function() {
function _( s ) {
for (var k in s) {
s[k.toUpperCase()] = s[k];
function X( t ) {
for ( var i=1; i&a. i++ ) {
var x = a[i];
for ( var k in x ) {
if (!t.hasOwnProperty(k)) {
t[k] = x[k];
var A = _({isindex:1,fieldset:1}),
B = _({input:1,button:1,select:1,textarea:1,label:1}),
C = X( _({a:1}), B ),
D = X( {iframe:1}, C ),
E = _({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}),
F = _({ins:1,del:1,script:1,style:1}),
G = X( _({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}), F ),
H = X( _({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}), G ),
I = X( _({p:1}), H ),
J = X( _({iframe:1}), H, B ),
K = _({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}),
L = X( _({a:0}), J ),//a不能被切开,所以把他
M = _({tr:1}),
N = _({'#':1}),
O = X( _({param:1}), K ),
P = X( _({form:1}), A, D, E, I ),
Q = _({li:1}),
R = _({style:1,script:1}),
S = _({base:1,link:1,meta:1,title:1}),
T = X( S, R ),
U = _({head:1,body:1}),
V = _({html:1});
var block = _({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}),
//针对优酷的embed他添加了结束标识,导致粘贴进来会变成两个,暂时去掉 ,embed:1
_({area:1,base:1,br:1,col:1,hr:1,img:1,input:1,link:1,meta:1,param:1,embed:1});
// $ 表示自定的属性
// body外的元素列表.
$nonBodyContent: X( V, U, S ),
//块结构元素列表
$block : block,
//内联元素列表
$inline : L,
$body : X( _({script:1,style:1}), block ),
$cdata : _({script:1,style:1}),
//自闭和元素
$empty : empty,
//不是自闭合,但不能让range选中里边
$nonChild : _({iframe:1,textarea:1}),
//列表元素列表
$listItem : _({dd:1,dt:1,li:1}),
//列表根元素列表
$list: _({ul:1,ol:1,dl:1}),
//不能认为是空的元素
$isNotEmpty : _({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}),
//如果没有子节点就可以删除的元素列表,像span,a
$removeEmpty : _({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1}),
$removeEmptyBlock : _({'p':1,'div':1}),
//在table元素里的元素列表
$tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}),
//不转换的标签
$notTransContent : _({pre:1,script:1,style:1,textarea:1}),
script: N,
tr : _({td:1,th:1}),
embed: {},
colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}),
noscript : P,
center : P,
button : X( I, E ),
basefont : {},
option : N,
form : X( A, D, E, I ),
select : _({optgroup:1,option:1}),
label : L,
table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),
tfoot : M,
input : {},
iframe : P,
strong : L,
textarea : N,
noframes : P,
small : L,
span :_({'#':1,br:1}),
optgroup : _({option:1}),
param : {},
'var' : L,
object : O,
strike : L,
area : {},
map : X( _({area:1,form:1,p:1}), A, F, E ),
applet : O,
dl : _({dt:1,dd:1}),
isindex : {},
fieldset : X( _({legend:1}), K ),
thead : M,
acronym : L,
a : X( _({a:1}), J ),
blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),
caption : L,
tbody : M,
address : X( D, I ),
legend : L,
pre : X( G, C ),
p : X(_({'a':1}),L),
* @name UE.dom.domUtils
* @short DomUtils
* @import editor.js, core/utils.js,core/browser.js,core/dom/dtd.js
* @desc UEditor封装的底层dom操作库
function getDomNode(node, start, ltr, startFromChild, fn, guard) {
var tmpNode = startFromChild && node[start],
!tmpNode && (tmpNode = node[ltr]);
while (!tmpNode && (parent = (parent || node).parentNode)) {
if (parent.tagName == 'BODY' || guard && !guard(parent)) {
tmpNode = parent[ltr];
if (tmpNode && fn && !fn(tmpNode)) {
getDomNode(tmpNode, start, ltr, false, fn);
return tmpN
var attrFix = ie && browser.version & 9 ? {
tabindex:&tabIndex&,
readonly:&readOnly&,
&for&:&htmlFor&,
&class&:&className&,
maxlength:&maxLength&,
cellspacing:&cellSpacing&,
cellpadding:&cellPadding&,
rowspan:&rowSpan&,
colspan:&colSpan&,
usemap:&useMap&,
frameborder:&frameBorder&
tabindex:&tabIndex&,
readonly:&readOnly&
styleBlock = utils.listToMap([
'-webkit-box', '-moz-box', 'block' ,
'list-item' , 'table' , 'table-row-group' ,
'table-header-group', 'table-footer-group' ,
'table-row' , 'table-column-group' , 'table-column' ,
'table-cell' , 'table-caption'
var domUtils = dom.domUtils = {
//节点常量
NODE_ELEMENT:1,
NODE_DOCUMENT:9,
NODE_TEXT:3,
NODE_COMMENT:8,
NODE_DOCUMENT_FRAGMENT:11,
//位置关系
POSITION_IDENTICAL:0,
POSITION_DISCONNECTED:1,
POSITION_FOLLOWING:2,
POSITION_PRECEDING:4,
POSITION_IS_CONTAINED:8,
POSITION_CONTAINS:16,
//ie6使用其他的会有一段空白出现
fillChar:ie && browser.version == '6' ? '\ufeff' : '\u200B',
//-------------------------Node部分--------------------------------
/*Backspace*/ 8:1, /*Delete*/ 46:1,
/*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
37:1, 38:1, 39:1, 40:1,
13:1 /*enter*/
* 获取节点A相对于节点B的位置关系
* @name getPosition
* @grammar UE.dom.domUtils.getPosition(nodeA,nodeB)
* @example
switch (returnValue) {
case 0: //相等,同一节点
case 1: //无关,节点不相连
case 2: //跟随,即节点A头部位于节点B头部的后面
case 4: //前置,即节点A头部位于节点B头部的前面
case 8: //被包含,即节点A被节点B包含
case 10://组合类型,即节点A满足跟随节点B且被节点B包含。实际上,如果被包含,必定跟随,所以returnValue事实上不会存在8的情况。
case 16://包含,即节点A包含节点B
case 20://组合类型,即节点A满足前置节点A且包含节点B。同样,如果包含,必定前置,所以returnValue事实上也不会存在16的情况
getPosition:function (nodeA, nodeB) {
// 如果两个节点是同一个节点
if (nodeA === nodeB) {
// domUtils.POSITION_IDENTICAL
parentsA = [nodeA],
parentsB = [nodeB];
node = nodeA;
while (node = node.parentNode) {
// 如果nodeB是nodeA的祖先节点
if (node === nodeB) {
// domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
return 10;
parentsA.push(node);
node = nodeB;
while (node = node.parentNode) {
// 如果nodeA是nodeB的祖先节点
if (node === nodeA) {
// domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
return 20;
parentsB.push(node);
parentsA.reverse();
parentsB.reverse();
if (parentsA[0] !== parentsB[0]) {
// domUtils.POSITION_DISCONNECTED
var i = -1;
while (i++, parentsA[i] === parentsB[i]) {
nodeA = parentsA[i];
nodeB = parentsB[i];
while (nodeA = nodeA.nextSibling) {
if (nodeA === nodeB) {
// domUtils.POSITION_PRECEDING
// domUtils.POSITION_FOLLOWING
* 返回节点node在父节点中的索引位置
* @name getNodeIndex
* @grammar UE.dom.domUtils.getNodeIndex(node)
//索引值从0开始
getNodeIndex:function (node, ignoreTextNode) {
var preNode = node,
while (preNode = preNode.previousSibling) {
if (ignoreTextNode && preNode.nodeType == 3) {
if(preNode.nodeType != preNode.nextSibling.nodeType ){
* 检测节点node是否在节点doc的树上,实质上是检测是否被doc包含
* @name inDoc
* @grammar UE.dom.domUtils.inDoc(node,doc)
true|false
inDoc:function (node, doc) {
return domUtils.getPosition(node, doc) == 10;
* 查找node节点的祖先节点
* @name findParent
* @grammar UE.dom.domUtils.findParent(node)
=& Element
// 直接返回node节点的父节点
* @grammar UE.dom.domUtils.findParent(node,filterFn)
=& Element
//filterFn为过滤函数,node作为参数,返回true时才会将node作为符合要求的节点返回
* @grammar UE.dom.domUtils.findParent(node,filterFn,includeSelf)
=& Element
//includeSelf指定是否包含自身
findParent:function (node, filterFn, includeSelf) {
if (node && !domUtils.isBody(node)) {
node = includeSelf ? node : node.parentN
while (node) {
if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
return filterFn && !filterFn(node) && domUtils.isBody(node) ? null :
node = node.parentN
* 通过tagName查找node节点的祖先节点
* @name findParentByTagName
* @grammar UE.dom.domUtils.findParentByTagName(node,tagNames)
//tagNames支持数组,区分大小写
* @grammar UE.dom.domUtils.findParentByTagName(node,tagNames,includeSelf)
//includeSelf指定是否包含自身
* @grammar UE.dom.domUtils.findParentByTagName(node,tagNames,includeSelf,excludeFn)
//excludeFn指定例外过滤条件,返回true时忽略该节点
findParentByTagName:function (node, tagNames, includeSelf, excludeFn) {
tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
return domUtils.findParent(node, function (node) {
return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
}, includeSelf);
* 查找节点node的祖先节点集合
* @name findParents
* @grammar UE.dom.domUtils.findParents(node)
//返回一个祖先节点数组集合,不包含自身
* @grammar UE.dom.domUtils.findParents(node,includeSelf)
//返回一个祖先节点数组集合,includeSelf指定是否包含自身
* @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn)
//返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
* @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst)
//返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
findParents:function (node, includeSelf, filterFn, closerFirst) {
var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : [];
while (node = domUtils.findParent(node, filterFn)) {
parents.push(node);
return closerFirst ? parents : parents.reverse();
* 在节点node后面插入新节点newNode
* @name insertAfter
* @grammar UE.dom.domUtils.insertAfter(node,newNode)
=& newNode
insertAfter:function (node, newNode) {
return node.parentNode.insertBefore(newNode, node.nextSibling);
* 删除节点node,并根据keepChildren指定是否保留子节点
* @name remove
* @grammar UE.dom.domUtils.remove(node)
* @grammar UE.dom.domUtils.remove(node,keepChildren)
remove:function (node, keepChildren) {
var parent = node.parentNode,
if (parent) {
if (keepChildren && node.hasChildNodes()) {
while (child = node.firstChild) {
parent.insertBefore(child, node);
parent.removeChild(node);
* 取得node节点在dom树上的下一个节点,即多叉树遍历
getNextDomNode
* @grammar UE.dom.domUtils.getNextDomNode(node)
=& Element
* @example
getNextDomNode:function (node, startFromChild, filterFn, guard) {
return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
* 检测节点node是否属于bookmark节点
* @name isBookmarkNode
* @grammar UE.dom.domUtils.isBookmarkNode(node)
=& true|false
isBookmarkNode:function (node) {
return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
* 获取节点node所在的window对象
* @grammar UE.dom.domUtils.getWindow(node)
=& window对象
getWindow:function (node) {
var doc = node.ownerDocument ||
return doc.defaultView || doc.parentW
* 得到nodeA与nodeB公共的祖先节点
getCommonAncestor
* @grammar UE.dom.domUtils.getCommonAncestor(nodeA,nodeB)
=& Element
getCommonAncestor:function (nodeA, nodeB) {
if (nodeA === nodeB)
return nodeA;
var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1;
while (parent = parent.parentNode) {
if (parent === nodeB) {
parentsA.push(parent);
parent = nodeB;
while (parent = parent.parentNode) {
if (parent === nodeA)
parentsB.push(parent);
parentsA.reverse();
parentsB.reverse();
while (i++, parentsA[i] === parentsB[i]) {
return i == 0 ? null : parentsA[i - 1];
* 清除node节点左右兄弟为空的inline节点
* @name clearEmptySibling
* @grammar UE.dom.domUtils.clearEmptySibling(node)
* @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext)
//ignoreNext指定是否忽略右边空节点
* @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre)
//ignorePre指定是否忽略左边空节点
* @example
* &b&&/b&&i&&/i&xxxx&b&bb&/b& --& xxxx&b&bb&/b&
clearEmptySibling:function (node, ignoreNext, ignorePre) {
function clear(next, dir) {
while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
//这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
|| !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) {
tmpNode = next[dir];
domUtils.remove(next);
next = tmpN
!ignoreNext && clear(node.nextSibling, 'nextSibling');
!ignorePre && clear(node.previousSibling, 'previousSibling');
* 将一个文本节点node拆分成两个文本节点,offset指定拆分位置
* @name split
* @grammar UE.dom.domUtils.split(node,offset)
//返回从切分位置开始的后一个文本节点
split:function (node, offset) {
var doc = node.ownerD
if (browser.ie && offset == node.nodeValue.length) {
var next = doc.createTextNode('');
return domUtils.insertAfter(node, next);
var retval = node.splitText(offset);
//ie8下splitText不会跟新childNodes,我们手动触发他的更新
if (browser.ie8) {
var tmpNode = doc.createTextNode('');
domUtils.insertAfter(retval, tmpNode);
domUtils.remove(tmpNode);
* 检测节点node是否为空节点(包括空格、换行、占位符等字符)
isWhitespace
* @grammar
UE.dom.domUtils.isWhitespace(node)
=& true|false
isWhitespace:function (node) {
return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
* 获取元素element相对于viewport的位置坐标
* @name getXY
* @grammar UE.dom.domUtils.getXY(element)
=& Object //返回坐标对象{x:left,y:top}
getXY:function (element) {
var x = 0, y = 0;
while (element.offsetParent) {
y += element.offsetT
x += element.offsetL
element = element.offsetP
return { 'x':x, 'y':y};
* 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
* @name on
* @grammar UE.dom.domUtils.on(element,type,handler)
//type支持数组传入
* @example
* UE.dom.domUtils.on(document.body,&click&,function(e){
//e为事件对象,this为被点击元素对戏那个
* @example
* UE.dom.domUtils.on(document.body,[&click&,&mousedown&],function(evt){
//evt为事件对象,this为被点击元素对象
on:function (element, type, handler) {
var types = utils.isArray(type) ? type : [type],
k = types.
if (k) while (k--) {
type = types[k];
if (element.addEventListener) {
element.addEventListener(type, handler, false);
if (!handler._d) {
handler._d = {
var key = type + handler.toString(),index = utils.indexOf(handler._d.els,element);
if (!handler._d[key] || index == -1) {
if(index == -1){
handler._d.els.push(element);
if(!handler._d[key]){
handler._d[key] = function (evt) {
return handler.call(evt.srcElement, evt || window.event);
element.attachEvent('on' + type, handler._d[key]);
* 解除原生DOM事件绑定
* @name un
* @grammar
UE.dom.donUtils.un(element,type,handler)
//参见&code&&a href=&#on&&on&/a&&/code&
un:function (element, type, handler) {
var types = utils.isArray(type) ? type : [type],
k = types.
if (k) while (k--) {
type = types[k];
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
var key = type + handler.toString();
element.detachEvent('on' + type, handler._d ? handler._d[key] : handler);
}catch(e){}
if (handler._d && handler._d[key]) {
var index = utils.indexOf(handler._d.els,element);
if(index!=-1){
handler._d.els.splice(index,1);
handler._d.els.length == 0 && delete handler._d[key];
* 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
isSameElement
* @grammar UE.dom.domUtils.isSameElement(nodeA,nodeB) =& true|false
* @example
style=&font-size:12px&&ssss&/span& and &span style=&font-size:12px&&bbbbb&/span&
style=&font-size:13px&&ssss&/span& and &span style=&font-size:12px&&bbbbb&/span&
isSameElement:function (nodeA, nodeB) {
if (nodeA.tagName != nodeB.tagName) {
var thisAttrs = nodeA.attributes,
otherAttrs = nodeB.
if (!ie && thisAttrs.length != otherAttrs.length) {
var attrA, attrB, al = 0, bl = 0;
for (var i = 0; attrA = thisAttrs[i++];) {
if (attrA.nodeName == 'style') {
if (attrA.specified) {
if (domUtils.isSameStyle(nodeA, nodeB)) {
if (attrA.specified) {
attrB = otherAttrs.getNamedItem(attrA.nodeName);
attrB = nodeB.attributes[attrA.nodeName];
if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
// 有可能attrB的属性包含了attrA的属性之外还有自己的属性
for (i = 0; attrB = otherAttrs[i++];) {
if (attrB.specified) {
if (al != bl) {
* 判断节点nodeA与节点nodeB的元素属性是否一致
* @name isSameStyle
* @grammar UE.dom.domUtils.isSameStyle(nodeA,nodeB) =& true|false
isSameStyle:function (nodeA, nodeB) {
var styleA = nodeA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'),
styleB = nodeB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':');
if (browser.opera) {
styleA = nodeA.
styleB = nodeB.
if (styleA.length != styleB.length)
for (var p in styleA) {
if (/^(\d+|csstext)$/i.test(p)) {
if (styleA[p] != styleB[p]) {
if (!styleA || !styleB) {
return styleA == styleB;
styleA = styleA.split(';');
styleB = styleB.split(';');
if (styleA.length != styleB.length) {
for (var i = 0, ci = styleA[i++];) {
if (utils.indexOf(styleB, ci) == -1) {
* 检查节点node是否为块元素
* @name isBlockElm
* @grammar UE.dom.domUtils.isBlockElm(node)
=& true|false
isBlockElm:function (node) {
return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
* 检测node节点是否为body节点
* @name isBody
* @grammar UE.dom.domUtils.isBody(node)
=& true|false
isBody:function (node) {
node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
* 以node节点为中心,将该节点的指定祖先节点parent拆分成2块
breakParent
* @grammar UE.dom.domUtils.breakParent(node,parent) =& node
* &code type=&html&&&b&ooo&/b&是node节点
* &p&xxxx&b&ooo&/b&xxx&/p& ==& &p&xxx&/p&&b&ooo&/b&&p&xxx&/p&
* &p&xxxxx&span&xxxx&b&ooo&/b&xxxxxx&/span&&/p&
&p&xxxxx&span&xxxx&/span&&/p&&b&ooo&/b&&p&&span&xxxxxx&/span&&/p&&/code&
breakParent:function (node, parent) {
var tmpNode,
parentClone = node,
clone = node,
leftNodes,
parentClone = parentClone.parentN
if (leftNodes) {
tmpNode = parentClone.cloneNode(false);
tmpNode.appendChild(leftNodes);
leftNodes = tmpN
tmpNode = parentClone.cloneNode(false);
tmpNode.appendChild(rightNodes);
rightNodes = tmpN
leftNodes = parentClone.cloneNode(false);
rightNodes = leftNodes.cloneNode(false);
while (tmpNode = clone.previousSibling) {
leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
while (tmpNode = clone.nextSibling) {
rightNodes.appendChild(tmpNode);
clone = parentC
} while (parent !== parentClone);
tmpNode = parent.parentN
tmpNode.insertBefore(leftNodes, parent);
tmpNode.insertBefore(rightNodes, parent);
tmpNode.insertBefore(node, rightNodes);
domUtils.remove(parent);
* 检查节点node是否是空inline节点
isEmptyInlineElement
* @grammar
UE.dom.domUtils.isEmptyInlineElement(node)
* @example
* &b&&i&&/i&&/b& =& 1
* &b&&i&&/i&&u&&/u&&/b& =& 1
* &b&&/b& =& 1
* &b&xx&i&&/i&&/b& =& 0
isEmptyInlineElement:function (node) {
if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
node = node.firstC
while (node) {
//如果是创建的bookmark就跳过
if (domUtils.isBookmarkNode(node)) {
if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
node.nodeType == 3 && !domUtils.isWhitespace(node)
node = node.nextS
* 删除node节点下的左右空白文本子节点
* @name trimWhiteTextNode
* @grammar UE.dom.domUtils.trimWhiteTextNode(node)
trimWhiteTextNode:function (node) {
function remove(dir) {
while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
node.removeChild(child);
remove('firstChild');
remove('lastChild');
* 合并node节点下相同的子节点
* @name mergeChild
* UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
* @example
* &p&&span style=&font-size:12&&xx&span style=&font-size:12&&aa&/span&xx&/span&&/p&
* ==& UE.dom.domUtils.mergeChild(node,'span')
* &p&&span style=&font-size:12&&xxaaxx&/span&&/p&
mergeChild:function (node, tagName, attrs) {
var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase());
for (var i = 0, ci = list[i++];) {
if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
//span单独处理
if (ci.tagName.toLowerCase() == 'span') {
if (node === ci.parentNode) {
domUtils.trimWhiteTextNode(node);
if (node.childNodes.length == 1) {
node.style.cssText = ci.style.cssText + &;& + node.style.cssT
domUtils.remove(ci, true);
ci.style.cssText = node.style.cssText + ';' + ci.style.cssT
if (attrs) {
var style = attrs.
if (style) {
style = style.split(';');
for (var j = 0, s = style[j++];) {
ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] = s.split(':')[1];
if (domUtils.isSameStyle(ci, node)) {
domUtils.remove(ci, true);
if (domUtils.isSameElement(node, ci)) {
domUtils.remove(ci, true);
* 原生方法getElementsByTagName的封装
* @name getElementsByTagName
* @grammar UE.dom.domUtils.getElementsByTagName(node,tagName)
//节点集合数组
getElementsByTagName:function (node, name,filter) {
if(filter && utils.isString(filter)){
var className =
function(node){return domUtils.hasClass(node,className)}
name = utils.trim(name).replace(/[ ]{2,}/g,' ').split(' ');
var arr = [];
for(var n = 0,ni=name[n++];){
var list = node.getElementsByTagName(ni);
for (var i = 0, ci = list[i++];) {
if(!filter || filter(ci))
arr.push(ci);
* 将节点node合并到父节点上
* @name mergeToParent
* @grammar UE.dom.domUtils.mergeToParent(node)
* @example
* &span style=&color:#fff&&&span style=&font-size:12px&&xxx&/span&&/span& ==& &span style=&color:#font-size:12px&&xxx&/span&
mergeToParent:function (node) {
var parent = node.parentN
while (parent && dtd.$removeEmpty[parent.tagName]) {
if (parent.tagName == node.tagName || parent.tagName == 'A') {//针对a标签单独处理
domUtils.trimWhiteTextNode(parent);
//span需要特殊处理
不处理这样的情况 &span stlye=&color:#fff&&xxx&span style=&color:#ccc&&xxx&/span&xxx&/span&
if (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node)
|| (parent.tagName == 'A' && node.tagName == 'SPAN')) {
if (parent.childNodes.length & 1 || parent !== node.parentNode) {
node.style.cssText = parent.style.cssText + &;& + node.style.cssT
parent = parent.parentN
parent.style.cssText += &;& + node.style.cssT
//trace:952 a标签要保持下划线
if (parent.tagName == 'A') {
parent.style.textDecoration = 'underline';
if (parent.tagName != 'A') {
parent === node.parentNode && domUtils.remove(node, true);
parent = parent.parentN
* 合并节点node的左右兄弟节点
* @name mergeSibling
* @grammar UE.dom.domUtils.mergeSibling(node)
* @grammar UE.dom.domUtils.mergeSibling(node,ignorePre)
//ignorePre指定是否忽略左兄弟
* @grammar UE.dom.domUtils.mergeSibling(node,ignorePre,ignoreNext)
//ignoreNext指定是否忽略右兄弟
* @example
* &b&xxxx&/b&&b&ooo&/b&&b&xxxx&/b& ==& &b&xxxxoooxxxx&/b&
mergeSibling:function (node, ignorePre, ignoreNext) {
function merge(rtl, start, node) {
if ((next = node[rtl]) && !domUtils.isBookmarkNode(next) && next.nodeType == 1 && domUtils.isSameElement(node, next)) {
while (next.firstChild) {
if (start == 'firstChild') {
node.insertBefore(next.lastChild, node.firstChild);
node.appendChild(next.firstChild);
domUtils.remove(next);
!ignorePre && merge('previousSibling', 'firstChild', node);
!ignoreNext && merge('nextSibling', 'lastChild', node);
* 设置节点node及其子节点不会被选中
* @name unSelectable
* @grammar UE.dom.domUtils.unSelectable(node)
unSelectable:ie || browser.opera ? function (node) {
node.onselectstart = function () {
node.onclick = node.onkeyup = node.onkeydown = function () {
node.unselectable = 'on';
node.setAttribute(&unselectable&, &on&);
for (var i = 0, ci = node.all[i++];) {
switch (ci.tagName.toLowerCase()) {
case 'iframe' :
case 'textarea' :
case 'input' :
case 'select' :
ci.unselectable = 'on';
node.setAttribute(&unselectable&, &on&);
} : function (node) {
node.style.MozUserSelect =
node.style.webkitUserSelect =
node.style.KhtmlUserSelect = 'none';
* 删除节点node上的属性attrNames,attrNames为属性名称数组
removeAttributes
* @grammar UE.dom.domUtils.removeAttributes(node,attrNames)
* @example
* //Before remove
* &span style=&font-size:14& id=&test& name=&followMe&&xxxxx&/span&
* //Remove
* UE.dom.domUtils.removeAttributes(node,[&id&,&name&]);
* //After remove
* &span style=&font-size:14&&xxxxx&/span&
removeAttributes:function (node, attrNames) {
attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g,' ').split(' ');
for (var i = 0, ci = attrNames[i++];) {
ci = attrFix[ci] ||
switch (ci) {
case 'className':
node[ci] = '';
case 'style':
node.style.cssText = '';
!browser.ie && node.removeAttributeNode(node.getAttributeNode('style'))
node.removeAttribute(ci);
* 在doc下创建一个标签名为tag,属性为attrs的元素
* @name createElement
* @grammar UE.dom.domUtils.createElement(doc,tag,attrs)
//返回创建的节点
createElement:function (doc, tag, attrs) {
return domUtils.setAttributes(doc.createElement(tag), attrs)
* 为节点node添加属性attrs,attrs为属性键值对
* @name setAttributes
* @grammar UE.dom.domUtils.setAttributes(node,attrs)
setAttributes:function (node, attrs) {
for (var attr in attrs) {
if(attrs.hasOwnProperty(attr)){
var value = attrs[attr];
switch (attr) {
case 'class':
//ie下要这样赋值,setAttribute不起作用
node.className =
case 'style' :
node.style.cssText = node.style.cssText + &;& +
case 'innerHTML':
node[attr] =
case 'value':
node.value =
node.setAttribute(attrFix[attr] || attr, value);
* 获取元素element的计算样式
* @name getComputedStyle
* @grammar UE.dom.domUtils.getComputedStyle(element,styleName)
=& String //返回对应样式名称的样式值
* @example
* getComputedStyle(document.body,&font-size&)
* getComputedStyle(form,&color&)
getComputedStyle:function (element, styleName) {
//一下的属性单独处理
var pros = 'width height top left';
if(pros.indexOf(styleName) & -1){
return element['offset' + styleName.replace(/^\w/,function(s){return s.toUpperCase()})] + 'px';
//忽略文本节点
if (element.nodeType == 3) {
element = element.parentN
//ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改.
if (browser.ie && browser.version & 9 && styleName == 'font-size' && !element.style.fontSize &&
!dtd.$empty[element.tagName] && !dtd.$nonChild[element.tagName]) {
var span = element.ownerDocument.createElement('span');
span.style.cssText = 'padding:0;border:0;font-family:';
span.innerHTML = '.';
element.appendChild(span);
var result = span.offsetH
element.removeChild(span);
return result + 'px';
var value = domUtils.getStyle(element, styleName) ||
(window.getComputedStyle ? domUtils.getWindow(element).getComputedStyle(element, '').getPropertyValue(styleName) :
( element.currentStyle || element.style )[utils.cssStyleToDomStyle(styleName)]);
} catch (e) {
return &&;
return utils.transUnitToPx(utils.fixColor(styleName, value));
* 在元素element上删除classNames,支持同时删除多个
* @name removeClasses
* @grammar UE.dom.domUtils.removeClasses(element,classNames)
* @example
* //执行方法前的dom结构
* &span class=&test1 test2 test3&&xxx&/span&
* //执行方法
* UE.dom.domUtils.removeClasses(element,[&test1&,&test3&])
* //执行方法后的dom结构
* &span class=&test2&&xxx&/span&
removeClasses:function (elm, classNames) {
classNames = utils.isArray(classNames) ? classNames :
utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' ');
for(var i = 0,ci,cls = elm.classNci=classNames[i++];){
cls = cls.replace(new RegExp('\\b' + ci + '\\b'),'')
cls = utils.trim(cls).replace(/[ ]{2,}/g,' ');
elm.className =
domUtils.removeAttributes(elm,['class']);
* 在元素element上增加一个样式类className,支持以空格分开的多个类名
* 如果相同的类名将不会添加
* @name addClass
* @grammar UE.dom.domUtils.addClass(element,classNames)
addClass:function (elm, classNames) {
classNames = utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' ');
for(var i = 0,ci,cls = elm.classNci=classNames[i++];){
if(!new RegExp('\\b' + ci + '\\b').test(cls)){
elm.className += ' ' +
* 判断元素element是否包含样式类名className,支持以空格分开的多个类名,多个类名顺序不同也可以比较
* @name hasClass
* @grammar UE.dom.domUtils.hasClass(element,className)
=&true|false
hasClass:function (element, className) {
if(utils.isRegExp(className)){
return className.test(element.className)
className = utils.trim(className).replace(/[ ]{2,}/g,' ').split(' ');
for(var i = 0,ci,cls = element.classNci=className[i++];){
if(!new RegExp('\\b' + ci + '\\b','i').test(cls)){
return i - 1 == className.
* 阻止事件默认行为
* @param {Event} evt
需要组织的事件对象
preventDefault:function (evt) {
evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
* 删除元素element的样式
* @grammar UE.dom.domUtils.removeStyle(element,name)
删除的样式名称
removeStyle:function (element, name) {
if(browser.ie && browser.version & 8){
element.style.cssText = element.style.cssText.replace(new RegExp(name + '\s*:\s*[^;]+;?'),'')
if (element.style.removeProperty) {
element.style.removeProperty (name);
element.style.removeAttribute (utils.cssStyleToDomStyle(name));
if (!element.style.cssText) {
domUtils.removeAttributes(element, ['style']);
* 获取元素element的某个样式值
* @name getStyle
* @grammar UE.dom.domUtils.getStyle(element,name)
getStyle:function (element, name) {
var value = element.style[ utils.cssStyleToDomStyle(name) ];
return utils.fixColor(name, value);
* 为元素element设置样式属性值
* @name setStyle
* @grammar UE.dom.domUtils.setStyle(element,name,value)
setStyle:function (element, name, value) {
element.style[utils.cssStyleToDomStyle(name)] =
* 为元素element设置样式属性值
* @name setStyles
* @grammar UE.dom.domUtils.setStyle(element,styles)
//styles为样式键值对
setStyles:function (element, styles) {
for (var name in styles) {
if (styles.hasOwnProperty(name)) {
domUtils.setStyle(element, name, styles[name]);
* 删除_moz_dirty属性
* @function
removeDirtyAttr:function (node) {
for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
ci.removeAttribute('_moz_dirty');
node.removeAttribute('_moz_dirty');
* 返回子节点的数量
* @function
* @param {Node}
{Function}
过滤子节点的规则,若为空,则得到所有子节点的数量
* @return {Number}
符合条件子节点的数量
getChildCount:function (node, fn) {
var count = 0, first = node.firstC
fn = fn || function () {
while (first) {
if (fn(first)) {
first = first.nextS
* 判断是否为空节点
* @function
* @param {Node}
* @return {Boolean}
是否为空节点
isEmptyNode:function (node) {
return !node.firstChild || domUtils.getChildCount(node, function (node) {
!domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
* 清空节点所有的className
* @function
* @param {Array}
clearSelectedArr:function (nodes) {
while (node = nodes.pop()) {
domUtils.removeAttributes(node, ['class']);
* 将显示区域滚动到显示节点的位置
* @function
window对象
距离上方的偏移量
scrollToView:function (node, win, offsetTop) {
var getViewPaneSize = function () {
var doc = win.document,
mode = patMode == 'CSS1Compat';
width:( mode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0,
height:( mode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0
getScrollPosition = function (win) {
if ('pageXOffset' in win) {
x:win.pageXOffset || 0,
y:win.pageYOffset || 0
var doc = win.
x:doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
y:doc.documentElement.scrollTop || doc.body.scrollTop || 0
var winHeight = getViewPaneSize().height, offset = winHeight * -1 + offsetT
offset += (node.offsetHeight || 0);
var elementPosition = domUtils.getXY(node);
offset += elementPosition.y;
var currentScroll = getScrollPosition(win).y;
// offset += 50;
if (offset & currentScroll || offset & currentScroll - winHeight) {
win.scrollTo(0, offset + (offset & 0 ? -20 : 20));
* 判断节点是否为br
* @function
* @param {Node}
isBr:function (node) {
return node.nodeType == 1 && node.tagName == 'BR';
isFillChar:function (node,isInStart) {
return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length
isStartInblock:function (range) {
var tmpRange = range.cloneRange(),
start = tmpRange.startContainer,
if(start.nodeType == 1 && start.childNodes[tmpRange.startOffset]){
start = start.childNodes[tmpRange.startOffset];
var pre = start.previousS
while(pre && domUtils.isFillChar(pre)){
pre = pre.previousS
if(this.isFillChar(start,true) && tmpRange.startOffset == 1){
tmpRange.setStartBefore(start);
start = tmpRange.startC
while (start && domUtils.isFillChar(start)) {
start = start.previousSibling
if (tmp) {
tmpRange.setStartBefore(tmp);
start = tmpRange.startC
if (start.nodeType == 1 && domUtils.isEmptyNode(start) && tmpRange.startOffset == 1) {
tmpRange.setStart(start, 0).collapse(true);
while (!tmpRange.startOffset) {
start = tmpRange.startC
if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
var pre = tmpRange.startContainer.previousSibling,
if (!pre) {
tmpRange.setStartBefore(tmpRange.startContainer);
while (pre && domUtils.isFillChar(pre)) {
pre = pre.previousS
if (tmpNode) {
tmpRange.setStartBefore(tmpNode);
tmpRange.setStartBefore(tmpRange.startContainer);
return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0;
isEmptyBlock:function (node) {
var reg = new RegExp('[ \t\r\n' + domUtils.fillChar + ']', 'g');
if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length & 0) {
for (var n in dtd.$isNotEmpty) {
if (node.getElementsByTagName(n).length) {
setViewportOffset:function (element, offset) {
var left = parseInt(element.style.left) | 0;
var top = parseInt(element.style.top) | 0;
var rect = element.getBoundingClientRect();
var offsetLeft = offset.left - rect.
var offsetTop = offset.top - rect.
if (offsetLeft) {
element.style.left = left + offsetLeft + 'px';
if (offsetTop) {
element.style.top = top + offsetTop + 'px';
fillNode:function (doc, node) {
var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
node.innerHTML = '';
node.appendChild(tmpNode);
moveChild:function (src, tag, dir) {
while (src.firstChild) {
if (dir && tag.firstChild) {
tag.insertBefore(src.lastChild, tag.firstChild);
tag.appendChild(src.firstChild);
//判断是否有额外属性
hasNoAttributes:function (node) {
return browser.ie ? /^&\w+\s*?&/.test(node.outerHTML) : node.attributes.length == 0;
//判断是否是编辑器自定义的参数
isCustomeNode:function (node) {
return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
isTagNode:function (node, tagName) {
return node.nodeType == 1 && new RegExp(node.tagName,'i').test(tagName)
* 对于nodelist用filter进行过滤
* @name filterNodeList
* @since 1.2.4+
* @grammar UE.dom.domUtils.filterNodeList(nodelist,filter,onlyFirst)
* @example
* UE.dom.domUtils.filterNodeList(document.getElementsByTagName('*'),'div p') //返回第一个是div或者p的节点
* UE.dom.domUtils.filterNodeList(document.getElementsByTagName('*'),function(n){return n.getAttribute('src')})
* //返回第一个带src属性的节点
* UE.dom.domUtils.filterNodeList(document.getElementsByTagName('*'),'i',true) //返回数组,里边都是i节点
filterNodeList : function(nodelist,filter,forAll){
var results = [];
if(!utils .isFunction(filter)){
filter = function(n){
return utils.indexOf(utils.isArray(str) ? str:str.split(' '), n.tagName.toLowerCase()) != -1
utils.each(nodelist,function(n){
filter(n) && results.push(n)
return results.length
== 0 ? null : results.length == 1 || !forAll ? results[0] : results
isInNodeEndBoundary : function (rng,node){
var start = rng.startC
if(start.nodeType == 3 && rng.startOffset != start.nodeValue.length){
if(start.nodeType == 1 && rng.startOffset != start.childNodes.length){
while(start !== node){
if(start.nextSibling){
start = start.parentN
isBoundaryNode : function (node,dir){
while(!domUtils.isBody(node)){
node = node.parentN
if(tmp !== node[dir]){
var fillCharReg = new RegExp(domUtils.fillChar, 'g');
///import editor.js
///import core/utils.js
///import core/browser.js
///import core/dom/dom.js
///import core/dom/dtd.js
///import core/dom/domUtils.js
* @name UE.dom.Range
* @anthor zhanyi
* @short Range
* @import editor.js,core/utils.js,core/browser.js,core/dom/domUtils.js,core/dom/dtd.js
* @desc Range范围实现类,本类是UEditor底层核心类,统一w3cRange和ieRange之间的差异,包括接口和属性
(function () {
var guid = 0,
fillChar = domUtils.fillChar,
* 更新range的collapse状态
function updateCollapse(range) {
range.collapsed =
range.startContainer && range.endContainer &&
range.startContainer === range.endContainer &&
range.startOffset == range.endO
function selectOneNode(rng){
return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
function setEndPoint(toStart, node, offset, range) {
//如果node是自闭合标签要处理
if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
node = node.parentN
if (toStart) {
range.startContainer =
range.startOffset =
if (!range.endContainer) {
range.collapse(true);
range.endContainer =
range.endOffset =
if (!range.startContainer) {
range.collapse(false);
updateCollapse(range);
function execContentsAction(range, action) {
//调整边界
//range.includeBookmark();
var start = range.startContainer,
end = range.endContainer,
startOffset = range.startOffset,
endOffset = range.endOffset,
doc = range.document,
frag = doc.createDocumentFragment(),
tmpStart, tmpE
if (start.nodeType == 1) {
start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
if (end.nodeType == 1) {
end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
if (start === end && start.nodeType == 3) {
frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
//is not clone
if (action) {
start.deleteData(startOffset, endOffset - startOffset);
range.collapse(true);
var current, currentLevel, clone = frag,
startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
for (var i = 0; startParents[i] == endParents[i];) {
for (var j = i, si = startParents[j]; j++) {
current = si.nextS
if (si == start) {
if (!tmpStart) {
if (range.startContainer.nodeType == 3) {
clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
//is not clone
if (action) {
start.deleteData(startOffset, start.nodeValue.length - startOffset);
clone.appendChild(!action ? start.cloneNode(true) : start);
currentLevel = si.cloneNode(false);
clone.appendChild(currentLevel);
while (current) {
if (current === end || current === endParents[j]) {
si = current.nextS
clone.appendChild(!action ? current.cloneNode(true) : current);
clone = currentL
if (!startParents[i]) {
clone.appendChild(startParents[i - 1].cloneNode(false));
clone = clone.firstC
for (var j = i, ei = endParents[j]; j++) {
current = ei.previousS
if (ei == end) {
if (!tmpEnd && range.endContainer.nodeType == 3) {
clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
//is not clone
if (action) {
end.deleteData(0, endOffset);
currentLevel = ei.cloneNode(false);
clone.appendChild(currentLevel);
//如果两端同级,右边第一次已经被开始做了
if (j != i || !startParents[i]) {
while (current) {
if (current === start) {
ei = current.previousS
clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
clone = currentL
if (action) {
range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
tmpStart && domUtils.remove(tmpStart);
tmpEnd && domUtils.remove(tmpEnd);
* @name Range
* @grammar new UE.dom.Range(document)
=& Range 实例
* @desc 创建一个跟document绑定的空的Range实例
* - ***startContainer*** 开始边界的容器节点,可以是elementNode或者是textNode
* - ***startOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
* - ***endContainer*** 结束边界的容器节点,可以是elementNode或者是textNode
* - ***endOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
* - ***document*** 跟range关联的document对象
* - ***collapsed*** 是否是闭合状态
var Range = dom.Range = function (document) {
me.startContainer =
me.startOffset =
me.endContainer =
me.endOffset =
me.document =
me.collapsed =
* 删除fillData
* @param doc
* @param excludeNode
function removeFillData(doc, excludeNode) {
if (fillData && domUtils.inDoc(fillData, doc)) {
if (!fillData.nodeValue.replace(fillCharReg, '').length) {
var tmpNode = fillData.parentN
domUtils.remove(fillData);
while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
//safari的contains有bug
(browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
fillData = tmpNode.parentN
domUtils.remove(tmpNode);
tmpNode = fillD
fillData.nodeValue = fillData.nodeVa

我要回帖

更多关于 merge plugins mod 的文章

 

随机推荐