(function(){UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};
var baidu = window.baidu || {};
window.baidu = baidu;
window.UE = baidu.editor = {};
UE.plugins = {};
UE.commands = {};
UE.instants = {};
UE.I18N = {};
//UE.defaultplugins = {};
//
//UE.commands = function(){
// var commandList = {},tmpList= {};
// return {
//
// register : function(commandsName,pluginName){
// commandsName = commandsName.split(',');
// for(var i= 0,ci;ci=commandsName[i++];){
// commandList[ci] = pluginName;
// }
//
// },
// get : function(commandName){
// return commandList[commandName];
// },
// getList : function(){
// return commandList;
// }
// }
//}();
UE.version = "1.2.3.0";
var dom = UE.dom = {};
///import editor.js
/**
* @class baidu.editor.browser 判断浏览器
*/
var browser = UE.browser = function(){
var agent = navigator.userAgent.toLowerCase(),
opera = window.opera,
browser = {
/**
* 检测浏览器是否为IE
* @name baidu.editor.browser.ie
* @property 检测浏览器是否为IE
* @grammar baidu.editor.browser.ie
* @return {Boolean} 返回是否为ie浏览器
*/
ie : !!window.ActiveXObject,
/**
* 检测浏览器是否为Opera
* @name baidu.editor.browser.opera
* @property 检测浏览器是否为Opera
* @grammar baidu.editor.browser.opera
* @return {Boolean} 返回是否为opera浏览器
*/
opera : ( !!opera && opera.version ),
/**
* 检测浏览器是否为WebKit内核
* @name baidu.editor.browser.webkit
* @property 检测浏览器是否为WebKit内核
* @grammar baidu.editor.browser.webkit
* @return {Boolean} 返回是否为WebKit内核
*/
webkit : ( agent.indexOf( ' applewebkit/' ) > -1 ),
/**
* 检查是否为Macintosh系统
* @name baidu.editor.browser.mac
* @property 检查是否为Macintosh系统
* @grammar baidu.editor.browser.mac
* @return {Boolean} 返回是否为Macintosh系统
*/
mac : ( agent.indexOf( 'macintosh' ) > -1 ),
/**
* 检查浏览器是否为quirks模式
* @name baidu.editor.browser.quirks
* @property 检查浏览器是否为quirks模式
* @grammar baidu.editor.browser.quirks
* @return {Boolean} 返回是否为quirks模式
*/
quirks : ( document.compatMode == 'BackCompat' )
};
/**
* 检测浏览器是否为Gecko内核,如Firefox
* @name baidu.editor.browser.gecko
* @property 检测浏览器是否为Gecko内核
* @grammar baidu.editor.browser.gecko
* @return {Boolean} 返回是否为Gecko内核
*/
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 模式
*/
browser.ie9Compat = document.documentMode == 9;
/**
* 检测浏览器是否为 IE8 浏览器
* @name baidu.editor.browser.IE8
* @property 检测浏览器是否为 IE8 浏览器
* @grammar baidu.editor.browser.IE8
* @return {Boolean} 返回是否为 IE8 浏览器
*/
browser.ie8 = !!document.documentMode;
/**
* 检测浏览器是否为 IE8 模式
* @name baidu.editor.browser.ie8Compat
* @property 检测浏览器是否为 IE8 模式
* @grammar baidu.editor.browser.ie8Compat
* @return {Boolean} 返回是否为 IE8 模式
*/
browser.ie8Compat = document.documentMode == 8;
/**
* 检测浏览器是否运行在 兼容IE7模式
* @name baidu.editor.browser.ie7Compat
* @property 检测浏览器是否为兼容IE7模式
* @grammar baidu.editor.browser.ie7Compat
* @return {Boolean} 返回是否为兼容IE7模式
*/
browser.ie7Compat = ( ( version == 7 && !document.documentMode )
|| document.documentMode == 7 );
/**
* 检测浏览器是否IE6模式或怪异模式
* @name baidu.editor.browser.ie6Compat
* @property 检测浏览器是否IE6 模式或怪异模式
* @grammar baidu.editor.browser.ie6Compat
* @return {Boolean} 返回是否为IE6 模式或怪异模式
*/
browser.ie6Compat = ( version < 7 || browser.quirks );
}
// 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;
}
}
/**
* 检测浏览器是否为chrome
* @name baidu.editor.browser.chrome
* @property 检测浏览器是否为chrome
* @grammar baidu.editor.browser.chrome
* @return {Boolean} 返回是否为chrome浏览器
*/
if (/chrome\/(\d+\.\d)/i.test(agent)) {
browser.chrome = + RegExp['\x241'];
}
/**
* 检测浏览器是否为safari
* @name baidu.editor.browser.safari
* @property 检测浏览器是否为safari
* @grammar baidu.editor.browser.safari
* @return {Boolean} 返回是否为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] );
/**
* 浏览器版本
*
* gecko内核浏览器的版本会转换成这样(如 1.9.0.2 -> 10900).
*
* webkit内核浏览器版本号使用其build号 (如 522).
* @name baidu.editor.browser.version
* @grammar baidu.editor.browser.version
* @return {Boolean} 返回浏览器版本号
* @example
* if ( baidu.editor.browser.ie && baidu.editor.browser.version <= 6 )
* alert( "Ouch!" );
*/
browser.version = version;
/**
* 是否是兼容模式的浏览器
* @name baidu.editor.browser.isCompatible
* @grammar baidu.editor.browser.isCompatible
* @return {Boolean} 返回是否是兼容模式的浏览器
* @example
* if ( baidu.editor.browser.isCompatible )
* alert( "Your browser is pretty cool!" );
*/
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 ) ||
false );
return browser;
}();
//快捷方式
var ie = browser.ie,
webkit = browser.webkit,
gecko = browser.gecko,
opera = browser.opera;
///import editor.js
///import core/utils.js
/**
* @class baidu.editor.utils 工具类
*/
var utils = UE.utils =
/**@lends baidu.editor.utils.prototype*/
{
/**
* 以obj为原型创建实例
* @public
* @function
* @param {Object} obj
* @return {Object} 返回新的对象
*/
makeInstance: function(obj) {
var noop = new Function();
noop.prototype = obj;
obj = new noop;
noop.prototype = null;
return obj;
},
/**
* 将s对象中的属性扩展到t对象上
* @public
* @function
* @param {Object} t
* @param {Object} s
* @param {Boolean} b 是否保留已有属性
* @returns {Object} t 返回扩展了s对象属性的t
*/
extend: function(t, s, b) {
if (s) {
for (var k in s) {
if (!b || !t.hasOwnProperty(k)) {
t[k] = s[k];
}
}
}
return t;
},
/**
* 判断是否为数组
* @public
* @function
* @param {Object} array
* @return {Boolean} true:为数组,false:不为数组
*/
isArray: function(array) {
return Object.prototype.toString.apply(array) === '[object Array]';
},
/**
* 判断是否为字符串
* @public
* @function
* @param {Object} str
* @return {Boolean} true:为字符串。 false:不为字符串
*/
isString: function(str) {
return typeof str == 'string' || str.constructor == String;
},
/**
* subClass继承superClass
* @public
* @function
* @param {Object} subClass 子类
* @param {Object} superClass 超类
* @return {Object} 扩展后的新对象
*/
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);
},
/**
* 为对象绑定函数
* @public
* @function
* @param {Function} fn 函数
* @param {Object} this_ 对象
* @return {Function} 绑定后的函数
*/
bind: function(fn, this_) {
return function() {
return fn.apply(this_, arguments);
};
},
/**
* 创建延迟执行的函数
* @public
* @function
* @param {Function} fn 要执行的函数
* @param {Number} delay 延迟时间,单位为毫秒
* @param {Boolean} exclusion 是否互斥执行,true则执行下一次defer时会先把前一次的延迟函数删除
* @return {Function} 延迟执行的函数
*/
defer: function(fn, delay, exclusion) {
var timerID;
return function() {
if (exclusion) {
clearTimeout(timerID);
}
timerID = setTimeout(fn, delay);
};
},
/**
* 查找元素在数组中的索引, 若找不到返回-1
* @public
* @function
* @param {Array} array 要查找的数组
* @param {*} item 查找的元素
* @param {Number} at 开始查找的位置
* @returns {Number} 返回在数组中的索引
*/
indexOf: function(array, item, at) {
for(var i=at||0,l = array.length;i]/g, function(m){
return {
'<': '<',
'&': '&',
'"': '"',
'>': '>'
}[m]
}) : '';
},
html: function(str) {
return str ? str.replace(/&((g|l|quo)t|amp);/g, function(m){
return {
'<':'<',
'&':'&',
'"':'"',
'>': '>'
}[m]
}) : '';
},
/**
* 将css样式转换为驼峰的形式。如font-size -> fontSize
* @public
* @function
* @param {String} cssName 需要转换的样式
* @returns {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();}));
};
}(),
/**
* 加载css文件,执行回调函数
* @public
* @function
* @param {document} doc document对象
* @param {String} path 文件路径
* @param {Function} fun 回调函数
* @param {String} id 元素id
*/
loadFile : function(){
var tmpList = {};
return function(doc,obj,fun){
var item = tmpList[obj.src||obj.href];
if(item){
if(utils.isArray(item.funs)){
item.ready?fun():tmpList[obj.src||obj.href].funs.push(fun);
}
return;
}
tmpList[obj.src||obj.href] = fun? {'funs' : [fun]} :1;
if(!doc.body){
doc.write('');
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 = tmpList[obj.src||obj.href];
if(item.funs){
item.ready = 1;
for(var fi;fi=item.funs.pop();){
fi();
}
}
element.onload = element.onreadystatechange = null;
}
};
doc.getElementsByTagName("head")[0].appendChild(element);
}
}(),
/**
* 判断对象是否为空
* @param {Object} obj
* @return {Boolean} true 空,false 不空
*/
isEmptyObject : function(obj){
for ( var p in obj ) {
return false;
}
return true;
},
isFunction : function (source) {
// chrome下,'function' == typeof /a/ 为true.
return '[object Function]' == Object.prototype.toString.call(source);
},
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;
},
/**
* 只针对border,padding,margin做了处理,因为性能问题
* @public
* @function
* @param {String} val style字符串
*/
optCss : function(val){
var padding,margin,border;
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] = val;
return '';
case 'margin':
!margin && (margin = {});
margin[name] = val;
return '';
case 'border':
return val == 'initial' ? '' : str;
}
}
return str;
});
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]+';';
}
}else{
val += ';'+name+':' +
(t == b && b == l && l == r ? t :
t == b && l == r ? (t + ' ' + l) :
l == r ? (t + ' ' + l + ' ' + b) : (t + ' ' + r + ' ' + b + ' ' + l))+';'
}
return val;
}
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 + ";;" : ';'
});
},
/**
* DOMContentLoaded 事件注册
* @public
* @function
* @param {Function} 触发的事件
*/
domReady : function (){
var isReady = false,
fnArr = [];
function doReady(){
//确保onready只执行一次
isReady = true;
for(var ci;ci=fnArr.pop();){
ci();
}
}
return function(onready){
if ( document.readyState === "complete" ) {
return onready && setTimeout( onready, 1 );
}
onready && fnArr.push(onready);
isReady && doReady();
if( browser.ie ){
(function(){
if ( isReady ) return;
try {
document.documentElement.doScroll("left");
} catch( error ) {
setTimeout( arguments.callee, 0 );
return;
}
doReady();
})();
window.attachEvent('onload',doReady);
}else{
document.addEventListener( "DOMContentLoaded", function(){
document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
doReady();
}, false );
window.addEventListener('load',doReady,false);
}
}
}()
};
utils.domReady();
///import editor.js
///import core/utils.js
/**
* 事件基础类
* @public
* @class
* @name baidu.editor.EventBase
*/
var EventBase = UE.EventBase = function(){};
EventBase.prototype = /**@lends baidu.editor.EventBase.prototype*/{
/**
* 注册事件监听器
* @public
* @function
* @param {String} types 事件名
* @param {Function} listener 监听器数组
*/
addListener : function ( types, listener ) {
types = utils.trim(types).split(' ');
for(var i= 0,ti;ti=types[i++];){
getListener( this, ti, true ).push( listener );
}
},
/**
* 移除事件监听器
* @public
* @function
* @param {String} types 事件名
* @param {Function} listener 监听器数组
*/
removeListener : function ( types, listener ) {
types = utils.trim(types).split(' ');
for(var i= 0,ti;ti=types[i++];){
utils.removeItem( getListener( this, ti ) || [], listener );
}
},
/**
* 触发事件
* @public
* @function
* @param {String} type 事件名
*
*/
fireEvent : function ( types ) {
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 -- ) {
t = listeners[k].apply( this, arguments );
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
/**
* dtd html语义化的体现类
* @constructor
* @namespace dtd
*/
var dtd = dom.dtd = (function() {
function _( s ) {
for (var k in s) {
s[k.toUpperCase()] = s[k];
}
return s;
}
function X( t ) {
var a = arguments;
for ( var i=1; ixxxxbb --> xxxxbb
*/
clearEmptySibling:function ( node, ingoreNext, ingorePre ) {
function clear( next, dir ) {
var tmpNode;
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 = tmpNode;
}
}
!ingoreNext && clear( node.nextSibling, 'nextSibling' );
!ingorePre && clear( node.previousSibling, 'previousSibling' );
},
//---------------------------Text----------------------------------
/**
* 将一个文本节点拆分成两个文本节点
* @param {TextNode} node 文本节点
* @param {Integer} offset 拆分的位置
* @return {TextNode} 拆分后的后一个文本节
*/
split:function ( node, offset ) {
var doc = node.ownerDocument;
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 );
}
return retval;
},
/**
* 判断是否为空白节点
* @param {TextNode} node 节点
* @return {Boolean} 返回是否为文本节点
*/
isWhitespace:function ( node ) {
return !new RegExp( '[^ \t\n\r' + domUtils.fillChar + ']' ).test( node.nodeValue );
},
//------------------------------Element-------------------------------------------
/**
* 获取元素相对于viewport的像素坐标
* @param {Element} element 元素
* @returns {Object} 返回坐标对象{x:left,y:top}
*/
getXY:function ( element ) {
var x = 0, y = 0;
while ( element.offsetParent ) {
y += element.offsetTop;
x += element.offsetLeft;
element = element.offsetParent;
}
return {
'x':x,
'y':y
};
},
/**
* 绑原生DOM事件
* @param {Element|Window|Document} target 元素
* @param {Array|String} type 事件类型
* @param {Function} handler 执行函数
*/
on:function ( obj, type, handler ) {
var types = utils.isArray(type) ? type : [type],
k = types.length;
if ( k ) while ( k-- ) {
type = types[k];
if ( obj.addEventListener ) {
obj.addEventListener( type, handler, false );
} else {
if ( !handler._d ) {
handler._d = {};
}
var key = type + handler.toString();
if ( !handler._d[key] ) {
handler._d[key] = function ( evt ) {
return handler.call( evt.srcElement, evt || window.event );
};
obj.attachEvent( 'on' + type, handler._d[key] );
}
}
}
obj = null;
},
/**
* 解除原生DOM事件绑定
* @param {Element|Window|Document} obj 元素
* @param {Array|String} type 事件类型
* @param {Function} handler 执行函数
*/
un:function ( obj, type, handler ) {
var types = utils.isArray(type) ? type : [type],
k = types.length;
if ( k ) while ( k-- ) {
type = types[k];
if ( obj.removeEventListener ) {
obj.removeEventListener( type, handler, false );
} else {
var key = type + handler.toString();
obj.detachEvent( 'on' + type, handler._d ? handler._d[key] : handler );
if ( handler._d && handler._d[key] ) {
delete handler._d[key];
}
}
}
},
/**
* 比较两个节点是否tagName相同且有相同的属性和属性值
* @param {Element} nodeA 节点A
* @param {Element} nodeB 节点B
* @return {Boolean} 返回两个节点的标签,属性和属性值是否相同
* @example
* <span style="font-size:12px">ssss</span>和<span style="font-size:12px">bbbbb</span> 相等
* <span style="font-size:13px">ssss</span>和<span style="font-size:12px">bbbbb</span> 不相等
*/
isSameElement:function ( nodeA, nodeB ) {
if ( nodeA.tagName != nodeB.tagName ) {
return 0;
}
var thisAttribs = nodeA.attributes,
otherAttribs = nodeB.attributes;
if ( !ie && thisAttribs.length != otherAttribs.length ) {
return 0;
}
var attrA, attrB, al = 0, bl = 0;
for ( var i = 0; attrA = thisAttribs[i++]; ) {
if ( attrA.nodeName == 'style' ) {
if ( attrA.specified ) {
al++;
}
if ( domUtils.isSameStyle( nodeA, nodeB ) ) {
continue;
} else {
return 0;
}
}
if ( ie ) {
if ( attrA.specified ) {
al++;
attrB = otherAttribs.getNamedItem( attrA.nodeName );
} else {
continue;
}
} else {
attrB = nodeB.attributes[attrA.nodeName];
}
if ( !attrB.specified || attrA.nodeValue != attrB.nodeValue ) {
return 0;
}
}
// 有可能attrB的属性包含了attrA的属性之外还有自己的属性
if ( ie ) {
for ( i = 0; attrB = otherAttribs[i++]; ) {
if ( attrB.specified ) {
bl++;
}
}
if ( al != bl ) {
return 0;
}
}
return 1;
},
/**
* 判断两个元素的style属性是不是一致
* @param {Element} elementA 元素A
* @param {Element} elementB 元素B
* @return {boolean} 返回判断结果,true为一致
*/
isSameStyle:function ( elementA, elementB ) {
var styleA = elementA.style.cssText.replace( /( ?; ?)/g, ';' ).replace( /( ?: ?)/g, ':' ),
styleB = elementB.style.cssText.replace( /( ?; ?)/g, ';' ).replace( /( ?: ?)/g, ':' );
if ( browser.opera ) {
styleA = elementA.style;
styleB = elementB.style;
if ( styleA.length != styleB.length )
return 0;
for ( var p in styleA ) {
if ( /^(\d+|csstext)$/i.test( p ) ) {
continue;
}
if ( styleA[p] != styleB[p] ) {
return 0;
}
}
return 1;
}
if ( !styleA || !styleB ) {
return styleA == styleB ? 1 : 0;
}
styleA = styleA.split( ';' );
styleB = styleB.split( ';' );
if ( styleA.length != styleB.length ) {
return 0;
}
for ( var i = 0, ci; ci = styleA[i++]; ) {
if ( utils.indexOf( styleB, ci ) == -1 ) {
return 0;
}
}
return 1;
},
/**
* 检查是否为块元素
* @function
* @param {Element} node 元素
* @param {String} customNodeNames 自定义的块元素的tagName
* @return {Boolean} 是否为块元素
*/
isBlockElm:function ( node ) {
return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle( node, 'display' )]) && !dtd.$nonChild[node.tagName];
},
/**
* 判断是否body
* @param {Node} 节点
* @return {Boolean} 是否是body节点
*/
isBody:function ( node ) {
return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
},
/**
* 以node节点为中心,将该节点的父节点拆分成2块
* @param {Element} node 节点
* @param {Element} parent 要被拆分的父节点
* @example xxxxxxxxxx
==> xxx
xxxxx
*/
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;
},
/**
* 检查是否是空inline节点
* @param {Node} node 节点
* @return {Boolean} 返回1为是,0为否
* @example
* <b><i></i></b> //true
* true
* <b></b> true <b>xx<i></i></b> //false
*/
isEmptyInlineElement:function ( node ) {
if ( node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ] ) {
return 0;
}
node = node.firstChild;
while ( node ) {
//如果是创建的bookmark就跳过
if ( domUtils.isBookmarkNode( node ) ) {
return 0;
}
if ( node.nodeType == 1 && !domUtils.isEmptyInlineElement( node ) ||
node.nodeType == 3 && !domUtils.isWhitespace( node )
) {
return 0;
}
node = node.nextSibling;
}
return 1;
},
/**
* 删除空白子节点
* @param {Element} 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' );
},
/**
* 合并子节点
* @param {Node} node 节点
* @param {String} tagName 标签
* @param {String} attrs 属性
* @example <span style="font-size:12px;">xx<span style="font-size:12px;">aa</span>xx</span 使用后
* <span style="font-size:12px;">xxaaxx</span
*/
mergChild:function ( node, tagName, attrs ) {
var list = domUtils.getElementsByTagName( node, node.tagName.toLowerCase() );
for ( var i = 0, ci; ci = list[i++]; ) {
if ( !ci.parentNode || domUtils.isBookmarkNode( ci ) ) {
continue;
}
//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.cssText;
domUtils.remove( ci, true );
continue;
}
}
ci.style.cssText = node.style.cssText + ';' + ci.style.cssText;
if ( attrs ) {
var style = attrs.style;
if ( style ) {
style = style.split( ';' );
for ( var j = 0, s; s = style[j++]; ) {
ci.style[utils.cssStyleToDomStyle( s.split( ':' )[0] )] = s.split( ':' )[1];
}
}
}
if ( domUtils.isSameStyle( ci, node ) ) {
domUtils.remove( ci, true );
}
continue;
}
if ( domUtils.isSameElement( node, ci ) ) {
domUtils.remove( ci, true );
}
}
if ( tagName == 'span' ) {
var as = domUtils.getElementsByTagName( node, 'a' );
for ( var i = 0, ai; ai = as[i++]; ) {
ai.style.cssText = ';' + node.style.cssText;
ai.style.textDecoration = 'underline';
}
}
},
/**
* 封装原生的getElemensByTagName
* @param {Node} node 根节点
* @param {String} name 标签的tagName
* @return {Array} 返回符合条件的元素数组
*/
getElementsByTagName:function ( node, name ) {
var list = node.getElementsByTagName( name ), arr = [];
for ( var i = 0, ci; ci = list[i++]; ) {
arr.push( ci )
}
return arr;
},
/**
* 将子节点合并到父节点上
* @param {Element} node 节点
* @example <span style="color:#ff"><span style="font-size:12px">xxx</span></span> ==> <span style="color:#ff;font-size:12px">xxx</span>
*/
mergToParent:function ( node ) {
var parent = node.parentNode;
while ( parent && dtd.$removeEmpty[parent.tagName] ) {
if ( parent.tagName == node.tagName || parent.tagName == 'A' ) {//针对a标签单独处理
domUtils.trimWhiteTextNode( parent );
//span需要特殊处理 不处理这样的情况 xxxxxxxxx
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.cssText;
parent = parent.parentNode;
continue;
} else {
parent.style.cssText += ";" + node.style.cssText;
//trace:952 a标签要保持下划线
if ( parent.tagName == 'A' ) {
parent.style.textDecoration = 'underline';
}
}
}
if ( parent.tagName != 'A' ) {
parent === node.parentNode && domUtils.remove( node, true );
break;
}
}
parent = parent.parentNode;
}
},
/**
* 合并左右兄弟节点
* @function
* @param {Node} node
* @param {Boolean} ingoreNext 默认为false合并上一个兄弟节点。true为不合并上一个兄弟节点
* @param {Boolean} ingorePre 默认为false合并下一个兄弟节点。true为不合并下一个兄弟节点
* @example <b>xxxx</b><b>xxx</b><b>xxxx</b> ==> <b>xxxxxxxxxxx</b>
*/
mergSibling:function ( node, ingorePre, ingoreNext ) {
function merg( rtl, start, node ) {
var next;
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 );
} else {
node.appendChild( next.firstChild );
}
}
domUtils.remove( next );
}
}
!ingorePre && merg( 'previousSibling', 'firstChild', node );
!ingoreNext && merg( 'nextSibling', 'lastChild', node );
},
/**
* 使得元素及其子节点不能被选择
* @function
* @param {Node} node 节点
*/
unselectable:ie || browser.opera ? function ( node ) {
//for ie9
node.onselectstart = function () {
return false;
};
node.onclick = node.onkeyup = node.onkeydown = function () {
return false;
};
node.unselectable = 'on';
node.setAttribute( "unselectable", "on" );
for ( var i = 0, ci; ci = node.all[i++]; ) {
switch ( ci.tagName.toLowerCase() ) {
case 'iframe' :
case 'textarea' :
case 'input' :
case 'select' :
break;
default :
ci.unselectable = 'on';
node.setAttribute( "unselectable", "on" );
}
}
} : function ( node ) {
node.style.MozUserSelect =
node.style.webkitUserSelect =
node.style.KhtmlUserSelect = 'none';
},
/**
* 删除元素上的属性,可以删除多个
* @function
* @param {Element} element 元素
* @param {Array} attrNames 要删除的属性数组
*/
removeAttributes:function ( elm, attrNames ) {
for ( var i = 0, ci; ci = attrNames[i++]; ) {
ci = attrFix[ci] || ci;
switch ( ci ) {
case 'className':
elm[ci] = '';
break;
case 'style':
elm.style.cssText = '';
!browser.ie && elm.removeAttributeNode( elm.getAttributeNode( 'style' ) )
}
elm.removeAttribute( ci );
}
},
creElm:function ( doc, tag, attrs ) {
return this.setAttributes( doc.createElement( tag ), attrs )
},
/**
* 给节点添加属性
* @function
* @param {Node} node 节点
* @param {Object} attrNames 要添加的属性名称,采用json对象存放
*/
setAttributes:function ( node, attrs ) {
for ( var name in attrs ) {
var value = attrs[name];
switch ( name ) {
case 'class':
//ie下要这样赋值,setAttribute不起作用
node.className = value;
break;
case 'style' :
node.style.cssText = node.style.cssText + ";" + value;
break;
case 'innerHTML':
node[name] = value;
break;
case 'value':
node.value = value;
break;
default:
node.setAttribute( attrFix[name] || name, value );
}
}
return node;
},
/**
* 获取元素的样式
* @function
* @param {Element} element 元素
* @param {String} styleName 样式名称
* @return {String} 样式值
*/
getComputedStyle:function ( element, styleName ) {
function fixUnit( key, val ) {
if ( key == 'font-size' && /pt$/.test( val ) ) {
val = Math.round( parseFloat( val ) / 0.75 ) + 'px';
}
return val;
}
if ( element.nodeType == 3 ) {
element = element.parentNode;
}
//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:simsun;';
span.innerHTML = '.';
element.appendChild( span );
var result = span.offsetHeight;
element.removeChild( span );
span = null;
return result + 'px';
}
try {
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 null;
}
return fixUnit( styleName, utils.fixColor( styleName, value ) );
},
/**
* 删除cssClass,可以支持删除多个class
* @param {Element} element 元素
* @param {Array} classNames 删除的className
*/
removeClasses:function ( element, classNames ) {
classNames = utils.isArray( classNames ) ? classNames : [classNames];
element.className = (' ' + element.className + ' ').replace(
new RegExp( '(?:\\s+(?:' + classNames.join( '|' ) + '))+\\s+', 'g' ), ' ' );
},
/**
* 增加一个class
* @param element
* @param className
*/
addClass:function ( element, className ) {
if ( !this.hasClass( element, className ) ) {
element.className += " " + className;
}
},
/**
* 删除元素的样式
* @param {Element} element元素
* @param {String} name 删除的样式名称
*/
removeStyle:function ( node, name ) {
node.style[utils.cssStyleToDomStyle( name )] = '';
if ( !node.style.cssText ) {
domUtils.removeAttributes( node, ['style'] );
}
},
/**
* 判断元素属性中是否包含某一个classname
* @param {Element} element 元素
* @param {String} className 样式名
* @returns {Boolean} 是否包含该classname
*/
hasClass:function ( element, className ) {
return ( ' ' + element.className + ' ' ).indexOf( ' ' + className + ' ' ) > -1;
},
/**
* 阻止事件默认行为
* @param {Event} evt 需要组织的事件对象
*/
preventDefault:function ( evt ) {
evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
},
/**
* 获得元素样式
* @param {Element} element 元素
* @param {String} name 样式名称
* @return {String} 返回元素样式值
*/
getStyle:function ( element, name ) {
var value = element.style[ utils.cssStyleToDomStyle( name ) ];
return utils.fixColor( name, value );
},
setStyle:function ( element, name, value ) {
element.style[utils.cssStyleToDomStyle( name )] = value;
},
setStyles:function ( element, styles ) {
for ( var name in styles ) {
if ( styles.hasOwnProperty( name ) ) {
domUtils.setStyle( element, name, styles[name] );
}
}
},
/**
* 删除_moz_dirty属性
* @function
* @param {Node} node 节点
*/
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} node 父节点
* @param {Function} fn 过滤子节点的规则,若为空,则得到所有子节点的数量
* @return {Number} 符合条件子节点的数量
*/
getChildCount:function ( node, fn ) {
var count = 0, first = node.firstChild;
fn = fn || function () {
return 1;
};
while ( first ) {
if ( fn( first ) ) {
count++;
}
first = first.nextSibling;
}
return count;
},
/**
* 判断是否为空节点
* @function
* @param {Node} node 节点
* @return {Boolean} 是否为空节点
*/
isEmptyNode:function ( node ) {
return !node.firstChild || domUtils.getChildCount( node, function ( node ) {
return !domUtils.isBr( node ) && !domUtils.isBookmarkNode( node ) && !domUtils.isWhitespace( node )
} ) == 0
},
/**
* 清空节点所有的className
* @function
* @param {Array} nodes 节点数组
*/
clearSelectedArr:function ( nodes ) {
var node;
while ( node = nodes.pop() ) {
domUtils.removeAttributes( node, ['class'] );
}
},
/**
* 将显示区域滚动到显示节点的位置
* @function
* @param {Node} node 节点
* @param {window} win window对象
* @param {Number} offsetTop 距离上方的偏移量
*/
scrollToView:function ( node, win, offsetTop ) {
var getViewPaneSize = function () {
var doc = win.document,
mode = doc.compatMode == 'CSS1Compat';
return {
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 ) {
return {
x:win.pageXOffset || 0,
y:win.pageYOffset || 0
};
}
else {
var doc = win.document;
return {
x:doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
y:doc.documentElement.scrollTop || doc.body.scrollTop || 0
};
}
};
var winHeight = getViewPaneSize().height, offset = winHeight * -1 + offsetTop;
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} node 节点
*/
isBr:function ( node ) {
return node.nodeType == 1 && node.tagName == 'BR';
},
isFillChar:function ( node ) {
return node.nodeType == 3 && !node.nodeValue.replace( new RegExp( domUtils.fillChar ), '' ).length
},
isStartInblock:function ( range ) {
var tmpRange = range.cloneRange(),
flag = 0,
start = tmpRange.startContainer,
tmp;
while ( start && domUtils.isFillChar( start ) ) {
tmp = start;
start = start.previousSibling
}
if ( tmp ) {
tmpRange.setStartBefore( tmp );
start = tmpRange.startContainer;
}
if ( start.nodeType == 1 && domUtils.isEmptyNode( start ) && tmpRange.startOffset == 1 ) {
tmpRange.setStart( start, 0 ).collapse( true );
}
while ( !tmpRange.startOffset ) {
start = tmpRange.startContainer;
if ( domUtils.isBlockElm( start ) || domUtils.isBody( start ) ) {
flag = 1;
break;
}
var pre = tmpRange.startContainer.previousSibling,
tmpNode;
if ( !pre ) {
tmpRange.setStartBefore( tmpRange.startContainer );
} else {
while ( pre && domUtils.isFillChar( pre ) ) {
tmpNode = pre;
pre = pre.previousSibling;
}
if ( tmpNode ) {
tmpRange.setStartBefore( tmpNode );
} else {
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 ) {
return 0;
}
for ( var n in dtd.$isNotEmpty ) {
if ( node.getElementsByTagName( n ).length ) {
return 0;
}
}
return 1;
},
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.left;
var offsetTop = offset.top - rect.top;
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 );
} else {
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 && node.tagName.toLowerCase() == tagName;
}
};
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
/**
* @class baidu.editor.dom.Range Range类
*/
/**
* @description Range类实现
* @author zhanyi
*/
(function () {
var guid = 0,
fillChar = domUtils.fillChar,
fillData;
/**
* 更新range的collapse状态
* @param {Range} range range对象
*/
function updateCollapse( range ) {
range.collapsed =
range.startContainer && range.endContainer &&
range.startContainer === range.endContainer &&
range.startOffset == range.endOffset;
}
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.parentNode;
}
if ( toStart ) {
range.startContainer = node;
range.startOffset = offset;
if ( !range.endContainer ) {
range.collapse( true );
}
} else {
range.endContainer = node;
range.endOffset = offset;
if ( !range.startContainer ) {
range.collapse( false );
}
}
updateCollapse( range );
return 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, tmpEnd;
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 );
}
return frag;
}
var current, currentLevel, clone = frag,
startParents = domUtils.findParents( start, true ), endParents = domUtils.findParents( end, true );
for ( var i = 0; startParents[i] == endParents[i]; ) {
i++;
}
for ( var j = i, si; si = startParents[j]; j++ ) {
current = si.nextSibling;
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 );
}
} else {
clone.appendChild( !action ? start.cloneNode( true ) : start );
}
}
} else {
currentLevel = si.cloneNode( false );
clone.appendChild( currentLevel );
}
while ( current ) {
if ( current === end || current === endParents[j] ) {
break;
}
si = current.nextSibling;
clone.appendChild( !action ? current.cloneNode( true ) : current );
current = si;
}
clone = currentLevel;
}
clone = frag;
if ( !startParents[i] ) {
clone.appendChild( startParents[i - 1].cloneNode( false ) );
clone = clone.firstChild;
}
for ( var j = i, ei; ei = endParents[j]; j++ ) {
current = ei.previousSibling;
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 );
}
}
} else {
currentLevel = ei.cloneNode( false );
clone.appendChild( currentLevel );
}
//如果两端同级,右边第一次已经被开始做了
if ( j != i || !startParents[i] ) {
while ( current ) {
if ( current === start ) {
break;
}
ei = current.previousSibling;
clone.insertBefore( !action ? current.cloneNode( true ) : current, clone.firstChild );
current = ei;
}
}
clone = currentLevel;
}
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 );
return frag;
}
/**
* Range类
* @param {Document} document 编辑器页面document对象
*/
var Range = dom.Range = function ( document ) {
var me = this;
me.startContainer =
me.startOffset =
me.endContainer =
me.endOffset = null;
me.document = document;
me.collapsed = true;
};
/**
* 删除fillData
* @param doc
* @param excludeNode
*/
function removeFillData( doc, excludeNode ) {
try {
if ( fillData && domUtils.inDoc( fillData, doc ) ) {
if ( !fillData.nodeValue.replace( fillCharReg, '' ).length ) {
var tmpNode = fillData.parentNode;
domUtils.remove( fillData );
while ( tmpNode && domUtils.isEmptyInlineElement( tmpNode ) && !tmpNode.contains( excludeNode ) ) {
fillData = tmpNode.parentNode;
domUtils.remove( tmpNode );
tmpNode = fillData;
}
} else {
fillData.nodeValue = fillData.nodeValue.replace( fillCharReg, '' );
}
}
} catch ( e ) {
}
}
/**
*
* @param node
* @param dir
*/
function mergSibling( node, dir ) {
var tmpNode;
node = node[dir];
while ( node && domUtils.isFillChar( node ) ) {
tmpNode = node[dir];
domUtils.remove( node );
node = tmpNode;
}
}
Range.prototype = {
/**
* 克隆选中的内容到一个fragment里
* @public
* @function
* @name baidu.editor.dom.Range.cloneContents
* @return {Fragment} frag|null 返回选中内容的文本片段或者空
*/
cloneContents:function () {
return this.collapsed ? null : execContentsAction( this, 0 );
},
/**
* 删除所选内容
* @public
* @function
* @name baidu.editor.dom.Range.deleteContents
* @return {Range} 删除选中内容后的Range
*/
deleteContents:function () {
var txt;
if ( !this.collapsed ) {
execContentsAction( this, 1 );
}
if ( browser.webkit ) {
txt = this.startContainer;
if ( txt.nodeType == 3 && !txt.nodeValue.length ) {
this.setStartBefore( txt ).collapse( true );
domUtils.remove( txt );
}
}
return this;
},
/**
* 取出内容
* @public
* @function
* @name baidu.editor.dom.Range.extractContents
* @return {String} 获得Range选中的内容
*/
extractContents:function () {
return this.collapsed ? null : execContentsAction( this, 2 );
},
/**
* 设置range的开始位置
* @public
* @function
* @name baidu.editor.dom.Range.setStart
* @param {Node} node range开始节点
* @param {Number} offset 偏移量
* @return {Range} 返回Range
*/
setStart:function ( node, offset ) {
return setEndPoint( true, node, offset, this );
},
/**
* 设置range结束点的位置
* @public
* @function
* @name baidu.editor.dom.Range.setEnd
* @param {Node} node range结束节点
* @param {Number} offset 偏移量
* @return {Range} 返回Range
*/
setEnd:function ( node, offset ) {
return setEndPoint( false, node, offset, this );
},
/**
* 将开始位置设置到node后
* @public
* @function
* @name baidu.editor.dom.Range.setStartAfter
* @param {Node} node 节点
* @return {Range} 返回Range
*/
setStartAfter:function ( node ) {
return this.setStart( node.parentNode, domUtils.getNodeIndex( node ) + 1 );
},
/**
* 将开始位置设置到node前
* @public
* @function
* @name baidu.editor.dom.Range.setStartBefore
* @param {Node} node 节点
* @return {Range} 返回Range
*/
setStartBefore:function ( node ) {
return this.setStart( node.parentNode, domUtils.getNodeIndex( node ) );
},
/**
* 将结束点位置设置到node后
* @public
* @function
* @name baidu.editor.dom.Range.setEndAfter
* @param {Node} node 节点
* @return {Range} 返回Range
*/
setEndAfter:function ( node ) {
return this.setEnd( node.parentNode, domUtils.getNodeIndex( node ) + 1 );
},
/**
* 将开始设置到node的最开始位置 ^text
* @public
* @function
* @name baidu.editor.dom.Range.setEndAfter
* @param {Node} node 节点
* @return {Range} 返回Range
*/
setStartAtFirst:function ( node ) {
return this.setStart( node, 0 );
},
/**
* 将开始设置到node的最开始位置 text^
* @public
* @function
* @name baidu.editor.dom.Range.setEndAfter
* @param {Node} node 节点
* @return {Range} 返回Range
*/
setStartAtLast:function ( node ) {
return this.setStart( node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length );
},
/**
* 将结束设置到node的最开始位置 ^text
* @public
* @function
* @name baidu.editor.dom.Range.setEndAfter
* @param {Node} node 节点
* @return {Range} 返回Range
*/
setEndAtFirst:function ( node ) {
return this.setEnd( node, 0 );
},
/**
* 将结束设置到node的最开始位置 text^
* @public
* @function
* @name baidu.editor.dom.Range.setEndAfter
* @param {Node} node 节点
* @return {Range} 返回Range
*/
setEndAtLast:function ( node ) {
return this.setEnd( node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length );
},
/**
* 将结束点位置设置到node前
* @public
* @function
* @name baidu.editor.dom.Range.setEndBefore
* @param {Node} node 节点
* @return {Range} 返回Range
*/
setEndBefore:function ( node ) {
return this.setEnd( node.parentNode, domUtils.getNodeIndex( node ) );
},
/**
* 选中指定节点
* @public
* @function
* @name baidu.editor.dom.Range.selectNode
* @param {Node} node 节点
* @return {Range} 返回Range
*/
selectNode:function ( node ) {
return this.setStartBefore( node ).setEndAfter( node );
},
/**
* 选中node下的所有节点
* @public
* @function
* @name baidu.editor.dom.Range.selectNodeContents
* @param {Element} node 要设置的节点
* @return {Range} 返回Range
*/
selectNodeContents:function ( node ) {
return this.setStart( node, 0 ).setEnd( node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length );
},
/**
* 克隆range
* @public
* @function
* @name baidu.editor.dom.Range.cloneRange
* @return {Range} 克隆的range对象
*/
cloneRange:function () {
var me = this, range = new Range( me.document );
return range.setStart( me.startContainer, me.startOffset ).setEnd( me.endContainer, me.endOffset );
},
/**
* 让选区闭合
* @public
* @function
* @name baidu.editor.dom.Range.collapse
* @param {Boolean} toStart 是否在选区开始位置闭合选区,true在开始位置闭合,false反之
* @return {Range} range对象
*/
collapse:function ( toStart ) {
var me = this;
if ( toStart ) {
me.endContainer = me.startContainer;
me.endOffset = me.startOffset;
}
else {
me.startContainer = me.endContainer;
me.startOffset = me.endOffset;
}
me.collapsed = true;
return me;
},
/**
* 调整range的边界,“缩”到合适的位置
* @public
* @function
* @name baidu.editor.dom.Range.shrinkBoundary
* @param {Boolean} ignoreEnd 是否考虑前面的元素
*/
shrinkBoundary:function ( ignoreEnd ) {
var me = this, child,
collapsed = me.collapsed;
while ( me.startContainer.nodeType == 1 //是element
&& (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
&& child.nodeType == 1 && !domUtils.isBookmarkNode( child )
&& !dtd.$empty[child.tagName] && !dtd.$nonChild[child.tagName] ) {
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
&& child.nodeType == 1 && !domUtils.isBookmarkNode( child )
&& !dtd.$empty[child.tagName] && !dtd.$nonChild[child.tagName] ) {
me.setEnd( child, child.childNodes.length );
}
}
return me;
},
/**
* 找到startContainer和endContainer的公共祖先节点
* @public
* @function
* @name baidu.editor.dom.Range.getCommonAncestor
* @param {Boolean} includeSelf 是否包含自身
* @param {Boolean} ignoreTextNode 是否忽略文本节点
* @return {Node} 祖先节点
*/
getCommonAncestor:function ( includeSelf, ignoreTextNode ) {
var start = this.startContainer,
end = this.endContainer;
if ( start === end ) {
if ( includeSelf && start.nodeType == 1 && this.startOffset == this.endOffset - 1 ) {
return start.childNodes[this.startOffset];
}
//只有在上来就相等的情况下才会出现是文本的情况
return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start;
}
return domUtils.getCommonAncestor( start, end );
},
/**
* 切割文本节点,将边界扩大到element
* @public
* @function
* @name baidu.editor.dom.Range.trimBoundary
* @param {Boolean} ignoreEnd 为真就不处理结束边界
* @return {Range} range对象
* @example |xxx
* startContainer = xxx; startOffset = 0
* 执行后
* startContainer = ; startOffset = 0
* @example xx|x
* startContainer = xxx; startOffset = 2
* 执行后
* startContainer = ; startOffset = 1 因为将xxx切割成2个节点了
*/
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 {
if ( offset >= end.nodeValue.length ) {
this.setEndAfter( end );
} else {
domUtils.split( end, offset );
this.setEndAfter( end );
}
}
}
}
return this;
},
/**
* 如果选区在文本的边界上,就扩展选区到文本的父节点上
* @public
* @function
* @name baidu.editor.dom.Range.txtToElmBoundary
* @return {Range} range对象
* @example |xxx
* startContainer = xxx; startOffset = 0
* 执行后
* startContainer = ; startOffset = 0
* @example xxx|
* startContainer = xxx; startOffset = 3
* 执行后
* startContainer = ; startOffset = 1
*/
txtToElmBoundary:function () {
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 ( !this.collapsed ) {
adjust( this, 'start' );
adjust( this, 'end' );
}
return this;
},
/**
* 在当前选区的开始位置前插入一个节点或者fragment
* @public
* @function
* @name baidu.editor.dom.Range.insertNode
* @param {Node/DocumentFragment} node 要插入的节点或fragment
* @return {Range} 返回range对象
*/
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 );
},
/**
* 设置光标位置
* @public
* @function
* @name baidu.editor.dom.Range.setCursor
* @param {Boolean} toEnd true为闭合到选区的结束位置后,false为闭合到选区的开始位置前
* @return {Range} 返回range对象
*/
setCursor:function ( toEnd, notFillData ) {
return this.collapse( !toEnd ).select( notFillData );
},
/**
* 创建书签
* @public
* @function
* @name baidu.editor.dom.Range.createBookmark
* @param {Boolean} serialize true:为true则返回对象中用id来分别表示书签的开始和结束节点
* @param {Boolean} same true:是否采用唯一的id,false将会为每一个标签产生一个唯一的id
* @returns {Object} bookmark对象
*/
createBookmark:function ( serialize, same ) {
var endNode,
startNode = this.document.createElement( 'span' );
startNode.style.cssText = 'display:none;line-height:0px;';
startNode.appendChild( this.document.createTextNode( '\uFEFF' ) );
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( false ).insertNode( endNode );
this.setEndBefore( endNode );
}
this.setStartAfter( startNode );
return {
start:serialize ? startNode.id : startNode,
end:endNode ? serialize ? endNode.id : endNode : null,
id:serialize
}
},
/**
* 移动边界到书签,并删除书签
* @public
* @function
* @name baidu.editor.dom.Range.moveToBookmark
* @params {Object} bookmark对象
* @returns {Range} Range对象
*/
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;
},
/**
* 调整边界到一个block元素上,或者移动到最大的位置
* @public
* @function
* @name baidu.editor.dom.Range.enlarge
* @params {Boolean} toBlock 扩展到block元素
* @params {Function} stopFn 停止函数,若返回true,则不再扩展
* @return {Range} Range对象
*/
enlarge:function ( toBlock, stopFn ) {
var isBody = domUtils.isBody,
pre, node, tmp = this.document.createTextNode( '' );
if ( toBlock ) {
node = this.startContainer;
if ( node.nodeType == 1 ) {
if ( node.childNodes[this.startOffset] ) {
pre = node = node.childNodes[this.startOffset]
} else {
node.appendChild( tmp );
pre = node = tmp;
}
} else {
pre = node;
}
while ( 1 ) {
if ( domUtils.isBlockElm( node ) ) {
node = pre;
while ( (pre = node.previousSibling) && !domUtils.isBlockElm( pre ) ) {
node = pre;
}
this.setStartBefore( node );
break;
}
pre = node;
node = node.parentNode;
}
node = this.endContainer;
if ( node.nodeType == 1 ) {
if ( pre = node.childNodes[this.endOffset] ) {
node.insertBefore( tmp, pre );
} else {
node.appendChild( tmp );
}
pre = node = tmp;
} else {
pre = node;
}
while ( 1 ) {
if ( domUtils.isBlockElm( node ) ) {
node = pre;
while ( (pre = node.nextSibling) && !domUtils.isBlockElm( pre ) ) {
node = pre;
}
this.setEndAfter( node );
break;
}
pre = node;
node = node.parentNode;
}
if ( tmp.parentNode === this.endContainer ) {
this.endOffset--;
}
domUtils.remove( tmp );
}
// 扩展边界到最大
if ( !this.collapsed ) {
while ( this.startOffset == 0 ) {
if ( stopFn && stopFn( this.startContainer ) ) {
break;
}
if ( isBody( this.startContainer ) ) {
break;
}
this.setStartBefore( this.startContainer );
}
while ( this.endOffset == (this.endContainer.nodeType == 1 ? this.endContainer.childNodes.length : this.endContainer.nodeValue.length) ) {
if ( stopFn && stopFn( this.endContainer ) ) {
break;
}
if ( isBody( this.endContainer ) ) {
break;
}
this.setEndAfter( this.endContainer );
}
}
return this;
},
/**
* 调整边界
* @public
* @function
* @name baidu.editor.dom.Range.adjustmentBoundary
* @return {Range} Range对象
* @example
* xx[xxxxx] ==> xx[xxxxx]
* [xx]xxx ==> [xx]xxx
*
*/
adjustmentBoundary:function () {
if ( !this.collapsed ) {
while ( !domUtils.isBody( this.startContainer ) &&
this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
) {
this.setStartAfter( this.startContainer );
}
while ( !domUtils.isBody( this.endContainer ) && !this.endOffset ) {
this.setEndBefore( this.endContainer );
}
}
return this;
},
/**
* 给选区中的内容加上inline样式
* @public
* @function
* @name baidu.editor.dom.Range.applyInlineStyle
* @param {String} tagName 标签名称
* @param {Object} attrObj 属性
* @return {Range} Range对象
*/
applyInlineStyle:function ( tagName, attrs, list ) {
if ( this.collapsed )return this;
this.trimBoundary().enlarge( false,
function ( node ) {
return node.nodeType == 1 && domUtils.isBlockElm( node )
} ).adjustmentBoundary();
var bookmark = this.createBookmark(),
end = bookmark.end,
filterFn = function ( node ) {
return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace( node );
},
current = domUtils.getNextDomNode( bookmark.start, false, filterFn ),
node,
pre,
range = this.cloneRange();
while ( current && (domUtils.getPosition( current, end ) & domUtils.POSITION_PRECEDING) ) {
if ( current.nodeType == 3 || dtd[tagName][current.tagName] ) {
range.setStartBefore( current );
node = current;
while ( node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end ) {
pre = node;
node = domUtils.getNextDomNode( node, node.nodeType == 1, null, function ( parent ) {
return dtd[tagName][parent.tagName];
} );
}
var frag = range.setEndAfter( pre ).extractContents(), elm;
if ( list && list.length > 0 ) {
var level, top;
top = level = list[0].cloneNode( false );
for ( var i = 1, ci; ci = list[i++]; ) {
level.appendChild( ci.cloneNode( false ) );
level = level.firstChild;
}
elm = level;
} else {
elm = range.document.createElement( tagName );
}
if ( attrs ) {
domUtils.setAttributes( elm, attrs );
}
elm.appendChild( frag );
range.insertNode( list ? top : elm );
//处理下滑线在a上的情况
var aNode;
if ( tagName == 'span' && attrs.style && /text\-decoration/.test( attrs.style ) && (aNode = domUtils.findParentByTagName( elm, 'a', true )) ) {
domUtils.setAttributes( aNode, attrs );
domUtils.remove( elm, true );
elm = aNode;
} else {
domUtils.mergSibling( elm );
domUtils.clearEmptySibling( elm );
}
//去除子节点相同的
domUtils.mergChild( elm, tagName, attrs );
current = domUtils.getNextDomNode( elm, false, filterFn );
domUtils.mergToParent( elm );
if ( node === end ) {
break;
}
} else {
current = domUtils.getNextDomNode( current, true, filterFn );
}
}
return this.moveToBookmark( bookmark );
},
/**
* 去掉inline样式
* @public
* @function
* @name baidu.editor.dom.Range.removeInlineStyle
* @param {String/Array} tagName 要去掉的标签名
* @return {Range} Range对象
*/
removeInlineStyle:function ( tagName ) {
if ( this.collapsed )return this;
tagName = utils.isArray( tagName ) ? tagName : [tagName];
this.shrinkBoundary().adjustmentBoundary();
var start = this.startContainer, end = this.endContainer;
while ( 1 ) {
if ( start.nodeType == 1 ) {
if ( utils.indexOf( tagName, start.tagName.toLowerCase() ) > -1 ) {
break;
}
if ( start.tagName.toLowerCase() == 'body' ) {
start = null;
break;
}
}
start = start.parentNode;
}
while ( 1 ) {
if ( end.nodeType == 1 ) {
if ( utils.indexOf( tagName, end.tagName.toLowerCase() ) > -1 ) {
break;
}
if ( end.tagName.toLowerCase() == 'body' ) {
end = null;
break;
}
}
end = end.parentNode;
}
var bookmark = this.createBookmark(),
frag,
tmpRange;
if ( start ) {
tmpRange = this.cloneRange().setEndBefore( bookmark.start ).setStartBefore( start );
frag = tmpRange.extractContents();
tmpRange.insertNode( frag );
domUtils.clearEmptySibling( start, true );
start.parentNode.insertBefore( bookmark.start, start );
}
if ( end ) {
tmpRange = this.cloneRange().setStartAfter( bookmark.end ).setEndAfter( end );
frag = tmpRange.extractContents();
tmpRange.insertNode( frag );
domUtils.clearEmptySibling( end, false, true );
end.parentNode.insertBefore( bookmark.end, end.nextSibling );
}
var current = domUtils.getNextDomNode( bookmark.start, false, function ( node ) {
return node.nodeType == 1;
} ), next;
while ( current && current !== bookmark.end ) {
next = domUtils.getNextDomNode( current, true, function ( node ) {
return node.nodeType == 1;
} );
if ( utils.indexOf( tagName, current.tagName.toLowerCase() ) > -1 ) {
domUtils.remove( current, true );
}
current = next;
}
return this.moveToBookmark( bookmark );
},
/**
* 得到一个自闭合的节点
* @public
* @function
* @name baidu.editor.dom.Range.getClosedNode
* @return {Node} 闭合节点
* @example
*
,
*/
getClosedNode:function () {
var node;
if ( !this.collapsed ) {
var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
if ( range.startContainer.nodeType == 1 && range.startContainer === range.endContainer && range.endOffset - range.startOffset == 1 ) {
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选中元素
* @public
* @function
* @name baidu.editor.dom.Range.select
* @param {Boolean} notInsertFillData true为不加占位符
*/
select:browser.ie ? function ( notInsertFillData, 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 ( !notInsertFillData && 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;
mergSibling( tmp, 'previousSibling' );
mergSibling( 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 ) {
var win = domUtils.getWindow( this.document ),
sel = win.getSelection(),
txtNode;
//FF下关闭自动长高时滚动条在关闭dialog时会跳
//ff下如果不body.focus将不能定位闭合光标到编辑器内
browser.gecko ? this.document.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 ) {
//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 );
}
if ( !notInsertFillData ) {
txtNode = this.document.createTextNode( fillChar );
//跟着前边走
this.insertNode( txtNode );
removeFillData( this.document, txtNode );
mergSibling( txtNode, 'previousSibling' );
mergSibling( txtNode, 'nextSibling' );
fillData = txtNode;
this.setStart( txtNode, browser.webkit ? 1 : 0 ).collapse( true );
}
}
var nativeRange = this.document.createRange();
nativeRange.setStart( this.startContainer, this.startOffset );
nativeRange.setEnd( this.endContainer, this.endOffset );
sel.addRange( nativeRange );
}
return this;
},
/**
* 滚动到可视范围
* @public
* @function
* @name baidu.editor.dom.Range.scrollToView
* @param {Boolean} win 操作的window对象,若为空,则使用当前的window对象
* @param {Number} offset 滚动的偏移量
* @return {Range} Range对象
*/
scrollToView:function ( win, offset ) {
win = win ? window : domUtils.getWindow( this.document );
var span = this.document.createElement( 'span' );
//trace:717
span.innerHTML = ' ';
var tmpRange = this.cloneRange();
tmpRange.insertNode( span );
domUtils.scrollToView( span, win, offset );
domUtils.remove( span );
return this;
}
};
})();
///import editor.js
///import core/browser.js
///import core/dom/dom.js
///import core/dom/dtd.js
///import core/dom/domUtils.js
///import core/dom/Range.js
/**
* @class baidu.editor.dom.Selection Selection类
*/
(function () {
function getBoundaryInformation( range, start ) {
var getIndex = domUtils.getNodeIndex;
range = range.duplicate();
range.collapse( start );
var parent = range.parentElement();
//如果节点里没有子节点,直接退出
if ( !parent.hasChildNodes() ) {
return {container:parent, offset:0};
}
var siblings = parent.children,
child,
testRange = range.duplicate(),
startIndex = 0, endIndex = siblings.length - 1, index = -1,
distance;
while ( startIndex <= endIndex ) {
index = Math.floor( (startIndex + endIndex) / 2 );
child = siblings[index];
testRange.moveToElementText( child );
var position = testRange.compareEndPoints( 'StartToStart', range );
if ( position > 0 ) {
endIndex = index - 1;
} else if ( position < 0 ) {
startIndex = index + 1;
} else {
//trace:1043
return {container:parent, offset:getIndex( child )};
}
}
if ( index == -1 ) {
testRange.moveToElementText( parent );
testRange.setEndPoint( 'StartToStart', range );
distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
siblings = parent.childNodes;
if ( !distance ) {
child = siblings[siblings.length - 1];
return {container:child, offset:child.nodeValue.length};
}
var i = siblings.length;
while ( distance > 0 ){
distance -= siblings[ --i ].nodeValue.length;
}
return {container:siblings[i], offset:-distance};
}
testRange.collapse( position > 0 );
testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
if ( !distance ) {
return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
{container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} :
{container:child, offset:position > 0 ? 0 : child.childNodes.length}
}
while ( distance > 0 ) {
try {
var pre = child;
child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
distance -= child.nodeValue.length;
} catch ( e ) {
return {container:parent, offset:getIndex( pre )};
}
}
return {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance}
}
/**
* 将ieRange转换为Range对象
* @param {Range} ieRange ieRange对象
* @param {Range} range Range对象
* @return {Range} range 返回转换后的Range对象
*/
function transformIERangeToRange( ieRange, range ) {
if ( ieRange.item ) {
range.selectNode( ieRange.item( 0 ) );
} else {
var bi = getBoundaryInformation( ieRange, true );
range.setStart( bi.container, bi.offset );
if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) {
bi = getBoundaryInformation( ieRange, false );
range.setEnd( bi.container, bi.offset );
}
}
return range;
}
/**
* 获得ieRange
* @param {Selection} sel Selection对象
* @return {ieRange} 得到ieRange
*/
function _getIERange( sel ) {
var ieRange;
//ie下有可能报错
try {
ieRange = sel.getNative().createRange();
} catch ( e ) {
return null;
}
var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement();
if ( ( el.ownerDocument || el ) === sel.document ) {
return ieRange;
}
return null;
}
var Selection = dom.Selection = function ( doc ) {
var me = this, iframe;
me.document = doc;
if ( ie ) {
iframe = domUtils.getWindow( doc ).frameElement;
domUtils.on( iframe, 'beforedeactivate', function () {
me._bakIERange = me.getIERange();
} );
domUtils.on( iframe, 'activate', function () {
try {
if ( !_getIERange( me ) && me._bakIERange ) {
me._bakIERange.select();
}
} catch ( ex ) {
}
me._bakIERange = null;
} );
}
iframe = doc = null;
};
Selection.prototype = {
/**
* 获取原生seleciton对象
* @public
* @function
* @name baidu.editor.dom.Selection.getNative
* @return {Selection} 获得selection对象
*/
getNative:function () {
var doc = this.document;
try {
return !doc ? null : ie ? doc.selection : domUtils.getWindow( doc ).getSelection();
} catch ( e ) {
return null;
}
},
/**
* 获得ieRange
* @public
* @function
* @name baidu.editor.dom.Selection.getIERange
* @return {ieRange} 返回ie原生的Range
*/
getIERange:function () {
var ieRange = _getIERange( this );
if ( !ieRange ) {
if ( this._bakIERange ) {
return this._bakIERange;
}
}
return ieRange;
},
/**
* 缓存当前选区的range和选区的开始节点
* @public
* @function
* @name baidu.editor.dom.Selection.cache
*/
cache:function () {
this.clear();
this._cachedRange = this.getRange();
this._cachedStartElement = this.getStart();
this._cachedStartElementPath = this.getStartElementPath();
},
getStartElementPath:function () {
if ( this._cachedStartElementPath ) {
return this._cachedStartElementPath;
}
var start = this.getStart();
if ( start ) {
return domUtils.findParents( start, true, null, true )
}
return [];
},
/**
* 清空缓存
* @public
* @function
* @name baidu.editor.dom.Selection.clear
*/
clear:function () {
this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
},
/**
* 编辑器是否得到了选区
*/
isFocus:function () {
try {
return browser.ie && _getIERange( this ) || !browser.ie && this.getNative().rangeCount ? true : false;
} catch ( e ) {
return false;
}
},
/**
* 获取选区对应的Range
* @public
* @function
* @name baidu.editor.dom.Selection.getRange
* @returns {baidu.editor.dom.Range} 得到Range对象
*/
getRange:function () {
var me = this;
function optimze( range ) {
var child = me.document.body.firstChild,
collapsed = range.collapsed;
while ( child && child.firstChild ) {
range.setStart( child, 0 );
child = child.firstChild;
}
if ( !range.startContainer ) {
range.setStart( me.document.body, 0 )
}
if ( collapsed ) {
range.collapse( true );
}
}
if ( me._cachedRange != null ) {
return this._cachedRange;
}
var range = new baidu.editor.dom.Range( me.document );
if ( ie ) {
var nativeRange = me.getIERange();
if ( nativeRange ) {
transformIERangeToRange( nativeRange, range );
} else {
optimze( range );
}
} else {
var sel = me.getNative();
if ( sel && sel.rangeCount ) {
var firstRange = sel.getRangeAt( 0 );
var lastRange = sel.getRangeAt( sel.rangeCount - 1 );
range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset );
if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) {
optimze( range );
}
} else {
//trace:1734 有可能已经不在dom树上了,标识的节点
if ( this._bakRange && domUtils.inDoc( this._bakRange.startContainer, this.document ) ){
return this._bakRange;
}
optimze( range );
}
}
return this._bakRange = range;
},
/**
* 获取开始元素,用于状态反射
* @public
* @function
* @name baidu.editor.dom.Selection.getStart
* @return {Element} 获得开始元素
*/
getStart:function () {
if ( this._cachedStartElement ) {
return this._cachedStartElement;
}
var range = ie ? this.getIERange() : this.getRange(),
tmpRange,
start, tmp, parent;
if ( ie ) {
if ( !range ) {
//todo 给第一个值可能会有问题
return this.document.body.firstChild;
}
//control元素
if ( range.item ){
return range.item( 0 );
}
tmpRange = range.duplicate();
//修正ie下x[xx] 闭合后 x|xx
tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 );
tmpRange.collapse( 1 );
start = tmpRange.parentElement();
parent = tmp = range.parentElement();
while ( tmp = tmp.parentNode ) {
if ( tmp == start ) {
start = parent;
break;
}
}
} else {
range.shrinkBoundary();
start = range.startContainer;
if ( start.nodeType == 1 && start.hasChildNodes() ){
start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )];
}
if ( start.nodeType == 3 ){
return start.parentNode;
}
}
return start;
},
/**
* 得到选区中的文本
* @public
* @function
* @name baidu.editor.dom.Selection.getText
* @return {String} 选区中包含的文本
*/
getText:function () {
var nativeSel, nativeRange;
if ( this.isFocus() && (nativeSel = this.getNative()) ) {
nativeRange = browser.ie ? nativeSel.createRange() : nativeSel.getRangeAt( 0 );
return browser.ie ? nativeRange.text : nativeRange.toString();
}
return '';
}
};
})();
///import editor.js
///import core/utils.js
///import core/EventBase.js
///import core/browser.js
///import core/dom/dom.js
///import core/dom/domUtils.js
///import core/dom/Selection.js
///import core/dom/dtd.js
(function () {
var uid = 0,
_selectionChangeTimer;
function replaceSrc( div ) {
var imgs = div.getElementsByTagName( "img" ),
orgSrc;
for ( var i = 0, img; img = imgs[i++]; ) {
if ( orgSrc = img.getAttribute( "orgSrc" ) ) {
img.src = orgSrc;
img.removeAttribute( "orgSrc" );
}
}
var as = div.getElementsByTagName( "a" );
for ( var i = 0, ai; ai = as[i++]; i++ ) {
if ( ai.getAttribute( 'data_ue_src' ) ) {
ai.setAttribute( 'href', ai.getAttribute( 'data_ue_src' ) )
}
}
}
function setValue( form, editor ) {
var textarea;
if ( editor.textarea ) {
if ( utils.isString( editor.textarea ) ) {
for ( var i = 0, ti, tis = domUtils.getElementsByTagName( form, 'textarea' ); ti = tis[i++]; ) {
if ( ti.id == 'ueditor_textarea_' + editor.options.textarea ) {
textarea = ti;
break;
}
}
} else {
textarea = editor.textarea;
}
}
if ( !textarea ) {
form.appendChild( textarea = domUtils.creElm( document, 'textarea', {
'name':editor.options.textarea,
'id':'ueditor_textarea_' + editor.options.textarea,
'style':"display:none"
} ) );
}
textarea.value = editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null,null,true)
}
/**
* 编辑器类
* @public
* @class
* @extends baidu.editor.EventBase
* @name baidu.editor.Editor
* @param {Object} options
*/
var Editor = UE.Editor = function ( options ) {
var me = this;
me.uid = uid++;
EventBase.call( me );
me.commands = {};
me.options = utils.extend( options || {},UEDITOR_CONFIG, true );
//设置默认的常用属性
me.setOpt( {
isShow:true,
initialContent:'欢迎使用ueditor!',
autoClearinitialContent:false,
iframeCssUrl:me.options.UEDITOR_HOME_URL + '/themes/default/iframe.css',
textarea:'editorValue',
focus:false,
minFrameHeight:320,
autoClearEmptyNode:true,
fullscreen:false,
readonly:false,
zIndex:999,
imagePopup:true,
enterTag:'p',
pageBreakTag:'_baidu_page_break_tag_',
customDomain:false,
lang:'zh-cn',
langPath:me.options.UEDITOR_HOME_URL + 'lang/',
allHtmlEnabled:false
} );
utils.loadFile( document, {
src:me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
tag:"script",
type:"text/javascript",
defer:"defer"
}, function () {
//初始化插件
for ( var pi in UE.plugins ) {
UE.plugins[pi].call( me )
}
me.langIsReady = true;
me.fireEvent( "langReady" );
} );
UE.instants['ueditorInstant' + me.uid] = me;
};
Editor.prototype = /**@lends baidu.editor.Editor.prototype*/{
/**
* 当编辑器ready后执行传入的fn,如果编辑器已经ready好了,就马上执行fn,fn的中的this是编辑器实例
* @param {function} fn 需要执行的函数
*/
ready:function ( fn ) {
var me = this;
if ( fn )
me.isReady ? fn.apply( me ) : me.addListener( 'ready', fn );
},
setOpt:function ( key, val ) {
var obj = {};
if ( utils.isString( key ) ) {
obj[key] = val
} else {
obj = key;
}
utils.extend( this.options, obj, true );
},
destroy:function () {
var me = this;
me.fireEvent( 'destroy' );
me.container.innerHTML = '';
domUtils.remove( me.container );
//trace:2004
for ( var p in me ) {
if ( me.hasOwnProperty( p ) ) {
delete this[p];
}
}
},
/**
* 渲染编辑器的DOM到指定容器,必须且只能调用一次
* @public
* @function
* @param {Element|String} container
*/
render:function ( container ) {
var me = this, options = me.options;
if ( container.constructor === String ) {
container = document.getElementById( container );
}
if ( container ) {
var useBodyAsViewport = ie && browser.version < 9,
html = ( ie && browser.version < 9 ? '' : '') +
'' +
( options.iframeCssUrl ? '' : '' ) +
'';
if ( options.customDomain && document.domain != location.hostname ) {
html += '';
container.appendChild( domUtils.creElm( document, 'iframe', {
id:'baidu_editor_' + me.uid,
width:"100%",
height:"100%",
frameborder:"0",
src:'javascript:void(function(){document.open();document.domain="' + document.domain + '";' +
'document.write("' + html + '");document.close();}())'
} ) );
} else {
container.innerHTML = '';
var doc = container.firstChild.contentWindow.document;
!browser.webkit && doc.open();
doc.write( html + '