/*! * UEditor Mini版本 * version: 1.2.2 * build: Fri Feb 10 2017 15:00:06 GMT+0800 (CST) */ (function($){ UMEDITOR_CONFIG = window.UMEDITOR_CONFIG || {}; window.UM = { plugins : {}, commands : {}, I18N : {}, version : "1.2.2" }; var dom = UM.dom = {}; /** * 浏览器判断模块 * @file * @module UE.browser * @since 1.2.6.1 */ /** * 提供浏览器检测的模块 * @unfile * @module UE.browser */ var browser = UM.browser = function(){ var agent = navigator.userAgent.toLowerCase(), opera = window.opera, browser = { /** * @property {boolean} ie 检测当前浏览器是否为IE * @example * ```javascript * if ( UE.browser.ie ) { * console.log( '当前浏览器是IE' ); * } * ``` */ ie : /(msie\s|trident.*rv:)([\w.]+)/.test(agent), /** * @property {boolean} opera 检测当前浏览器是否为Opera * @example * ```javascript * if ( UE.browser.opera ) { * console.log( '当前浏览器是Opera' ); * } * ``` */ opera : ( !!opera && opera.version ), /** * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器 * @example * ```javascript * if ( UE.browser.webkit ) { * console.log( '当前浏览器是webkit内核浏览器' ); * } * ``` */ webkit : ( agent.indexOf( ' applewebkit/' ) > -1 ), /** * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下 * @example * ```javascript * if ( UE.browser.mac ) { * console.log( '当前浏览器运行在mac平台下' ); * } * ``` */ mac : ( agent.indexOf( 'macintosh' ) > -1 ), /** * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下 * @example * ```javascript * if ( UE.browser.quirks ) { * console.log( '当前浏览器运行处于“怪异模式”' ); * } * ``` */ quirks : ( document.compatMode == 'BackCompat' ) }; /** * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核 * @example * ```javascript * if ( UE.browser.gecko ) { * console.log( '当前浏览器内核是gecko内核' ); * } * ``` */ browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie); var version = 0; // Internet Explorer 6.0+ if ( browser.ie ){ var v1 = agent.match(/(?:msie\s([\w.]+))/); var v2 = agent.match(/(?:trident.*rv:([\w.]+))/); if(v1 && v2 && v1[1] && v2[1]){ version = Math.max(v1[1]*1,v2[1]*1); }else if(v1 && v1[1]){ version = v1[1]*1; }else if(v2 && v2[1]){ version = v2[1]*1; }else{ version = 0; } browser.ie11Compat = document.documentMode == 11; /** * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie9Compat ) { * console.log( '当前浏览器运行在IE9兼容模式下' ); * } * ``` */ browser.ie9Compat = document.documentMode == 9; /** * @property { boolean } ie8 检测浏览器是否是IE8浏览器 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie8 ) { * console.log( '当前浏览器是IE8浏览器' ); * } * ``` */ browser.ie8 = !!document.documentMode; /** * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie8Compat ) { * console.log( '当前浏览器运行在IE8兼容模式下' ); * } * ``` */ browser.ie8Compat = document.documentMode == 8; /** * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie7Compat ) { * console.log( '当前浏览器运行在IE7兼容模式下' ); * } * ``` */ browser.ie7Compat = ( ( version == 7 && !document.documentMode ) || document.documentMode == 7 ); /** * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie6Compat ) { * console.log( '当前浏览器运行在IE6模式或者怪异模式下' ); * } * ``` */ browser.ie6Compat = ( version < 7 || browser.quirks ); browser.ie9above = version > 8; browser.ie9below = version < 9; } // Gecko. 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; } } /** * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号 * @warning 如果浏览器不是chrome, 则该值为undefined * @example * ```javascript * if ( UE.browser.chrome ) { * console.log( '当前浏览器是Chrome' ); * } * ``` */ if (/chrome\/(\d+\.\d)/i.test(agent)) { browser.chrome = + RegExp['\x241']; } /** * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号 * @warning 如果浏览器不是safari, 则该值为undefined * @example * ```javascript * if ( UE.browser.safari ) { * console.log( '当前浏览器是Safari' ); * } * ``` */ 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] ); /** * @property { Number } version 检测当前浏览器版本号 * @remind *
unhtml
*/
html:function (str) {
return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) {
return {
'<':'<',
'&':'&',
'"':'"',
'>':'>',
''':"'"
}[m]
}) : '';
},
/**
* 将css样式转换为驼峰的形式。如font-size => fontSize
* @name cssStyleToDomStyle
* @grammar UM.utils.cssStyleToDomStyle(cssName) => String
*/
cssStyleToDomStyle:function () {
var test = document.createElement('div').style,
cache = {
'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 UM.utils.loadFile(doc,obj)
* @grammar UM.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) {
try {
for (var i = 0, ci; ci = tmpList[i++];) {
if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
return ci;
}
}
} catch (e) {
return null;
}
}
return function (doc, obj, fn) {
var item = getItem(doc, obj);
if (item) {
if (item.ready) {
fn && fn();
} else {
item.funs.push(fn)
}
return;
}
tmpList.push({
doc:doc,
url:obj.src || obj.href,
funs:[fn]
});
if (!doc.body) {
var html = [];
for (var p in obj) {
if (p == 'tag')continue;
html.push(p + '="' + obj[p] + '"')
}
doc.write('<' + obj.tag + ' ' + html.join(' ') + ' >' + obj.tag + '>');
return;
}
if (obj.id && doc.getElementById(obj.id)) {
return;
}
var element = doc.createElement(obj.tag);
delete obj.tag;
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 (var fi; fi = item.funs.pop();) {
fi();
}
}
element.onload = element.onreadystatechange = null;
}
};
element.onerror = function () {
throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file umeditor.config.js ')
};
doc.getElementsByTagName("head")[0].appendChild(element);
}
}(),
/**
* 判断obj对象是否为空
* @name isEmptyObject
* @grammar UM.utils.isEmptyObject(obj) => true|false
* @example
* UM.utils.isEmptyObject({}) ==>true
* UM.utils.isEmptyObject([]) ==>true
* UM.utils.isEmptyObject("") ==>true
*/
isEmptyObject:function (obj) {
if (obj == null) return true;
if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
for (var key in obj) if (obj.hasOwnProperty(key)) return false;
return true;
},
/**
* 统一将颜色值使用16进制形式表示
* @name fixColor
* @grammar UM.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; color = array[i++];) {
color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
value += color.length == 1 ? "0" + color : color;
}
value = value.toUpperCase();
}
return value;
},
/**
* 深度克隆对象,从source到target
* @name clone
* @grammar UM.utils.clone(source) => anthorObj 新的对象是完整的source的副本
* @grammar UM.utils.clone(source,target) => target包含了source的所有内容,重名会覆盖
*/
clone:function (source, target) {
var tmp;
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])
} else {
target[i] = tmp;
}
}
}
return target;
},
/**
* 转换cm/pt到px
* @name transUnitToPx
* @grammar UM.utils.transUnitToPx('20pt') => '27px'
* @grammar UM.utils.transUnitToPx('0pt') => '0'
*/
transUnitToPx:function (val) {
if (!/(pt|cm)/.test(val)) {
return val
}
var unit;
val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
val = v;
unit = u;
});
switch (unit) {
case 'cm':
val = parseFloat(val) * 25;
break;
case 'pt':
val = Math.round(parseFloat(val) * 96 / 72);
}
return val + (val ? 'px' : '');
},
/**
* 动态添加css样式
* @name cssRule
* @grammar UM.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
* @grammar UM.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
* @grammar UM.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
* @grammar UM.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
*/
cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) {
var indexList, index;
doc = doc || document;
if (doc.indexList) {
indexList = doc.indexList;
} else {
indexList = doc.indexList = {};
}
var sheetStyle;
if (!indexList[key]) {
if (style === undefined) {
return ''
}
sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
indexList[key] = index;
} else {
sheetStyle = doc.styleSheets[indexList[key]];
}
if (style === undefined) {
return sheetStyle.cssText
}
sheetStyle.cssText = style || ''
} : function (key, style, doc) {
doc = doc || document;
var head = doc.getElementsByTagName('head')[0], node;
if (!(node = doc.getElementById(key))) {
if (style === undefined) {
return ''
}
node = doc.createElement('style');
node.id = key;
head.appendChild(node)
}
if (style === undefined) {
return node.innerHTML
}
if (style !== '') {
node.innerHTML = style;
} else {
head.removeChild(node)
}
},
/**
* etpl 渲染函数
* @name render
* @grammar UM.utils.render(tpl, data) => string
*/
render: function (tpl, data) {
var _render = etpl.compile(tpl);
return _render(data);
}
};
/**
* 判断str是否为字符串
* @name isString
* @grammar UM.utils.isString(str) => true|false
*/
/**
* 判断array是否为数组
* @name isArray
* @grammar UM.utils.isArray(obj) => true|false
*/
/**
* 判断obj对象是否为方法
* @name isFunction
* @grammar UM.utils.isFunction(obj) => true|false
*/
/**
* 判断obj对象是否为数字
* @name isNumber
* @grammar UM.utils.isNumber(obj) => true|false
*/
utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function (v) {
UM.utils['is' + v] = function (obj) {
return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
}
});
/**
* @file
* @name UM.EventBase
* @short EventBase
* @import editor.js,core/utils.js
* @desc UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
* 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
*/
var EventBase = UM.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
* }else{
* //do something
* }
* console.log(this.getContent) // this是注册的事件的编辑器实例
* })
*/
addListener:function (types, listener) {
types = utils.trim(types).split(' ');
for (var i = 0, ti; 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; ti = types[i++];) {
utils.removeItem(getListener(this, ti) || [], listener);
}
},
/**
* 触发事件
* @name fireEvent
* @grammar editor.fireEvent(types) //types为事件名称,多个可用空格分隔
* @example
* editor.fireEvent("selectionchange");
*/
fireEvent:function () {
var types = arguments[0];
types = utils.trim(types).split(' ');
for (var i = 0, ti; ti = types[i++];) {
var listeners = getListener(this, ti),
r, t, k;
if (listeners) {
k = listeners.length;
while (k--) {
if(!listeners[k])continue;
t = listeners[k].apply(this, arguments);
if(t === true){
return t;
}
if (t !== undefined) {
r = t;
}
}
}
if (t = this['on' + ti.toLowerCase()]) {
r = t.apply(this, arguments);
}
}
return r;
}
};
/**
* 获得对象所拥有监听类型的所有监听器
* @public
* @function
* @param {Object} obj 查询监听器的对象
* @param {String} type 事件类型
* @param {Boolean} force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
* @returns {Array} 监听器数组
*/
function getListener(obj, type, force) {
var allListeners;
type = type.toLowerCase();
return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) )
&& ( allListeners[type] || force && ( allListeners[type] = [] ) ) );
}
///import editor.js
///import core/dom/dom.js
///import core/utils.js
/**
* dtd html语义化的体现类
* @constructor
* @namespace dtd
*/
var dtd = dom.dtd = (function() {
function _( s ) {
for (var k in s) {
s[k.toUpperCase()] = s[k];
}
return s;
}
var X = utils.extend2;
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,ol:1,ul: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}),
empty = _({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1});
return _({
// $ 表示自定的属性
// body外的元素列表.
$nonBodyContent: X( V, U, S ),
//块结构元素列表
$block : block,
//内联元素列表
$inline : L,
$inlineWithA : X(_({a:1}),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}),
html: U,
head: T,
style: N,
script: N,
body: P,
base: {},
link: {},
meta: {},
title: N,
col : {},
tr : _({td:1,th:1}),
img : {},
embed: {},
colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}),
noscript : P,
td : P,
br : {},
th : P,
center : P,
kbd : L,
button : X( I, E ),
basefont : {},
h5 : L,
h4 : L,
samp : L,
h6 : L,
ol : Q,
h1 : L,
h3 : L,
option : N,
h2 : L,
form : X( A, D, E, I ),
select : _({optgroup:1,option:1}),
font : L,
ins : L,
menu : Q,
abbr : L,
label : L,
table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),
code : L,
tfoot : M,
cite : L,
li : P,
input : {},
iframe : P,
strong : L,
textarea : N,
noframes : P,
big : L,
small : L,
//trace:
span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}),
hr : L,
dt : L,
sub : L,
optgroup : _({option:1}),
param : {},
bdo : L,
'var' : L,
div : P,
object : O,
sup : L,
dd : P,
strike : L,
area : {},
dir : Q,
map : X( _({area:1,form:1,p:1}), A, F, E ),
applet : O,
dl : _({dt:1,dd:1}),
del : L,
isindex : {},
fieldset : X( _({legend:1}), K ),
thead : M,
ul : Q,
acronym : L,
b : L,
a : X( _({a:1}), J ),
blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),
caption : L,
i : L,
u : L,
tbody : M,
s : L,
address : X( D, I ),
tt : L,
legend : L,
q : L,
pre : X( G, C ),
p : X(_({'a':1}),L),
em :L,
dfn : L
});
})();
/**
* @file
* @name UM.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],
parent;
!tmpNode && (tmpNode = node[ltr]);
while (!tmpNode && (parent = (parent || node).parentNode)) {
if (parent.tagName == 'BODY' || guard && !guard(parent)) {
return null;
}
tmpNode = parent[ltr];
}
if (tmpNode && fn && !fn(tmpNode)) {
return getDomNode(tmpNode, start, ltr, false, fn);
}
return tmpNode;
}
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部分--------------------------------
keys: {
/*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*/
},
breakParent:function (node, parent) {
var tmpNode,
parentClone = node,
clone = node,
leftNodes,
rightNodes;
do {
parentClone = parentClone.parentNode;
if (leftNodes) {
tmpNode = parentClone.cloneNode(false);
tmpNode.appendChild(leftNodes);
leftNodes = tmpNode;
tmpNode = parentClone.cloneNode(false);
tmpNode.appendChild(rightNodes);
rightNodes = tmpNode;
} else {
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 = parentClone;
} while (parent !== parentClone);
tmpNode = parent.parentNode;
tmpNode.insertBefore(leftNodes, parent);
tmpNode.insertBefore(rightNodes, parent);
tmpNode.insertBefore(node, rightNodes);
domUtils.remove(parent);
return node;
},
trimWhiteTextNode:function (node) {
function remove(dir) {
var child;
while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
node.removeChild(child);
}
}
remove('firstChild');
remove('lastChild');
},
/**
* 获取节点A相对于节点B的位置关系
* @name getPosition
* @grammar UM.dom.domUtils.getPosition(nodeA,nodeB) => Number
* @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
return 0;
}
var node,
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
return 1;
}
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
return 4
}
}
// domUtils.POSITION_FOLLOWING
return 2;
},
/**
* 返回节点node在父节点中的索引位置
* @name getNodeIndex
* @grammar UM.dom.domUtils.getNodeIndex(node) => Number //索引值从0开始
*/
getNodeIndex: function (node, ignoreTextNode) {
var preNode = node,
i = 0;
while (preNode = preNode.previousSibling) {
if (ignoreTextNode && preNode.nodeType == 3) {
if (preNode.nodeType != preNode.nextSibling.nodeType) {
i++;
}
continue;
}
i++;
}
return i;
},
/**
* 检测节点node是否在节点doc的树上,实质上是检测是否被doc包含
* @name inDoc
* @grammar UM.dom.domUtils.inDoc(node,doc) => true|false
*/
inDoc: function (node, doc) {
return domUtils.getPosition(node, doc) == 10;
},
/**
* 查找node节点的祖先节点
* @name findParent
* @grammar UM.dom.domUtils.findParent(node) => Element // 直接返回node节点的父节点
* @grammar UM.dom.domUtils.findParent(node,filterFn) => Element //filterFn为过滤函数,node作为参数,返回true时才会将node作为符合要求的节点返回
* @grammar UM.dom.domUtils.findParent(node,filterFn,includeSelf) => Element //includeSelf指定是否包含自身
*/
findParent: function (node, filterFn, includeSelf) {
if (node && !domUtils.isBody(node)) {
node = includeSelf ? node : node.parentNode;
while (node) {
if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
}
node = node.parentNode;
}
}
return null;
},
/**
* 通过tagName查找node节点的祖先节点
* @name findParentByTagName
* @grammar UM.dom.domUtils.findParentByTagName(node,tagNames) => Element //tagNames支持数组,区分大小写
* @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf) => Element //includeSelf指定是否包含自身
* @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf,excludeFn) => Element //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 UM.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
* @grammar UM.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
* @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
* @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,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 UM.dom.domUtils.insertAfter(node,newNode) => newNode
*/
insertAfter: function (node, newNode) {
return node.parentNode.insertBefore(newNode, node.nextSibling);
},
/**
* 删除节点node,并根据keepChildren指定是否保留子节点
* @name remove
* @grammar UM.dom.domUtils.remove(node) => node
* @grammar UM.dom.domUtils.remove(node,keepChildren) => node
*/
remove: function (node, keepChildren) {
var parent = node.parentNode,
child;
if (parent) {
if (keepChildren && node.hasChildNodes()) {
while (child = node.firstChild) {
parent.insertBefore(child, node);
}
}
parent.removeChild(node);
}
return node;
},
/**
* 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
* 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
* @method getNextDomNode
* @param { Node } node 需要获取其后的兄弟节点的节点对象
* @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
* @example
* ```html
*
* adjustmentBoundary
* @grammar range.shrinkBoundary(true) => Range //仅调整开始位置,忽略结束位置
* @example
* xx[xxxxx] ==> xx[xxxxx]
* x[xx]xxx ==> x[xx]xxx
* [xxxxxxxxxxx] ==> [xxxxxxxxxxx]
*/
shrinkBoundary:function (ignoreEnd) {
var me = this, child,
collapsed = me.collapsed;
function check(node){
return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
}
while (me.startContainer.nodeType == 1 //是element
&& (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
&& check(child)) {
me.setStart(child, 0);
}
if (collapsed) {
return me.collapse(true);
}
if (!ignoreEnd) {
while (me.endContainer.nodeType == 1//是element
&& me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
&& (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
&& check(child)) {
me.setEnd(child, child.childNodes.length);
}
}
return me;
},
/**
* 调整边界容器,如果是textNode,就调整到elementNode上
* @name trimBoundary
* @grammar range.trimBoundary([ignoreEnd]) => Range //true忽略结束边界
* @example
* DOM Element :
* |xxx
* startContainer = xxx; startOffset = 0
* //执行后本方法后
* startContainer = ; startOffset = 0
* @example
* Dom Element :
* xx|x
* startContainer = xxx; startOffset = 2
* //执行本方法后,xxx被实实在在地切分成两个TextNode
* startContainer = ; startOffset = 1
*/
trimBoundary:function (ignoreEnd) {
this.txtToElmBoundary();
var start = this.startContainer,
offset = this.startOffset,
collapsed = this.collapsed,
end = this.endContainer;
if (start.nodeType == 3) {
if (offset == 0) {
this.setStartBefore(start);
} else {
if (offset >= start.nodeValue.length) {
this.setStartAfter(start);
} else {
var textNode = domUtils.split(start, offset);
//跟新结束边界
if (start === end) {
this.setEnd(textNode, this.endOffset - offset);
} else if (start.parentNode === end) {
this.endOffset += 1;
}
this.setStartBefore(textNode);
}
}
if (collapsed) {
return this.collapse(true);
}
}
if (!ignoreEnd) {
offset = this.endOffset;
end = this.endContainer;
if (end.nodeType == 3) {
if (offset == 0) {
this.setEndBefore(end);
} else {
offset < end.nodeValue.length && domUtils.split(end, offset);
this.setEndAfter(end);
}
}
}
return this;
},
/**
* 如果选区在文本的边界上,就扩展选区到文本的父节点上
* @name txtToElmBoundary
* @example
* Dom Element :
* |xxx
* startContainer = xxx; startOffset = 0
* //本方法执行后
* startContainer = ; startOffset = 0
* @example
* Dom Element :
* xxx|
* startContainer = xxx; startOffset = 3
* //本方法执行后
* startContainer = ; startOffset = 1
*/
txtToElmBoundary:function (ignoreCollapsed) {
function adjust(r, c) {
var container = r[c + 'Container'],
offset = r[c + 'Offset'];
if (container.nodeType == 3) {
if (!offset) {
r['set' + c.replace(/(\w)/, function (a) {
return a.toUpperCase();
}) + 'Before'](container);
} else if (offset >= container.nodeValue.length) {
r['set' + c.replace(/(\w)/, function (a) {
return a.toUpperCase();
}) + 'After' ](container);
}
}
}
if (ignoreCollapsed || !this.collapsed) {
adjust(this, 'start');
adjust(this, 'end');
}
return this;
},
/**
* 在当前选区的开始位置前插入一个节点或者fragment,range的开始位置会在插入节点的前边
* @name insertNode
* @grammar range.insertNode(node) => Range //node可以是textNode,elementNode,fragment
* @example
* Range :
* xxx[xxxxx
xxxx]xsdfsdf
* 待插入Node : *ssss
* 执行本方法后的Range : * xxx[ssss
xxxxx
xxxx]xsdfsdf
*/ insertNode:function (node) { var first = node, length = 1; if (node.nodeType == 11) { first = node.firstChild; length = node.childNodes.length; } this.trimBoundary(true); var start = this.startContainer, offset = this.startOffset; var nextNode = start.childNodes[ offset ]; if (nextNode) { start.insertBefore(node, nextNode); } else { start.appendChild(node); } if (first.parentNode === this.endContainer) { this.endOffset = this.endOffset + length; } return this.setStartBefore(first); }, /** * 设置光标闭合位置,toEnd设置为true时光标将闭合到选区的结尾 * @name setCursor * @grammar range.setCursor([toEnd]) => Range //toEnd为true时,光标闭合到选区的末尾 */ setCursor:function (toEnd, noFillData) { return this.collapse(!toEnd).select(noFillData); }, /** * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置 * @name createBookmark * @grammar range.createBookmark([serialize]) => Object //{start:开始标记,end:结束标记,id:serialize} serialize为真时,开始结束标记是插入节点的id,否则是插入节点的引用 */ createBookmark:function (serialize, same) { var endNode, startNode = this.document.createElement('span'); startNode.style.cssText = 'display:none;line-height:0px;'; startNode.appendChild(this.document.createTextNode('\u200D')); startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++); if (!this.collapsed) { endNode = startNode.cloneNode(true); endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++); } this.insertNode(startNode); if (endNode) { this.collapse().insertNode(endNode).setEndBefore(endNode); } this.setStartAfter(startNode); return { start:serialize ? startNode.id : startNode, end:endNode ? serialize ? endNode.id : endNode : null, id:serialize } }, /** * 移动边界到书签位置,并删除插入的书签节点 * @name moveToBookmark * @grammar range.moveToBookmark(bookmark) => Range //让当前的range选到给定bookmark的位置,bookmark对象是由range.createBookmark创建的 */ moveToBookmark:function (bookmark) { var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start, end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end; this.setStartBefore(start); domUtils.remove(start); if (end) { this.setEndBefore(end); domUtils.remove(end); } else { this.collapse(true); } return this; }, /** * 调整Range的边界,使其"缩小"到最合适的位置 * @name adjustmentBoundary * @grammar range.adjustmentBoundary() => Range //参见shrinkBoundary
* @example
* xx[xxxxx] ==> xx[xxxxx]
* x[xx]xxx ==> x[xx]xxx
*/
adjustmentBoundary:function () {
if (!this.collapsed) {
while (!domUtils.isBody(this.startContainer) &&
this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
) {
this.setStartAfter(this.startContainer);
}
while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
) {
this.setEndBefore(this.endContainer);
}
}
return this;
},
/**
* 得到一个自闭合的节点,常用于获取自闭和的节点,例如图片节点
* @name getClosedNode
* @grammar range.getClosedNode() => node|null
* @example
* xxxx[]xxx
*/
getClosedNode:function () {
var node;
if (!this.collapsed) {
var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
if (selectOneNode(range)) {
var child = range.startContainer.childNodes[range.startOffset];
if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
node = child;
}
}
}
return node;
},
/**
* 根据当前range选中内容节点(在页面上表现为反白显示)
* @name select
* @grammar range.select(); => Range
*/
select:browser.ie ? function (noFillData, textRange) {
var nativeRange;
if (!this.collapsed)
this.shrinkBoundary();
var node = this.getClosedNode();
if (node && !textRange) {
try {
nativeRange = this.document.body.createControlRange();
nativeRange.addElement(node);
nativeRange.select();
} catch (e) {}
return this;
}
var bookmark = this.createBookmark(),
start = bookmark.start,
end;
nativeRange = this.document.body.createTextRange();
nativeRange.moveToElementText(start);
nativeRange.moveStart('character', 1);
if (!this.collapsed) {
var nativeRangeEnd = this.document.body.createTextRange();
end = bookmark.end;
nativeRangeEnd.moveToElementText(end);
nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
} else {
if (!noFillData && this.startContainer.nodeType != 3) {
//使用|x固定住光标
var tmpText = this.document.createTextNode(fillChar),
tmp = this.document.createElement('span');
tmp.appendChild(this.document.createTextNode(fillChar));
start.parentNode.insertBefore(tmp, start);
start.parentNode.insertBefore(tmpText, start);
//当点b,i,u时,不能清除i上边的b
removeFillData(this.document, tmpText);
fillData = tmpText;
mergeSibling(tmp, 'previousSibling');
mergeSibling(start, 'nextSibling');
nativeRange.moveStart('character', -1);
nativeRange.collapse(true);
}
}
this.moveToBookmark(bookmark);
tmp && domUtils.remove(tmp);
//IE在隐藏状态下不支持range操作,catch一下
try {
nativeRange.select();
} catch (e) {
}
return this;
} : function (notInsertFillData) {
function checkOffset(rng){
function check(node,offset,dir){
if(node.nodeType == 3 && node.nodeValue.length < offset){
rng[dir + 'Offset'] = node.nodeValue.length
}
}
check(rng.startContainer,rng.startOffset,'start');
check(rng.endContainer,rng.endOffset,'end');
}
var win = domUtils.getWindow(this.document),
sel = win.getSelection(),
txtNode;
//FF下关闭自动长高时滚动条在关闭dialog时会跳
//ff下如果不body.focus将不能定位闭合光标到编辑器内
browser.gecko ? this.body.focus() : win.focus();
if (sel) {
sel.removeAllRanges();
// trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
// this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
if (this.collapsed && !notInsertFillData) {
// //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
// if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
// var tmp = this.document.createTextNode('');
// this.insertNode(tmp).setStart(tmp, 0).collapse(true);
// }
//
//处理光标落在文本节点的情况
//处理以下的情况
//|xxxx
//xxxx|xxxx
//xxxx|
var start = this.startContainer,child = start;
if(start.nodeType == 1){
child = start.childNodes[this.startOffset];
}
if( !(start.nodeType == 3 && this.startOffset) &&
(child ?
(!child.previousSibling || child.previousSibling.nodeType != 3)
:
(!start.lastChild || start.lastChild.nodeType != 3)
)
){
txtNode = this.document.createTextNode(fillChar);
//跟着前边走
this.insertNode(txtNode);
removeFillData(this.document, txtNode);
mergeSibling(txtNode, 'previousSibling');
mergeSibling(txtNode, 'nextSibling');
fillData = txtNode;
this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
}
}
var nativeRange = this.document.createRange();
if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){
var child = this.startContainer.childNodes[this.startOffset];
if(!child){
//往前靠拢
child = this.startContainer.lastChild;
if( child && domUtils.isBr(child)){
this.setStartBefore(child).collapse(true);
}
}else{
//向后靠拢
while(child && domUtils.isBlockElm(child)){
if(child.nodeType == 1 && child.childNodes[0]){
child = child.childNodes[0]
}else{
break;
}
}
child && this.setStartBefore(child).collapse(true)
}
}
//是createAddress最后一位算的不准,现在这里进行微调
checkOffset(this);
nativeRange.setStart(this.startContainer, this.startOffset);
nativeRange.setEnd(this.endContainer, this.endOffset);
sel.addRange(nativeRange);
}
return this;
},
createAddress : function(ignoreEnd,ignoreTxt){
var addr = {},me = this;
function getAddress(isStart){
var node = isStart ? me.startContainer : me.endContainer;
var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}),
addrs = [];
for(var i = 0,ci;ci = parents[i++];){
addrs.push(domUtils.getNodeIndex(ci,ignoreTxt));
}
var firstIndex = 0;
if(ignoreTxt){
if(node.nodeType == 3){
var tmpNode = node.previousSibling;
while(tmpNode && tmpNode.nodeType == 3){
firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length;
tmpNode = tmpNode.previousSibling;
}
firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
}else{
node = node.childNodes[ isStart ? me.startOffset : me.endOffset];
if(node){
firstIndex = domUtils.getNodeIndex(node,ignoreTxt);
}else{
node = isStart ? me.startContainer : me.endContainer;
var first = node.firstChild;
while(first){
if(domUtils.isFillChar(first)){
first = first.nextSibling;
continue;
}
firstIndex++;
if(first.nodeType == 3){
while( first && first.nodeType == 3){
first = first.nextSibling;
}
}else{
first = first.nextSibling;
}
}
}
}
}else{
firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset
}
if(firstIndex < 0){
firstIndex = 0;
}
addrs.push(firstIndex);
return addrs;
}
addr.startAddress = getAddress(true);
if(!ignoreEnd){
addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
}
return addr;
},
moveToAddress : function(addr,ignoreEnd){
var me = this;
function getNode(address,isStart){
var tmpNode = me.body,
parentNode,offset;
for(var i= 0,ci,l=address.length;i' + (browser.ie ? '' : '
') + '
' + (browser.ie ? ' ' : '
') + '
{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}
* @grammar editor.hasContents() => (true|false)
* @grammar editor.hasContents(tags) => (true|false) //若文档中包含tags数组里对应的tag,直接返回true
* @example
* editor.hasContents(['span']) //如果编辑器里有这些,不认为是空
*/
hasContents: function (tags) {
if (tags) {
for (var i = 0, ci; ci = tags[i++];) {
if (this.body.getElementsByTagName(ci).length > 0) {
return true;
}
}
}
if (!domUtils.isEmptyBlock(this.body)) {
return true
}
//随时添加,定义的特殊标签如果存在,不能认为是空
tags = ['div'];
for (i = 0; ci = tags[i++];) {
var nodes = domUtils.getElementsByTagName(this.body, ci);
for (var n = 0, cn; cn = nodes[n++];) {
if (domUtils.isCustomeNode(cn)) {
return true;
}
}
}
return false;
},
/**
* 重置编辑器,可用来做多个tab使用同一个编辑器实例
* @name reset
* @desc
* * 清空编辑器内容
* * 清空回退列表
* @grammar editor.reset()
*/
reset: function () {
this.fireEvent('reset');
},
isEnabled: function(){
return this._isEnabled != true;
},
setEnabled: function () {
var me = this, range;
me.body.contentEditable = true;
/* 恢复选区 */
if (me.lastBk) {
range = me.selection.getRange();
try {
range.moveToBookmark(me.lastBk);
delete me.lastBk
} catch (e) {
range.setStartAtFirst(me.body).collapse(true)
}
range.select(true);
}
/* 恢复query函数 */
if (me.bkqueryCommandState) {
me.queryCommandState = me.bkqueryCommandState;
delete me.bkqueryCommandState;
}
/* 恢复原生事件 */
if (me._bkproxyDomEvent) {
me._proxyDomEvent = me._bkproxyDomEvent;
delete me._bkproxyDomEvent;
}
/* 触发事件 */
me.fireEvent('setEnabled');
},
/**
* 设置当前编辑区域可以编辑
* @name enable
* @grammar editor.enable()
*/
enable: function () {
return this.setEnabled();
},
setDisabled: function (except, keepDomEvent) {
var me = this;
me.body.contentEditable = false;
me._except = except ? utils.isArray(except) ? except : [except] : [];
/* 备份最后的选区 */
if (!me.lastBk) {
me.lastBk = me.selection.getRange().createBookmark(true);
}
/* 备份并重置query函数 */
if(!me.bkqueryCommandState) {
me.bkqueryCommandState = me.queryCommandState;
me.queryCommandState = function (type) {
if (utils.indexOf(me._except, type) != -1) {
return me.bkqueryCommandState.apply(me, arguments);
}
return -1;
};
}
/* 备份并墙原生事件 */
if(!keepDomEvent && !me._bkproxyDomEvent) {
me._bkproxyDomEvent = me._proxyDomEvent;
me._proxyDomEvent = function () {
return false;
};
}
/* 触发事件 */
me.fireEvent('selectionchange');
me.fireEvent('setDisabled', me._except);
},
/** 设置当前编辑区域不可编辑,except中的命令除外
* @name disable
* @grammar editor.disable()
* @grammar editor.disable(except) //例外的命令,也即即使设置了disable,此处配置的命令仍然可以执行
* @example
* //禁用工具栏中除加粗和插入图片之外的所有功能
* editor.disable(['bold','insertimage']);//可以是单一的String,也可以是Array
*/
disable: function (except) {
return this.setDisabled(except);
},
/**
* 设置默认内容
* @ignore
* @private
* @param {String} cont 要存入的内容
*/
_setDefaultContent: function () {
function clear() {
var me = this;
if (me.document.getElementById('initContent')) {
me.body.innerHTML = '' + (ie ? '' : '
') + '
' + cont + '
'; me.addListener('firstBeforeExecCommand focus', clear); } }(), /** * show方法的兼容版本 * @private * @ignore */ setShow: function () { var me = this, range = me.selection.getRange(); if (me.container.style.display == 'none') { //有可能内容丢失了 try { range.moveToBookmark(me.lastBk); delete me.lastBk } catch (e) { range.setStartAtFirst(me.body).collapse(true) } //ie下focus实效,所以做了个延迟 setTimeout(function () { range.select(true); }, 100); me.container.style.display = ''; } }, /** * 显示编辑器 * @name show * @grammar editor.show() */ show: function () { return this.setShow(); }, /** * hide方法的兼容版本 * @private * @ignore */ setHide: function () { var me = this; if (!me.lastBk) { me.lastBk = me.selection.getRange().createBookmark(true); } me.container.style.display = 'none' }, /** * 隐藏编辑器 * @name hide * @grammar editor.hide() */ hide: function () { return this.setHide(); }, /** * 根据制定的路径,获取对应的语言资源 * @name getLang * @grammar editor.getLang(path) => (JSON|String) 路径根据的是lang目录下的语言文件的路径结构 * @example * editor.getLang('contextMenu.delete') //如果当前是中文,那返回是的是删除 */ getLang: function (path) { var lang = UM.I18N[this.options.lang]; if (!lang) { throw Error("not import language file"); } path = (path || "").split("."); for (var i = 0, ci; ci = path[i++];) { lang = lang[ci]; if (!lang)break; } return lang; }, /** * 计算编辑器当前内容的长度 * @name getContentLength * @grammar editor.getContentLength(ingoneHtml,tagNames) => * @example * editor.getLang(true) */ getContentLength: function (ingoneHtml, tagNames) { var count = this.getContent(false,false,true).length; if (ingoneHtml) { tagNames = (tagNames || []).concat([ 'hr', 'img', 'iframe']); count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length; for (var i = 0, ci; ci = tagNames[i++];) { count += this.body.getElementsByTagName(ci).length; } } return count; }, addInputRule: function (rule,ignoreUndo) { rule.ignoreUndo = ignoreUndo; this.inputRules.push(rule); }, filterInputRule: function (root,isUndoLoad) { for (var i = 0, ci; ci = this.inputRules[i++];) { if(isUndoLoad && ci.ignoreUndo){ continue; } ci.call(this, root) } }, addOutputRule: function (rule,ignoreUndo) { rule.ignoreUndo = ignoreUndo; this.outputRules.push(rule) }, filterOutputRule: function (root,isUndoLoad) { for (var i = 0, ci; ci = this.outputRules[i++];) { if(isUndoLoad && ci.ignoreUndo){ continue; } ci.call(this, root) } } }; utils.inherits(Editor, EventBase); })(); /** * @file * @name UM.filterWord * @short filterWord * @desc 用来过滤word粘贴过来的字符串 * @import editor.js,core/utils.js * @anthor zhanyi */ var filterWord = UM.filterWord = function () { //是否是word过来的内容 function isWordDocument( str ) { return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test( str ); } //去掉小数 function transUnit( v ) { v = v.replace( /[\d.]+\w+/g, function ( m ) { return utils.transUnitToPx(m); } ); return v; } function filterPasteWord( str ) { return str.replace(/[\t\r\n]+/g,' ') .replace( //ig, "" ) //转换图片 .replace(/]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "
$1
" ) //去掉多余的属性 .replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){ //保留list的标示 return name == 'class' && val == 'MsoListParagraph' ? str : '' }) //清除多余的font/span不能匹配 有可能是空格 .replace( /<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a,b,c){ return c.replace(/[\t\r\n ]+/g,' ') }) //处理style的问题 .replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) { var n = [], s = style.replace( /^\s+|\s+$/, '' ) .replace(/'/g,'\'') .replace( /"/gi, "'" ) .split( /;\s*/g ); for ( var i = 0,v; v = s[i];i++ ) { var name, value, parts = v.split( ":" ); if ( parts.length == 2 ) { name = parts[0].toLowerCase(); value = parts[1].toLowerCase(); if(/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g,'').length == 0 || /^(margin)\w*/.test(name) && /^0\w+$/.test(value) ){ continue; } 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": //ie下会出现挤到一起的情况 //case "mso-table-layout-alt": case "mso-height": case "mso-width": case "mso-vertical-align-alt": //trace:1819 ff下会解析出padding在table上 if(!/ 这样的标签了
//先去掉了,加上的原因忘了,这里先记录
var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
//ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
var allowEmptyTags = {
b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1,
sub:1,img:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1
};
htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '');
if(!ignoreBlank){
htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n'+(ignoreBlank?'':' ')+']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n'+(ignoreBlank?'':' ')+']*','g'), function(a,b){
//br暂时单独处理
if(b && allowEmptyTags[b.toLowerCase()]){
return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,'');
}
return a.replace(new RegExp('^[\\r\\n'+(ignoreBlank?'':' ')+']+'),'').replace(new RegExp('[\\r\\n'+(ignoreBlank?'':' ')+']+$'),'');
});
}
var notTransAttrs = {
'href':1,
'src':1
};
var uNode = UM.uNode,
needParentNode = {
'td':'tr',
'tr':['tbody','thead','tfoot'],
'tbody':'table',
'th':'tr',
'thead':'table',
'tfoot':'table',
'caption':'table',
'li':['ul', 'ol'],
'dt':'dl',
'dd':'dl',
'option':'select'
},
needChild = {
'ol':'li',
'ul':'li'
};
function text(parent, data) {
if(needChild[parent.tagName]){
var tmpNode = uNode.createElement(needChild[parent.tagName]);
parent.appendChild(tmpNode);
tmpNode.appendChild(uNode.createText(data));
parent = tmpNode;
}else{
parent.appendChild(uNode.createText(data));
}
}
function element(parent, tagName, htmlattr) {
var needParentTag;
if (needParentTag = needParentNode[tagName]) {
var tmpParent = parent,hasParent;
while(tmpParent.type != 'root'){
if(utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName){
parent = tmpParent;
hasParent = true;
break;
}
tmpParent = tmpParent.parentNode;
}
if(!hasParent){
parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
}
}
//按dtd处理嵌套
// if(parent.type != 'root' && !dtd[parent.tagName][tagName])
// parent = parent.parentNode;
var elm = new uNode({
parentNode:parent,
type:'element',
tagName:tagName.toLowerCase(),
//是自闭合的处理一下
children:dtd.$empty[tagName] ? null : []
});
//如果属性存在,处理属性
if (htmlattr) {
var attrs = {}, match;
while (match = re_attr.exec(htmlattr)) {
attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4])
}
elm.attrs = attrs;
}
parent.children.push(elm);
//如果是自闭合节点返回父亲节点
return dtd.$empty[tagName] ? parent : elm
}
function comment(parent, data) {
parent.children.push(new uNode({
type:'comment',
data:data,
parentNode:parent
}));
}
var match, currentIndex = 0, nextIndex = 0;
//设置根节点
var root = new uNode({
type:'root',
children:[]
});
var currentParent = root;
while (match = re_tag.exec(htmlstr)) {
currentIndex = match.index;
try{
if (currentIndex > nextIndex) {
//text node
text(currentParent, htmlstr.slice(nextIndex, currentIndex));
}
if (match[3]) {
if(dtd.$cdata[currentParent.tagName]){
text(currentParent, match[0]);
}else{
//start tag
currentParent = element(currentParent, match[3].toLowerCase(), match[4]);
}
} else if (match[1]) {
if(currentParent.type != 'root'){
if(dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]){
text(currentParent, match[0]);
}else{
var tmpParent = currentParent;
while(currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()){
currentParent = currentParent.parentNode;
if(currentParent.type == 'root'){
currentParent = tmpParent;
throw 'break'
}
}
//end tag
currentParent = currentParent.parentNode;
}
}
} else if (match[2]) {
//comment
comment(currentParent, match[2])
}
}catch(e){}
nextIndex = re_tag.lastIndex;
}
//如果结束是文本,就有可能丢掉,所以这里手动判断一下
//例如 if(range.endContainer && range.endContainer.nodeType == 1){ tmpNode = range.endContainer.childNodes[range.endOffset]; if(tmpNode && domUtils.isBr(tmpNode)){ range.setEndAfter(tmpNode); } } if(range.startOffset == 0){ tmpNode = range.startContainer; if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){ tmpNode = range.endContainer; if(range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){ me.body.innerHTML = ' '+(browser.ie ? '' : ' |<[p> ==> | var pre = child.previousSibling; domUtils.trimWhiteTextNode(pre); if(!pre.childNodes.length){ domUtils.remove(pre); } //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位 if(!browser.ie && (next = child.nextSibling) && domUtils.isBlockElm(next) && next.lastChild && !domUtils.isBr(next.lastChild)){ next.appendChild(me.document.createElement('br')); } hadBreak = 1; } } var next = child.nextSibling; if(!div.firstChild && next && domUtils.isBlockElm(next)){ range.setStart(next,0).collapse(true); break; } range.setEndAfter( child ).collapse(); } child = range.startContainer; if(nextNode && domUtils.isBr(nextNode)){ domUtils.remove(nextNode) } //用chrome可能有空白展位符 if(domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)){ if(nextNode = child.nextSibling){ domUtils.remove(child); if(nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]){ range.setStart(nextNode,0).collapse(true).shrinkBoundary() } }else{ try{ child.innerHTML = browser.ie ? domUtils.fillChar : ''; }catch(e){ range.setStartBefore(child); domUtils.remove(child) } } } //加上true因为在删除表情等时会删两次,第一次是删的fillData try{ if(browser.ie9below && range.startContainer.nodeType == 1 && !range.startContainer.childNodes[range.startOffset]){ var start = range.startContainer,pre = start.childNodes[range.startOffset-1]; if(pre && pre.nodeType == 1 && dtd.$empty[pre.tagName]){ var txt = this.document.createTextNode(domUtils.fillChar); range.insertNode(txt).setStart(txt,0).collapse(true); } } setTimeout(function(){ range.select(true); }) }catch(e){} setTimeout(function(){ range = me.selection.getRange(); range.scrollIntoView(); me.fireEvent('afterinserthtml'); },200); } }; ///import core ///import plugins\inserthtml.js ///commands 插入图片,操作图片的对齐方式 ///commandsName InsertImage,ImageNone,ImageLeft,ImageRight,ImageCenter ///commandsTitle 图片,默认,居左,居右,居中 ///commandsDialog dialogs\image /** * Created by . * User: zhanyi * for image */ UM.commands['insertimage'] = { execCommand:function (cmd, opt) { opt = utils.isArray(opt) ? opt : [opt]; if (!opt.length) { return; } var me = this; var html = [], str = '', ci; ci = opt[0]; if (opt.length == 1) { str = ''; if (ci['floatStyle'] == 'center') { str = ' ' + str + ' '; } html.push(str); } else { for (var i = 0; ci = opt[i++];) { str = ''; html.push(str); } } me.execCommand('insertHtml', html.join(''), true); } }; ///import core ///commands 段落格式,居左,居右,居中,两端对齐 ///commandsName JustifyLeft,JustifyCenter,JustifyRight,JustifyJustify ///commandsTitle 居左对齐,居中对齐,居右对齐,两端对齐 /** * @description 居左右中 * @name UM.execCommand * @param {String} cmdName justify执行对齐方式的命令 * @param {String} align 对齐方式:left居左,right居右,center居中,justify两端对齐 * @author zhanyi */ UM.plugins['justify']=function(){ var me = this; $.each('justifyleft justifyright justifycenter justifyfull'.split(' '),function(i,cmdName){ me.commands[cmdName] = { execCommand:function (cmdName) { return this.document.execCommand(cmdName); }, queryCommandValue: function (cmdName) { var val = this.document.queryCommandValue(cmdName); return val === true || val === 'true' ? cmdName.replace(/justify/,'') : ''; }, queryCommandState: function (cmdName) { return this.document.queryCommandState(cmdName) ? 1 : 0 } }; }) }; ///import core ///import plugins\removeformat.js ///commands 字体颜色,背景色,字号,字体,下划线,删除线 ///commandsName ForeColor,BackColor,FontSize,FontFamily,Underline,StrikeThrough ///commandsTitle 字体颜色,背景色,字号,字体,下划线,删除线 /** * @description 字体 * @name UM.execCommand * @param {String} cmdName 执行的功能名称 * @param {String} value 传入的值 */ UM.plugins['font'] = function () { var me = this, fonts = { 'forecolor': 'forecolor', 'backcolor': 'backcolor', 'fontsize': 'fontsize', 'fontfamily': 'fontname' }, cmdNameToStyle = { 'forecolor': 'color', 'backcolor': 'background-color', 'fontsize': 'font-size', 'fontfamily': 'font-family' }, cmdNameToAttr = { 'forecolor': 'color', 'fontsize': 'size', 'fontfamily': 'face' }; me.setOpt({ 'fontfamily': [ { name: 'songti', val: '宋体,SimSun'}, { name: 'yahei', val: '微软雅黑,Microsoft YaHei'}, { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'}, { name: 'heiti', val: '黑体, SimHei'}, { name: 'lishu', val: '隶书, SimLi'}, { name: 'andaleMono', val: 'andale mono'}, { name: 'arial', val: 'arial, helvetica,sans-serif'}, { name: 'arialBlack', val: 'arial black,avant garde'}, { name: 'comicSansMs', val: 'comic sans ms'}, { name: 'impact', val: 'impact,chicago'}, { name: 'timesNewRoman', val: 'times new roman'}, { name: 'sans-serif',val:'sans-serif'} ], 'fontsize': [10, 12, 16, 18,24, 32,48] }); me.addOutputRule(function (root) { utils.each(root.getNodesByTagName('font'), function (node) { if (node.tagName == 'font') { var cssStyle = []; for (var p in node.attrs) { switch (p) { case 'size': var val = node.attrs[p]; $.each({ '10':'1', '12':'2', '16':'3', '18':'4', '24':'5', '32':'6', '48':'7' },function(k,v){ if(v == val){ val = k; return false; } }); cssStyle.push('font-size:' + val + 'px'); break; case 'color': cssStyle.push('color:' + node.attrs[p]); break; case 'face': cssStyle.push('font-family:' + node.attrs[p]); break; case 'style': cssStyle.push(node.attrs[p]); } } node.attrs = { 'style': cssStyle.join(';') }; } node.tagName = 'span'; if(node.parentNode.tagName == 'span' && node.parentNode.children.length == 1){ $.each(node.attrs,function(k,v){ node.parentNode.attrs[k] = k == 'style' ? node.parentNode.attrs[k] + v : v; }) node.parentNode.removeChild(node,true); } }); }); for(var p in fonts){ (function (cmd) { me.commands[cmd] = { execCommand: function (cmdName,value) { if(value == 'transparent'){ return; } var rng = this.selection.getRange(); if(rng.collapsed){ var span = $('').css(cmdNameToStyle[cmdName],value)[0]; rng.insertNode(span).setStart(span,0).setCursor(); }else{ if(cmdName == 'fontsize'){ value = { '10':'1', '12':'2', '16':'3', '18':'4', '24':'5', '32':'6', '48':'7' }[(value+"").replace(/px/,'')] } this.document.execCommand(fonts[cmdName],false, value); if(browser.gecko){ $.each(this.$body.find('a'),function(i,a){ var parent = a.parentNode; if(parent.lastChild === parent.firstChild && /FONT|SPAN/.test(parent.tagName)){ var cloneNode = parent.cloneNode(false); cloneNode.innerHTML = a.innerHTML; $(a).html('').append(cloneNode).insertBefore(parent); $(parent).remove(); } }); } if(!browser.ie){ var nativeRange = this.selection.getNative().getRangeAt(0); var common = nativeRange.commonAncestorContainer; var rng = this.selection.getRange(), bk = rng.createBookmark(true); $(common).find('a').each(function(i,n){ var parent = n.parentNode; if(parent.nodeName == 'FONT'){ var font = parent.cloneNode(false); font.innerHTML = n.innerHTML; $(n).html('').append(font); } }); rng.moveToBookmark(bk).select() } return true } }, queryCommandValue: function (cmdName) { var start = me.selection.getStart(); var val = $(start).css(cmdNameToStyle[cmdName]); if(val === undefined){ val = $(start).attr(cmdNameToAttr[cmdName]) } return val ? utils.fixColor(cmdName,val).replace(/px/,'') : ''; }, queryCommandState: function (cmdName) { return this.queryCommandValue(cmdName) } }; })(p); } }; ///import core ///commands 超链接,取消链接 ///commandsName Link,Unlink ///commandsTitle 超链接,取消链接 ///commandsDialog dialogs\link /** * 超链接 * @function * @name UM.execCommand * @param {String} cmdName link插入超链接 * @param {Object} options url地址,title标题,target是否打开新页 * @author zhanyi */ /** * 取消链接 * @function * @name UM.execCommand * @param {String} cmdName unlink取消链接 * @author zhanyi */ UM.plugins['link'] = function(){ var me = this; me.setOpt('autourldetectinie',false); //在ie下禁用autolink if(browser.ie && this.options.autourldetectinie === false){ this.addListener('keyup',function(cmd,evt){ var me = this,keyCode = evt.keyCode; if(keyCode == 13 || keyCode == 32){ var rng = me.selection.getRange(); var start = rng.startContainer; if(keyCode == 13){ if(start.nodeName == 'P'){ var pre = start.previousSibling; if(pre && pre.nodeType == 1){ var pre = pre.lastChild; if(pre && pre.nodeName == 'A' && !pre.getAttribute('_href')){ domUtils.remove(pre,true); } } } }else if(keyCode == 32){ if(start.nodeType == 3 && /^\s$/.test(start.nodeValue)){ start = start.previousSibling; if(start && start.nodeName == 'A' && !start.getAttribute('_href')){ domUtils.remove(start,true); } } } } }); } this.addOutputRule(function(root){ $.each(root.getNodesByTagName('a'),function(i,a){ var _href = a.getAttr('_href'); if(!/^(ftp|https?|\/|file)/.test(_href)){ _href = 'http://' + _href; } a.setAttr('href', _href); a.setAttr('_href') if(a.getAttr('title')==''){ a.setAttr('title') } }) }); this.addInputRule(function(root){ $.each(root.getNodesByTagName('a'),function(i,a){ a.setAttr('_href', a.getAttr('href')); }) }); me.commands['link'] = { execCommand : function( cmdName, opt ) { var me = this; var rng = me.selection.getRange(); opt._href && (opt._href = utils.unhtml(opt._href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g)); opt.href && (opt.href = utils.unhtml(opt.href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g)); if(rng.collapsed){ var start = rng.startContainer; if(start = domUtils.findParentByTagName(start,'a',true)){ $(start).attr(opt); rng.selectNode(start).select() }else{ rng.insertNode($('' + opt.href +'').attr(opt)[0]).select() } }else{ me.document.execCommand('createlink',false,'_umeditor_link'); utils.each(domUtils.getElementsByTagName(me.body,'a',function(n){ return n.getAttribute('href') == '_umeditor_link' }),function(l){ if($(l).text() == '_umeditor_link'){ $(l).text(opt.href); } domUtils.setAttributes(l,opt); rng.selectNode(l).select() }) } }, queryCommandState:function(){ return this.queryCommandValue('link') ? 1 : 0; }, queryCommandValue:function(){ var path = this.selection.getStartElementPath(); var result; $.each(path,function(i,n){ if(n.nodeName == "A"){ result = n; return false; } }) return result; } }; me.commands['unlink'] = { execCommand : function() { this.document.execCommand('unlink'); } }; }; ///import core ///commands 打印 ///commandsName Print ///commandsTitle 打印 /** * @description 打印 * @name baidu.editor.execCommand * @param {String} cmdName print打印编辑器内容 * @author zhanyi */ UM.commands['print'] = { execCommand : function(){ var me = this, id = 'editor_print_' + +new Date(); $('').attr('id', id) .css({ width:'0px', height:'0px', 'overflow':'hidden', 'float':'left', 'position':'absolute', top:'-10000px', left:'-10000px' }) .appendTo(me.$container.find('.edui-dialog-container')); var w = window.open('', id, ''), d = w.document; d.open(); d.write(''+this.getContent(null,null,true)+' ');
d.close();
},
notNeedUndo : 1
};
///import core
///commands 格式
///commandsName Paragraph
///commandsTitle 段落格式
/**
* 段落样式
* @function
* @name UM.execCommand
* @param {String} cmdName paragraph插入段落执行命令
* @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
* @param {String} attrs 标签的属性
* @author zhanyi
*/
UM.plugins['paragraph'] = function() {
var me = this;
me.setOpt('paragraph',{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''});
me.commands['paragraph'] = {
execCommand : function( cmdName, style ) {
return this.document.execCommand('formatBlock',false,'<' + style + '>');
},
queryCommandValue : function() {
try{
var val = this.document.queryCommandValue('formatBlock')
}catch(e){
}
return val ;
}
};
};
///import core
///import plugins\inserthtml.js
///commands 分割线
///commandsName Horizontal
///commandsTitle 分隔线
/**
* 分割线
* @function
* @name UM.execCommand
* @param {String} cmdName horizontal插入分割线
*/
UM.plugins['horizontal'] = function(){
var me = this;
me.commands['horizontal'] = {
execCommand : function( ) {
this.document.execCommand('insertHorizontalRule');
var rng = me.selection.getRange().txtToElmBoundary(true),
start = rng.startContainer;
if(domUtils.isBody(rng.startContainer)){
var next = rng.startContainer.childNodes[rng.startOffset];
if(!next){
next = $('').appendTo(rng.startContainer).html(browser.ie ? ' ' : '')[0] } rng.setStart(next,0).setCursor() }else{ while(dtd.$inline[start.tagName] && start.lastChild === start.firstChild){ var parent = start.parentNode; parent.appendChild(start.firstChild); parent.removeChild(start); start = parent; } while(dtd.$inline[start.tagName]){ start = start.parentNode; } if(start.childNodes.length == 1 && start.lastChild.nodeName == 'HR'){ var hr = start.lastChild; $(hr).insertBefore(start); rng.setStart(start,0).setCursor(); }else{ hr = $('hr',start)[0]; domUtils.breakParent(hr,start); var pre = hr.previousSibling; if(pre && domUtils.isEmptyBlock(pre)){ $(pre).remove() } rng.setStart(hr.nextSibling,0).setCursor(); } } } }; }; ///import core ///commands 清空文档 ///commandsName ClearDoc ///commandsTitle 清空文档 /** * * 清空文档 * @function * @name UM.execCommand * @param {String} cmdName cleardoc清空文档 */ UM.commands['cleardoc'] = { execCommand : function() { var me = this, range = me.selection.getRange(); me.body.innerHTML = " "+(ie ? "" : " ' + (browser.ie ? '' : ' ' + (browser.ie ? '' : ' '+(browser.ie?'':' '); doSave = 1; } } else { //chrome remove div if (start.nodeType == 1) { var tmp = me.document.createTextNode(''),div; range.insertNode(tmp); div = domUtils.findParentByTagName(tmp, 'div', true); if (div) { var p = me.document.createElement('p'); while (div.firstChild) { p.appendChild(div.firstChild); } div.parentNode.insertBefore(p, div); domUtils.remove(div); range.setStartBefore(tmp).setCursor(); doSave = 1; } domUtils.remove(tmp); } } if (me.undoManger && doSave) { me.undoManger.save(); } } //没有站位符,会出现多行的问题 browser.opera && range.select(); }else{ me.fireEvent('saveScene',true,true) } } }); me.addListener('keydown', function(type, evt) { var keyCode = evt.keyCode || evt.which; if (keyCode == 13) {//回车 if(me.fireEvent('beforeenterkeydown')){ domUtils.preventDefault(evt); return; } me.fireEvent('saveScene',true,true); hTag = ''; var range = me.selection.getRange(); if (!range.collapsed) { //跨td不能删 var start = range.startContainer, end = range.endContainer, startTd = domUtils.findParentByTagName(start, 'td', true), endTd = domUtils.findParentByTagName(end, 'td', true); if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) { evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false); return; } } if (tag == 'p') { if (!browser.ie) { start = domUtils.findParentByTagName(range.startContainer, ['ol','ul','p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption'], true); //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command //trace:2431 if (!start && !browser.opera) { me.document.execCommand('formatBlock', false, ' '); if (browser.gecko) { range = me.selection.getRange(); start = domUtils.findParentByTagName(range.startContainer, 'p', true); start && domUtils.removeDirtyAttr(start); } } else { hTag = start.tagName; start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start); } } } } }); browser.ie && me.addListener('setDisabled',function(){ $(me.body).find('p').each(function(i,p){ if(domUtils.isEmptyBlock(p)){ p.innerHTML = ' ' } }) }) }; ///import core ///commands 预览 ///commandsName Preview ///commandsTitle 预览 /** * 预览 * @function * @name UM.execCommand * @param {String} cmdName preview预览编辑器内容 */ UM.commands['preview'] = { execCommand : function(){ var w = window.open('', '_blank', ''), d = w.document, c = this.getContent(null,null,true), path = this.getOpt('UMEDITOR_HOME_URL'), formula = c.indexOf('mathquill-embedded-latex')!=-1 ? '' + '' + '':''; d.open(); d.write(' ' + formula + ''+c+' ');
d.close();
},
notNeedUndo : 1
};
///import core
///commands 加粗,斜体,上标,下标
///commandsName Bold,Italic,Subscript,Superscript
///commandsTitle 加粗,加斜,下标,上标
/**
* b u i等基础功能实现
* @function
* @name UM.execCommands
* @param {String} cmdName bold加粗。italic斜体。subscript上标。superscript下标。
*/
UM.plugins['basestyle'] = function(){
var basestyles = ['bold','underline','superscript','subscript','italic','strikethrough'],
me = this;
//添加快捷键
me.addshortcutkey({
"Bold" : "ctrl+66",//^B
"Italic" : "ctrl+73", //^I
"Underline" : "ctrl+shift+85",//^U
"strikeThrough" : 'ctrl+shift+83' //^s
});
//过滤最后的产出数据
me.addOutputRule(function(root){
$.each(root.getNodesByTagName('b i u strike s'),function(i,node){
switch (node.tagName){
case 'b':
node.tagName = 'strong';
break;
case 'i':
node.tagName = 'em';
break;
case 'u':
node.tagName = 'span';
node.setStyle('text-decoration','underline');
break;
case 's':
case 'strike':
node.tagName = 'span';
node.setStyle('text-decoration','line-through')
}
});
});
$.each(basestyles,function(i,cmd){
me.commands[cmd] = {
execCommand : function( cmdName ) {
var rng = this.selection.getRange();
if(rng.collapsed && this.queryCommandState(cmdName) != 1){
var node = this.document.createElement({
'bold':'strong',
'underline':'u',
'superscript':'sup',
'subscript':'sub',
'italic':'em',
'strikethrough':'strike'
}[cmdName]);
rng.insertNode(node).setStart(node,0).setCursor(false);
return true;
}else{
return this.document.execCommand(cmdName)
}
},
queryCommandState : function(cmdName) {
if(browser.gecko){
return this.document.queryCommandState(cmdName)
}
var path = this.selection.getStartElementPath(),result = false;
$.each(path,function(i,n){
switch (cmdName){
case 'bold':
if(n.nodeName == 'STRONG' || n.nodeName == 'B'){
result = 1;
return false;
}
break;
case 'underline':
if(n.nodeName == 'U' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'underline'){
result = 1;
return false;
}
break;
case 'superscript':
if(n.nodeName == 'SUP'){
result = 1;
return false;
}
break;
case 'subscript':
if(n.nodeName == 'SUB'){
result = 1;
return false;
}
break;
case 'italic':
if(n.nodeName == 'EM' || n.nodeName == 'I'){
result = 1;
return false;
}
break;
case 'strikethrough':
if(n.nodeName == 'S' || n.nodeName == 'STRIKE' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'line-through'){
result = 1;
return false;
}
break;
}
})
return result
}
};
})
};
///import core
///import plugins/inserthtml.js
///commands 视频
///commandsName InsertVideo
///commandsTitle 插入视频
///commandsDialog dialogs\video
UM.plugins['video'] = function (){
var me =this,
div;
/**
* 创建插入视频字符窜
* @param url 视频地址
* @param width 视频宽度
* @param height 视频高度
* @param align 视频对齐
* @param toEmbed 是否以flash代替显示
* @param addParagraph 是否需要添加P 标签
*/
function creatInsertStr(url,width,height,id,align,toEmbed){
return !toEmbed ?
''
:
' |