static.assets.platform.plugins.umeditor.umeditor.js Maven / Gradle / Ivy
/*!
* UEditor Mini版本
* version: 1.2.2
* build: Thu Dec 22 2016 16:36:28 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
*
* - IE系列返回值为5,6,7,8,9,10等
* - gecko系列会返回10900,158900等
* - webkit系列会返回其build号 (如 522等)
*
* @example
* ```javascript
* console.log( '当前浏览器版本号是: ' + UE.browser.version );
* ```
*/
browser.version = version;
/**
* @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
* @example
* ```javascript
* if ( UE.browser.isCompatible ) {
* console.log( '浏览器与UEditor能够良好兼容' );
* }
* ```
*/
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;
/**
* @file
* @name UM.Utils
* @short Utils
* @desc UEditor封装使用的静态工具函数
* @import editor.js
*/
var utils = UM.utils = {
/**
* 遍历数组,对象,nodeList
* @name each
* @grammar UM.utils.each(obj,iterator,[context])
* @since 1.2.4+
* @desc
* * obj 要遍历的对象
* * iterator 遍历的方法,方法的第一个是遍历的值,第二个是索引,第三个是obj
* * context iterator的上下文
* @example
* UM.utils.each([1,2],function(v,i){
* console.log(v)//值
* console.log(i)//索引
* })
* UM.utils.each(document.getElementsByTagName('*'),function(n){
* console.log(n.tagName)
* })
*/
each : function(obj, iterator, context) {
if (obj == null) return;
if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if(iterator.call(context, obj[i], i, obj) === false)
return false;
}
} else {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if(iterator.call(context, obj[key], key, obj) === false)
return false;
}
}
}
},
makeInstance:function (obj) {
var noop = new Function();
noop.prototype = obj;
obj = new noop;
noop.prototype = null;
return obj;
},
/**
* 将source对象中的属性扩展到target对象上
* @name extend
* @grammar UM.utils.extend(target,source) => Object //覆盖扩展
* @grammar UM.utils.extend(target,source,true) ==> Object //保留扩展
*/
extend:function (t, s, b) {
if (s) {
for (var k in s) {
if (!b || !t.hasOwnProperty(k)) {
t[k] = s[k];
}
}
}
return t;
},
extend2:function (t) {
var a = arguments;
for (var i = 1; i < a.length; i++) {
var x = a[i];
for (var k in x) {
if (!t.hasOwnProperty(k)) {
t[k] = x[k];
}
}
}
return t;
},
/**
* 模拟继承机制,subClass继承superClass
* @name inherits
* @grammar UM.utils.inherits(subClass,superClass) => subClass
* @example
* function SuperClass(){
* this.name = "小李";
* }
* SuperClass.prototype = {
* hello:function(str){
* console.log(this.name + str);
* }
* }
* function SubClass(){
* this.name = "小张";
* }
* UM.utils.inherits(SubClass,SuperClass);
* var sub = new SubClass();
* sub.hello("早上好!"); ==> "小张早上好!"
*/
inherits:function (subClass, superClass) {
var oldP = subClass.prototype,
newP = utils.makeInstance(superClass.prototype);
utils.extend(newP, oldP, true);
subClass.prototype = newP;
return (newP.constructor = subClass);
},
/**
* 用指定的context作为fn上下文,也就是this
* @name bind
* @grammar UM.utils.bind(fn,context) => fn
*/
bind:function (fn, context) {
return function () {
return fn.apply(context, arguments);
};
},
/**
* 创建延迟delay执行的函数fn
* @name defer
* @grammar UM.utils.defer(fn,delay) =>fn //延迟delay毫秒执行fn,返回fn
* @grammar UM.utils.defer(fn,delay,exclusion) =>fn //延迟delay毫秒执行fn,若exclusion为真,则互斥执行fn
* @example
* function test(){
* console.log("延迟输出!");
* }
* //非互斥延迟执行
* var testDefer = UM.utils.defer(test,1000);
* testDefer(); => "延迟输出!";
* testDefer(); => "延迟输出!";
* //互斥延迟执行
* var testDefer1 = UM.utils.defer(test,1000,true);
* testDefer1(); => //本次不执行
* testDefer1(); => "延迟输出!";
*/
defer:function (fn, delay, exclusion) {
var timerID;
return function () {
if (exclusion) {
clearTimeout(timerID);
}
timerID = setTimeout(fn, delay);
};
},
/**
* 查找元素item在数组array中的索引, 若找不到返回-1
* @name indexOf
* @grammar UM.utils.indexOf(array,item) => index|-1 //默认从数组开头部开始搜索
* @grammar UM.utils.indexOf(array,item,start) => index|-1 //start指定开始查找的位置
*/
indexOf:function (array, item, start) {
var index = -1;
start = this.isNumber(start) ? start : 0;
this.each(array, function (v, i) {
if (i >= start && v === item) {
index = i;
return false;
}
});
return index;
},
/**
* 移除数组array中的元素item
* @name removeItem
* @grammar UM.utils.removeItem(array,item)
*/
removeItem:function (array, item) {
for (var i = 0, l = array.length; i < l; i++) {
if (array[i] === item) {
array.splice(i, 1);
i--;
}
}
},
/**
* 删除字符串str的首尾空格
* @name trim
* @grammar UM.utils.trim(str) => String
*/
trim:function (str) {
return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
},
/**
* 将字符串list(以','分隔)或者数组list转成哈希对象
* @name listToMap
* @grammar UM.utils.listToMap(list) => Object //Object形如{test:1,br:1,textarea:1}
*/
listToMap:function (list) {
if (!list)return {};
list = utils.isArray(list) ? list : list.split(',');
for (var i = 0, ci, obj = {}; ci = list[i++];) {
obj[ci.toUpperCase()] = obj[ci] = 1;
}
return obj;
},
/**
* 将str中的html符号转义,默认将转义''&<">''四个字符,可自定义reg来确定需要转义的字符
* @name unhtml
* @grammar UM.utils.unhtml(str); => String
* @grammar UM.utils.unhtml(str,reg) => String
* @example
* var html = 'You say:"你好!Baidu & UEditor!"';
* UM.utils.unhtml(html); ==> <body>You say:"你好!Baidu & UEditor!"</body>
* UM.utils.unhtml(html,/[<>]/g) ==> <body>You say:"你好!Baidu & UEditor!"</body>
*/
unhtml:function (str, reg) {
return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) {
if (b) {
return a;
} else {
return {
'<':'<',
'&':'&',
'"':'"',
'>':'>',
"'":'''
}[a]
}
}) : '';
},
/**
* 将str中的转义字符还原成html字符
* @name html
* @grammar UM.utils.html(str) => String //详细参见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)
}
}
};
/**
* 判断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
*
*
*
*
* xxx
*
*
* ```
* @example
* ```html
*
*
*
* xxx
*
* xxx
*
*
* ```
*/
/**
* 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
* 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
* 则执行getNextDomNode(Node node)的查找过程。
* @method getNextDomNode
* @param { Node } node 需要获取其后的兄弟节点的节点对象
* @param { Boolean } startFromChild 查找过程是否从其子节点开始
* @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
* @see UE.dom.domUtils.getNextDomNode(Node)
*/
getNextDomNode:function (node, startFromChild, filterFn, guard) {
return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
},
getPreDomNode:function (node, startFromChild, filterFn, guard) {
return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
},
/**
* 检测节点node是否属于bookmark节点
* @name isBookmarkNode
* @grammar UM.dom.domUtils.isBookmarkNode(node) => true|false
*/
isBookmarkNode: function (node) {
return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
},
/**
* 获取节点node所在的window对象
* @name getWindow
* @grammar UM.dom.domUtils.getWindow(node) => window对象
*/
getWindow: function (node) {
var doc = node.ownerDocument || node;
return doc.defaultView || doc.parentWindow;
},
/**
* 获取离nodeA与nodeB最近的公共的祖先节点
* @method getCommonAncestor
* @param { Node } nodeA 第一个节点
* @param { Node } nodeB 第二个节点
* @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
* @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
* @example
* ```javascript
* var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
* //output: true
* console.log( commonAncestor.tagName.toLowerCase() === 'body' );
* ```
*/
getCommonAncestor:function (nodeA, nodeB) {
if (nodeA === nodeB)
return nodeA;
var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1;
while (parent = parent.parentNode) {
if (parent === nodeB) {
return parent;
}
parentsA.push(parent);
}
parent = nodeB;
while (parent = parent.parentNode) {
if (parent === nodeA)
return parent;
parentsB.push(parent);
}
parentsA.reverse();
parentsB.reverse();
while (i++, parentsA[i] === parentsB[i]) {
}
return i == 0 ? null : parentsA[i - 1];
},
/**
* 清除node节点左右连续为空的兄弟inline节点
* @method clearEmptySibling
* @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
* 则这些兄弟节点将被删除
* @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
* @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
* @example
* ```html
*
*
*
*
*
* xxx
*
*
*
* ```
*/
/**
* 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
* 则忽略对右边兄弟节点的操作。
* @method clearEmptySibling
* @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
* @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
* 则这些兄弟节点将被删除
* @see UE.dom.domUtils.clearEmptySibling(Node)
*/
/**
* 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
* 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
* @method clearEmptySibling
* @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
* @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
* @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
* 则这些兄弟节点将被删除
* @see UE.dom.domUtils.clearEmptySibling(Node)
*/
clearEmptySibling:function (node, ignoreNext, ignorePre) {
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;
}
}
!ignoreNext && clear(node.nextSibling, 'nextSibling');
!ignorePre && clear(node.previousSibling, 'previousSibling');
},
/**
* 将一个文本节点node拆分成两个文本节点,offset指定拆分位置
* @name split
* @grammar UM.dom.domUtils.split(node,offset) => 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;
},
/**
* 检测节点node是否为空节点(包括空格、换行、占位符等字符)
* @name isWhitespace
* @grammar UM.dom.domUtils.isWhitespace(node) => true|false
*/
isWhitespace: function (node) {
return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
},
/**
* 获取元素element相对于viewport的位置坐标
* @name getXY
* @grammar UM.dom.domUtils.getXY(element) => 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};
},
/**
* 检查节点node是否是空inline节点
* @name isEmptyInlineElement
* @grammar UM.dom.domUtils.isEmptyInlineElement(node) => 1|0
* @example
* => 1
* => 1
* => 1
* xx => 0
*/
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;
},
/**
* 检查节点node是否为块元素
* @name isBlockElm
* @grammar UM.dom.domUtils.isBlockElm(node) => true|false
*/
isBlockElm: function (node) {
return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
},
/**
* 原生方法getElementsByTagName的封装
* @name getElementsByTagName
* @grammar UM.dom.domUtils.getElementsByTagName(node,tagName) => Array //节点集合数组
*/
getElementsByTagName: function (node, name, filter) {
if (filter && utils.isString(filter)) {
var className = filter;
filter = function (node) {
var result = false;
$.each(utils.trim(className).replace(/[ ]{2,}/g, ' ').split(' '), function (i, v) {
if ($(node).hasClass(v)) {
result = true;
return false;
}
})
return result;
}
}
name = utils.trim(name).replace(/[ ]{2,}/g, ' ').split(' ');
var arr = [];
for (var n = 0, ni; ni = name[n++];) {
var list = node.getElementsByTagName(ni);
for (var i = 0, ci; ci = list[i++];) {
if (!filter || filter(ci))
arr.push(ci);
}
}
return arr;
},
/**
* 设置节点node及其子节点不会被选中
* @name unSelectable
* @grammar UM.dom.domUtils.unSelectable(node)
*/
unSelectable: ie && browser.ie9below || 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.msUserSelect =
node.style.KhtmlUserSelect = 'none';
},
/**
* 删除节点node上的属性attrNames,attrNames为属性名称数组
* @name removeAttributes
* @grammar UM.dom.domUtils.removeAttributes(node,attrNames)
* @example
* //Before remove
* xxxxx
* //Remove
* UM.dom.domUtils.removeAttributes(node,["id","name"]);
* //After remove
* xxxxx
*/
removeAttributes: function (node, attrNames) {
attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g, ' ').split(' ');
for (var i = 0, ci; ci = attrNames[i++];) {
ci = attrFix[ci] || ci;
switch (ci) {
case 'className':
node[ci] = '';
break;
case 'style':
node.style.cssText = '';
!browser.ie && node.removeAttributeNode(node.getAttributeNode('style'))
}
node.removeAttribute(ci);
}
},
/**
* 在doc下创建一个标签名为tag,属性为attrs的元素
* @name createElement
* @grammar UM.dom.domUtils.createElement(doc,tag,attrs) => Node //返回创建的节点
*/
createElement: function (doc, tag, attrs) {
return domUtils.setAttributes(doc.createElement(tag), attrs)
},
/**
* 为节点node添加属性attrs,attrs为属性键值对
* @name setAttributes
* @grammar UM.dom.domUtils.setAttributes(node,attrs) => node
*/
setAttributes: function (node, attrs) {
for (var attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
var value = attrs[attr];
switch (attr) {
case 'class':
//ie下要这样赋值,setAttribute不起作用
node.className = value;
break;
case 'style' :
node.style.cssText = node.style.cssText + ";" + value;
break;
case 'innerHTML':
node[attr] = value;
break;
case 'value':
node.value = value;
break;
default:
node.setAttribute(attrFix[attr] || attr, value);
}
}
}
return node;
},
/**
* 获取元素element的计算样式
* @name getComputedStyle
* @grammar UM.dom.domUtils.getComputedStyle(element,styleName) => String //返回对应样式名称的样式值
* @example
* getComputedStyle(document.body,"font-size") => "15px"
* getComputedStyle(form,"color") => "#ffccdd"
*/
getComputedStyle: function (element, styleName) {
return utils.transUnitToPx(utils.fixColor(styleName, $(element).css(styleName)));
},
/**
* 阻止事件默认行为
* @param {Event} evt 需要组织的事件对象
*/
preventDefault: function (evt) {
evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
},
/**
* 删除元素element指定的样式
* @method removeStyle
* @param { Element } element 需要删除样式的元素
* @param { String } styleName 需要删除的样式名
* @example
* ```html
*
*
*
* ```
*/
removeStyle:function (element, name) {
if(browser.ie ){
//针对color先单独处理一下
if(name == 'color'){
name = '(^|;)' + name;
}
element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'')
}else{
if (element.style.removeProperty) {
element.style.removeProperty (name);
}else {
element.style.removeAttribute (utils.cssStyleToDomStyle(name));
}
}
if (!element.style.cssText) {
domUtils.removeAttributes(element, ['style']);
}
},
/**
* 获取元素element的某个样式值
* @name getStyle
* @grammar UM.dom.domUtils.getStyle(element,name) => String
*/
getStyle: function (element, name) {
var value = element.style[ utils.cssStyleToDomStyle(name) ];
return utils.fixColor(name, value);
},
/**
* 为元素element设置样式属性值
* @name setStyle
* @grammar UM.dom.domUtils.setStyle(element,name,value)
*/
setStyle: function (element, name, value) {
element.style[utils.cssStyleToDomStyle(name)] = value;
if (!utils.trim(element.style.cssText)) {
this.removeAttributes(element, 'style')
}
},
/**
* 删除_moz_dirty属性
* @function
*/
removeDirtyAttr: function (node) {
for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
ci.removeAttribute('_moz_dirty');
}
node.removeAttribute('_moz_dirty');
},
/**
* 返回子节点的数量
* @function
* @param {Node} 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
},
/**
* 判断节点是否为br
* @function
* @param {Node} node 节点
*/
isBr: function (node) {
return node.nodeType == 1 && node.tagName == 'BR';
},
isEmptyBlock: function (node, reg) {
if (node.nodeType != 1)
return 0;
reg = 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;
},
//判断是否是编辑器自定义的参数
isCustomeNode: function (node) {
return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
},
fillNode: function (doc, node) {
var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
node.innerHTML = '';
node.appendChild(tmpNode);
},
isBoundaryNode: function (node, dir) {
var tmp;
while (!domUtils.isBody(node)) {
tmp = node;
node = node.parentNode;
if (tmp !== node[dir]) {
return false;
}
}
return true;
},
isFillChar: function (node, isInStart) {
return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length
},
isBody: function(node){
return $(node).hasClass('edui-body-container');
}
};
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
/**
* @file
* @name UM.dom.Range
* @anthor zhanyi
* @short Range
* @import editor.js,core/utils.js,core/browser.js,core/dom/domUtils.js,core/dom/dtd.js
* @desc Range范围实现类,本类是UEditor底层核心类,统一w3cRange和ieRange之间的差异,包括接口和属性
*/
(function () {
var guid = 0,
fillChar = domUtils.fillChar,
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 selectOneNode(rng){
return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
}
function setEndPoint(toStart, node, offset, range) {
//如果node是自闭合标签要处理
if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
node = node.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;
}
/**
* @name Range
* @grammar new UM.dom.Range(document) => Range 实例
* @desc 创建一个跟document绑定的空的Range实例
* - ***startContainer*** 开始边界的容器节点,可以是elementNode或者是textNode
* - ***startOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
* - ***endContainer*** 结束边界的容器节点,可以是elementNode或者是textNode
* - ***endOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
* - ***document*** 跟range关联的document对象
* - ***collapsed*** 是否是闭合状态
*/
var Range = dom.Range = function (document,body) {
var me = this;
me.startContainer =
me.startOffset =
me.endContainer =
me.endOffset = null;
me.document = document;
me.collapsed = true;
me.body = body;
};
/**
* 删除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) &&
//safari的contains有bug
(browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !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 mergeSibling(node, dir) {
var tmpNode;
node = node[dir];
while (node && domUtils.isFillChar(node)) {
tmpNode = node[dir];
domUtils.remove(node);
node = tmpNode;
}
}
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.prototype = {
/**
* @name deleteContents
* @grammar range.deleteContents() => Range
* @desc 删除当前选区范围中的所有内容并返回range实例,这时的range已经变成了闭合状态
* @example
* DOM Element :
* xx[xxx]x
* //执行方法后
* xx|x
* 注意range改变了
* range.startContainer => b
* range.startOffset => 2
* range.endContainer => b
* range.endOffset => 2
* range.collapsed => true
*/
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;
},
inFillChar : function(){
var start = this.startContainer;
if(this.collapsed && start.nodeType == 3
&& start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length
){
return true;
}
return false;
},
/**
* @name setStart
* @grammar range.setStart(node,offset) => Range
* @desc 设置range的开始位置位于node节点内,偏移量为offset
* 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符
*/
setStart:function (node, offset) {
return setEndPoint(true, node, offset, this);
},
/**
* 设置range的结束位置位于node节点,偏移量为offset
* 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符
* @name setEnd
* @grammar range.setEnd(node,offset) => Range
*/
setEnd:function (node, offset) {
return setEndPoint(false, node, offset, this);
},
/**
* 将Range开始位置设置到node节点之后
* @name setStartAfter
* @grammar range.setStartAfter(node) => Range
* @example
* xxx|xx
* 执行setStartAfter(i)后
* range.startContainer =>b
* range.startOffset =>2
*/
setStartAfter:function (node) {
return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
},
/**
* 将Range开始位置设置到node节点之前
* @name setStartBefore
* @grammar range.setStartBefore(node) => Range
* @example
* xxx|xx
* 执行setStartBefore(i)后
* range.startContainer =>b
* range.startOffset =>1
*/
setStartBefore:function (node) {
return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
},
/**
* 将Range结束位置设置到node节点之后
* @name setEndAfter
* @grammar range.setEndAfter(node) => Range
* @example
* xxx|xx
* setEndAfter(i)后
* range.endContainer =>b
* range.endtOffset =>2
*/
setEndAfter:function (node) {
return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
},
/**
* 将Range结束位置设置到node节点之前
* @name setEndBefore
* @grammar range.setEndBefore(node) => Range
* @example
* xxx|xx
* 执行setEndBefore(i)后
* range.endContainer =>b
* range.endtOffset =>1
*/
setEndBefore:function (node) {
return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
},
/**
* 将Range开始位置设置到node节点内的开始位置
* @name setStartAtFirst
* @grammar range.setStartAtFirst(node) => Range
*/
setStartAtFirst:function (node) {
return this.setStart(node, 0);
},
/**
* 将Range开始位置设置到node节点内的结束位置
* @name setStartAtLast
* @grammar range.setStartAtLast(node) => Range
*/
setStartAtLast:function (node) {
return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
},
/**
* 将Range结束位置设置到node节点内的开始位置
* @name setEndAtFirst
* @grammar range.setEndAtFirst(node) => Range
*/
setEndAtFirst:function (node) {
return this.setEnd(node, 0);
},
/**
* 将Range结束位置设置到node节点内的结束位置
* @name setEndAtLast
* @grammar range.setEndAtLast(node) => Range
*/
setEndAtLast:function (node) {
return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
},
/**
* 选中完整的指定节点,并返回包含该节点的range
* @name selectNode
* @grammar range.selectNode(node) => Range
*/
selectNode:function (node) {
return this.setStartBefore(node).setEndAfter(node);
},
/**
* 选中node内部的所有节点,并返回对应的range
* @name selectNodeContents
* @grammar range.selectNodeContents(node) => Range
* @example
* xx[xxxx]xxx
* 执行后
* [xxxxxxxxx]
* range.startContainer =>b
* range.startOffset =>0
* range.endContainer =>b
* range.endOffset =>3
*/
selectNodeContents:function (node) {
return this.setStart(node, 0).setEndAtLast(node);
},
/**
* 克隆一个新的range对象
* @name cloneRange
* @grammar range.cloneRange() => Range
*/
cloneRange:function () {
var me = this;
return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
},
/**
* 让选区闭合到尾部,若toStart为真,则闭合到头部
* @name collapse
* @grammar range.collapse() => Range
* @grammar range.collapse(true) => 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的边界,使其"收缩"到最小的位置
* @name shrinkBoundary
* @grammar range.shrinkBoundary() => Range //range开始位置和结束位置都调整,参见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 ');
this.cloneRange().insertNode($span.get(0));
var winScrollTop = $(window).scrollTop(),
winHeight = $(window).height(),
spanTop = $span.offset().top;
if(spanTop < winScrollTop-winHeight || spanTop > winScrollTop + winHeight ){
if(spanTop > winScrollTop + winHeight){
window.scrollTo(0,spanTop - winHeight + $span.height())
}else{
window.scrollTo(0,winScrollTop - spanTop)
}
}
$span.remove();
},
getOffset : function(){
var bk = this.createBookmark();
var offset = $(bk.start).css('display','inline-block').offset();
this.moveToBookmark(bk);
return offset
}
};
})();
///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 UM.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,txtRange ) {
var ieRange;
//ie下有可能报错
try {
ieRange = sel.getNative(txtRange).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,body ) {
var me = this;
me.document = doc;
me.body = body;
if ( browser.ie9below ) {
$( body).on('beforedeactivate', function () {
me._bakIERange = me.getIERange();
} ).on('activate', function () {
try {
var ieNativRng = _getIERange( me );
if ( (!ieNativRng || !me.rangeInBody(ieNativRng)) && me._bakIERange ) {
me._bakIERange.select();
}
} catch ( ex ) {
}
me._bakIERange = null;
} );
}
};
Selection.prototype = {
hasNativeRange : function(){
var rng;
if(!browser.ie || browser.ie9above){
var nativeSel = this.getNative();
if(!nativeSel.rangeCount){
return false;
}
rng = nativeSel.getRangeAt(0);
}else{
rng = _getIERange(this);
}
return this.rangeInBody(rng);
},
/**
* 获取原生seleciton对象
* @public
* @function
* @name UM.dom.Selection.getNative
* @return {Selection} 获得selection对象
*/
getNative:function (txtRange) {
var doc = this.document;
try {
return !doc ? null : browser.ie9below || txtRange? doc.selection : domUtils.getWindow( doc ).getSelection();
} catch ( e ) {
return null;
}
},
/**
* 获得ieRange
* @public
* @function
* @name UM.dom.Selection.getIERange
* @return {ieRange} 返回ie原生的Range
*/
getIERange:function (txtRange) {
var ieRange = _getIERange( this,txtRange );
if ( !ieRange || !this.rangeInBody(ieRange,txtRange)) {
if ( this._bakIERange ) {
return this._bakIERange;
}
}
return ieRange;
},
rangeInBody : function(rng,txtRange){
var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
return node === this.body || domUtils.inDoc(node,this.body);
},
/**
* 缓存当前选区的range和选区的开始节点
* @public
* @function
* @name UM.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 UM.dom.Selection.clear
*/
clear:function () {
this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
},
/**
* 编辑器是否得到了选区
*/
isFocus:function () {
return this.hasNativeRange()
},
/**
* 获取选区对应的Range
* @public
* @function
* @name UM.dom.Selection.getRange
* @returns {UM.dom.Range} 得到Range对象
*/
getRange:function () {
var me = this;
function optimze( range ) {
var child = me.body.firstChild,
collapsed = range.collapsed;
while ( child && child.firstChild ) {
range.setStart( child, 0 );
child = child.firstChild;
}
if ( !range.startContainer ) {
range.setStart( me.body, 0 )
}
if ( collapsed ) {
range.collapse( true );
}
}
if ( me._cachedRange != null ) {
return this._cachedRange;
}
var range = new dom.Range( me.document,me.body );
if ( browser.ie9below ) {
var nativeRange = me.getIERange();
if ( nativeRange && this.rangeInBody(nativeRange)) {
try{
transformIERangeToRange( nativeRange, range );
}catch(e){
optimze( range );
}
} else {
optimze( range );
}
} else {
var sel = me.getNative();
if ( sel && sel.rangeCount && me.rangeInBody(sel.getRangeAt( 0 ))) {
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 && (this._bakRange.startContainer === this.body || domUtils.inDoc( this._bakRange.startContainer, this.body )) ){
return this._bakRange;
}
optimze( range );
}
}
return this._bakRange = range;
},
/**
* 获取开始元素,用于状态反射
* @public
* @function
* @name UM.dom.Selection.getStart
* @return {Element} 获得开始元素
*/
getStart:function () {
if ( this._cachedStartElement ) {
return this._cachedStartElement;
}
var range = browser.ie9below ? this.getIERange() : this.getRange(),
tmpRange,
start, tmp, parent;
if ( browser.ie9below ) {
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 {
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 UM.dom.Selection.getText
* @return {String} 选区中包含的文本
*/
getText:function () {
var nativeSel, nativeRange;
if ( this.isFocus() && (nativeSel = this.getNative()) ) {
nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 );
return browser.ie9below ? nativeRange.text : nativeRange.toString();
}
return '';
}
};
})();
/**
* @file
* @name UM.Editor
* @short Editor
* @import editor.js,core/utils.js,core/EventBase.js,core/browser.js,core/dom/dtd.js,core/dom/domUtils.js,core/dom/Range.js,core/dom/Selection.js,plugins/serialize.js
* @desc 编辑器主类,包含编辑器提供的大部分公用接口
*/
(function () {
var uid = 0, _selectionChangeTimer;
/**
* @private
* @ignore
* @param form 编辑器所在的form元素
* @param editor 编辑器实例对象
*/
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 == 'umeditor_textarea_' + editor.options.textarea) {
textarea = ti;
break;
}
}
} else {
textarea = editor.textarea;
}
}
if (!textarea) {
form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
'name': editor.options.textarea,
'id': 'umeditor_textarea_' + editor.options.textarea,
'style': "display:none"
}));
//不要产生多个textarea
editor.textarea = textarea;
}
textarea.value = editor.hasContents() ?
(editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
''
}
function loadPlugins(me){
//初始化插件
for (var pi in UM.plugins) {
if(me.options.excludePlugins.indexOf(pi) == -1){
UM.plugins[pi].call(me);
me.plugins[pi] = 1;
}
}
me.langIsReady = true;
me.fireEvent("langReady");
}
function checkCurLang(I18N){
for(var lang in I18N){
return lang
}
}
/**
* UEditor编辑器类
* @name Editor
* @desc 创建一个跟编辑器实例
* - ***container*** 编辑器容器对象
* - ***iframe*** 编辑区域所在的iframe对象
* - ***window*** 编辑区域所在的window
* - ***document*** 编辑区域所在的document对象
* - ***body*** 编辑区域所在的body对象
* - ***selection*** 编辑区域的选区对象
*/
var Editor = UM.Editor = function (options) {
var me = this;
me.uid = uid++;
EventBase.call(me);
me.commands = {};
me.options = utils.extend(utils.clone(options || {}), UMEDITOR_CONFIG, true);
me.shortcutkeys = {};
me.inputRules = [];
me.outputRules = [];
//设置默认的常用属性
me.setOpt({
isShow: true,
initialContent: '',
initialStyle:'',
autoClearinitialContent: false,
textarea: 'editorValue',
focus: false,
focusInEnd: true,
autoClearEmptyNode: true,
fullscreen: false,
readonly: false,
zIndex: 999,
enterTag: 'p',
lang: 'zh-cn',
langPath: me.options.UMEDITOR_HOME_URL + 'lang/',
theme: 'default',
themePath: me.options.UMEDITOR_HOME_URL + 'themes/',
allHtmlEnabled: false,
autoSyncData : true,
autoHeightEnabled : true,
excludePlugins:''
});
me.plugins = {};
if(!utils.isEmptyObject(UM.I18N)){
//修改默认的语言类型
me.options.lang = checkCurLang(UM.I18N);
loadPlugins(me)
}else{
utils.loadFile(document, {
src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
tag: "script",
type: "text/javascript",
defer: "defer"
}, function () {
loadPlugins(me)
});
}
};
Editor.prototype = {
/**
* 当编辑器ready后执行传入的fn,如果编辑器已经完成ready,就马上执行fn,fn的中的this是编辑器实例。
* 大部分的实例接口都需要放在该方法内部执行,否则在IE下可能会报错。
* @name ready
* @grammar editor.ready(fn) fn是当编辑器渲染好后执行的function
* @example
* var editor = new UM.ui.Editor();
* editor.render("myEditor");
* editor.ready(function(){
* editor.setContent("欢迎使用UEditor!");
* })
*/
ready: function (fn) {
var me = this;
if (fn) {
me.isReady ? fn.apply(me) : me.addListener('ready', fn);
}
},
/**
* 为编辑器设置默认参数值。若用户配置为空,则以默认配置为准
* @grammar editor.setOpt(key,value); //传入一个键、值对
* @grammar editor.setOpt({ key:value}); //传入一个json对象
*/
setOpt: function (key, val) {
var obj = {};
if (utils.isString(key)) {
obj[key] = val
} else {
obj = key;
}
utils.extend(this.options, obj, true);
},
getOpt:function(key){
return this.options[key] || ''
},
/**
* 销毁编辑器实例对象
* @name destroy
* @grammar editor.destroy();
*/
destroy: function () {
var me = this;
me.fireEvent('destroy');
var container = me.container.parentNode;
if(container === document.body){
container = me.container;
}
var textarea = me.textarea;
if (!textarea) {
textarea = document.createElement('textarea');
container.parentNode.insertBefore(textarea, container);
} else {
textarea.style.display = ''
}
textarea.style.width = me.body.offsetWidth + 'px';
textarea.style.height = me.body.offsetHeight + 'px';
textarea.value = me.getContent();
textarea.id = me.key;
if(container.contains(textarea)){
$(textarea).insertBefore(container);
}
container.innerHTML = '';
domUtils.remove(container);
UM.clearCache(me.id);
//trace:2004
for (var p in me) {
if (me.hasOwnProperty(p)) {
delete this[p];
}
}
},
initialCont : function(holder){
if(holder){
holder.getAttribute('name') && ( this.options.textarea = holder.getAttribute('name'));
if (holder && /script|textarea/ig.test(holder.tagName)) {
var newDiv = document.createElement('div');
holder.parentNode.insertBefore(newDiv, holder);
this.options.initialContent = UM.htmlparser(holder.value || holder.innerHTML|| this.options.initialContent).toHtml();
holder.className && (newDiv.className = holder.className);
holder.style.cssText && (newDiv.style.cssText = holder.style.cssText);
if (/textarea/i.test(holder.tagName)) {
this.textarea = holder;
this.textarea.style.display = 'none';
} else {
holder.parentNode.removeChild(holder);
holder.id && (newDiv.id = holder.id);
}
holder = newDiv;
holder.innerHTML = '';
}
return holder;
}else{
return null;
}
},
/**
* 渲染编辑器的DOM到指定容器,必须且只能调用一次
* @name render
* @grammar editor.render(containerId); //可以指定一个容器ID
* @grammar editor.render(containerDom); //也可以直接指定容器对象
*/
render: function (container) {
var me = this,
options = me.options,
getStyleValue=function(attr){
return parseInt($(container).css(attr));
};
if (utils.isString(container)) {
container = document.getElementById(container);
}
if (container) {
this.id = container.getAttribute('id');
UM.setEditor(this);
utils.cssRule('edui-style-body',me.options.initialStyle,document);
container = this.initialCont(container);
container.className += ' edui-body-container';
if(options.initialFrameWidth){
options.minFrameWidth = options.initialFrameWidth
}else{
//都没给值,先写死了
options.minFrameWidth = options.initialFrameWidth = $(container).width() || UM.defaultWidth;
}
if(options.initialFrameHeight){
options.minFrameHeight = options.initialFrameHeight
}else{
options.initialFrameHeight = options.minFrameHeight = $(container).height() || UM.defaultHeight;
}
container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth -
getStyleValue("padding-left")-
getStyleValue("padding-right") +'px';
var height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - getStyleValue("padding-top")- getStyleValue("padding-bottom") );
if(this.options.autoHeightEnabled){
container.style.minHeight = height +'px';
container.style.height = '';
if(browser.ie && browser.version <= 6){
container.style.height = height ;
container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
}
}else{
$(container).height(height)
}
container.style.zIndex = options.zIndex;
this._setup(container);
}
},
/**
* 编辑器初始化
* @private
* @ignore
* @param {Element} doc 编辑器Iframe中的文档对象
*/
_setup: function (cont) {
var me = this,
options = me.options;
cont.contentEditable = true;
document.body.spellcheck = false;
me.document = document;
me.window = document.defaultView || document.parentWindow;
me.body = cont;
me.$body = $(cont);
me.selection = new dom.Selection(document,me.body);
me._isEnabled = false;
//gecko初始化就能得到range,无法判断isFocus了
var geckoSel;
if (browser.gecko && (geckoSel = this.selection.getNative())) {
geckoSel.removeAllRanges();
}
this._initEvents();
//为form提交提供一个隐藏的textarea
for (var form = cont.parentNode; form && !domUtils.isBody(form); form = form.parentNode) {
if (form.tagName == 'FORM') {
me.form = form;
if(me.options.autoSyncData){
$(cont).on('blur',function(){
setValue(form,me);
})
}else{
$(form).on('submit', function () {
setValue(this, me);
})
}
break;
}
}
if (options.initialContent) {
if (options.autoClearinitialContent) {
var oldExecCommand = me.execCommand;
me.execCommand = function () {
me.fireEvent('firstBeforeExecCommand');
return oldExecCommand.apply(me, arguments);
};
this._setDefaultContent(options.initialContent);
} else
this.setContent(options.initialContent, false, true);
}
//编辑器不能为空内容
if (domUtils.isEmptyNode(me.body)) {
me.body.innerHTML = '' + (browser.ie ? '' : '
') + '
';
}
//如果要求focus, 就把光标定位到内容开始
if (options.focus) {
setTimeout(function () {
me.focus(me.options.focusInEnd);
//如果自动清除开着,就不需要做selectionchange;
!me.options.autoClearinitialContent && me._selectionChange();
}, 0);
}
if (!me.container) {
me.container = cont.parentNode;
}
me._bindshortcutKeys();
me.isReady = 1;
me.fireEvent('ready');
options.onready && options.onready.call(me);
if(!browser.ie || browser.ie9above){
$(me.body).on( 'blur focus', function (e) {
var nSel = me.selection.getNative();
//chrome下会出现alt+tab切换时,导致选区位置不对
if (e.type == 'blur') {
if(nSel.rangeCount > 0 ){
me._bakRange = nSel.getRangeAt(0);
}
} else {
try {
me._bakRange && nSel.addRange(me._bakRange)
} catch (e) {
}
me._bakRange = null;
}
});
}
!options.isShow && me.setHide();
options.readonly && me.setDisabled();
},
/**
* 同步编辑器的数据,为提交数据做准备,主要用于你是手动提交的情况
* @name sync
* @grammar editor.sync(); //从编辑器的容器向上查找,如果找到就同步数据
* @grammar editor.sync(formID); //formID制定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
* @desc
* 后台取得数据得键值使用你容器上得''name''属性,如果没有就使用参数传入的''textarea''
* @example
* editor.sync();
* form.sumbit(); //form变量已经指向了form元素
*
*/
sync: function (formId) {
var me = this,
form = formId ? document.getElementById(formId) :
domUtils.findParent(me.body.parentNode, function (node) {
return node.tagName == 'FORM'
}, true);
form && setValue(form, me);
},
/**
* 设置编辑器高度
* @name setHeight
* @grammar editor.setHeight(number); //纯数值,不带单位
*/
setHeight: function (height,notSetHeight) {
!notSetHeight && (this.options.initialFrameHeight = height);
if(this.options.autoHeightEnabled){
$(this.body).css({
'min-height':height + 'px'
});
if(browser.ie && browser.version <= 6 && this.container){
this.container.style.height = height ;
this.container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
}
}else{
$(this.body).height(height)
}
this.fireEvent('resize');
},
/**
* 设置编辑器宽度
* @name setWidth
* @grammar editor.setWidth(number); //纯数值,不带单位
*/
setWidth:function(width){
this.$container && this.$container.width(width);
$(this.body).width(width - $(this.body).css('padding-left').replace('px','') * 1 - $(this.body).css('padding-right').replace('px','') * 1);
this.fireEvent('resize');
},
addshortcutkey: function (cmd, keys) {
var obj = {};
if (keys) {
obj[cmd] = keys
} else {
obj = cmd;
}
utils.extend(this.shortcutkeys, obj)
},
_bindshortcutKeys: function () {
var me = this, shortcutkeys = this.shortcutkeys;
me.addListener('keydown', function (type, e) {
var keyCode = e.keyCode || e.which;
for (var i in shortcutkeys) {
var tmp = shortcutkeys[i].split(',');
for (var t = 0, ti; ti = tmp[t++];) {
ti = ti.split(':');
var key = ti[0], param = ti[1];
if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
if (( (RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0)
&& (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1)
&& keyCode == RegExp.$3
) ||
keyCode == RegExp.$1
) {
if (me.queryCommandState(i,param) != -1)
me.execCommand(i, param);
domUtils.preventDefault(e);
}
}
}
}
});
},
/**
* 获取编辑器内容
* @name getContent
* @grammar editor.getContent() => String //若编辑器中只包含字符"<p><br /></p/>"会返回空。
* @grammar editor.getContent(fn) => String
* @example
* getContent默认是会现调用hasContents来判断编辑器是否为空,如果是,就直接返回空字符串
* 你也可以传入一个fn来接替hasContents的工作,定制判断的规则
* editor.getContent(function(){
* return false //编辑器没有内容 ,getContent直接返回空
* })
*/
getContent: function (cmd, fn,notSetCursor,ignoreBlank,formatter) {
var me = this;
if (cmd && utils.isFunction(cmd)) {
fn = cmd;
cmd = '';
}
if (fn ? !fn() : !this.hasContents()) {
return '';
}
me.fireEvent('beforegetcontent');
var root = UM.htmlparser(me.body.innerHTML,ignoreBlank);
me.filterOutputRule(root);
me.fireEvent('aftergetcontent',root);
return root.toHtml(formatter);
},
/**
* 取得完整的html代码,可以直接显示成完整的html文档
* @name getAllHtml
* @grammar editor.getAllHtml() => String
*/
getAllHtml: function () {
var me = this,
headHtml = [],
html = '';
me.fireEvent('getAllHtml', headHtml);
if (browser.ie && browser.version > 8) {
var headHtmlForIE9 = '';
utils.each(me.document.styleSheets, function (si) {
headHtmlForIE9 += ( si.href ? '' : '');
});
utils.each(me.document.getElementsByTagName('script'), function (si) {
headHtmlForIE9 += si.outerHTML;
});
}
return '' + (me.options.charset ? '' : '')
+ (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + ''
+ '' + me.getContent(null, null, true) + '';
},
/**
* 得到编辑器的纯文本内容,但会保留段落格式
* @name getPlainTxt
* @grammar editor.getPlainTxt() => String
*/
getPlainTxt: function () {
var reg = new RegExp(domUtils.fillChar, 'g'),
html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理
html = html.replace(/<(p|div)[^>]*>(
| )<\/\1>/gi, '\n')
.replace(/
/gi, '\n')
.replace(/<[^>/]+>/g, '')
.replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
return dtd.$block[c] ? '\n' : b ? b : '';
});
//取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/ /g, ' ');
},
/**
* 获取编辑器中的纯文本内容,没有段落格式
* @name getContentTxt
* @grammar editor.getContentTxt() => String
*/
getContentTxt: function () {
var reg = new RegExp(domUtils.fillChar, 'g');
//取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');
},
/**
* 将html设置到编辑器中, 如果是用于初始化时给编辑器赋初值,则必须放在ready方法内部执行
* @name setContent
* @grammar editor.setContent(html)
* @example
* var editor = new UM.ui.Editor()
* editor.ready(function(){
* //需要ready后执行,否则可能报错
* editor.setContent("欢迎使用UEditor!");
* })
*/
setContent: function (html, isAppendTo, notFireSelectionchange) {
var me = this;
me.fireEvent('beforesetcontent', html);
var root = UM.htmlparser(html);
me.filterInputRule(root);
html = root.toHtml();
me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html;
function isCdataDiv(node){
return node.tagName == 'DIV' && node.getAttribute('cdata_tag');
}
//给文本或者inline节点套p标签
if (me.options.enterTag == 'p') {
var child = this.body.firstChild, tmpNode;
if (!child || child.nodeType == 1 &&
(dtd.$cdata[child.tagName] || isCdataDiv(child) ||
domUtils.isCustomeNode(child)
)
&& child === this.body.lastChild) {
this.body.innerHTML = '' + (browser.ie ? ' ' : '
') + '
' + this.body.innerHTML;
} else {
var p = me.document.createElement('p');
while (child) {
while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) {
tmpNode = child.nextSibling;
p.appendChild(child);
child = tmpNode;
}
if (p.firstChild) {
if (!child) {
me.body.appendChild(p);
break;
} else {
child.parentNode.insertBefore(p, child);
p = me.document.createElement('p');
}
}
child = child.nextSibling;
}
}
}
me.fireEvent('aftersetcontent');
me.fireEvent('contentchange');
!notFireSelectionchange && me._selectionChange();
//清除保存的选区
me._bakRange = me._bakIERange = me._bakNativeRange = null;
//trace:1742 setContent后gecko能得到焦点问题
var geckoSel;
if (browser.gecko && (geckoSel = this.selection.getNative())) {
geckoSel.removeAllRanges();
}
if(me.options.autoSyncData){
me.form && setValue(me.form,me);
}
},
/**
* 让编辑器获得焦点,toEnd确定focus位置
* @name focus
* @grammar editor.focus([toEnd]) //默认focus到编辑器头部,toEnd为true时focus到内容尾部
*/
focus: function (toEnd) {
try {
var me = this,
rng = me.selection.getRange();
if (toEnd) {
rng.setStartAtLast(me.body.lastChild).setCursor(false, true);
} else {
rng.select(true);
}
this.fireEvent('focus');
} catch (e) {
}
},
/**
* 使编辑区域失去焦点
*/
blur:function(){
var sel = this.selection.getNative();
sel.empty ? sel.empty() : sel.removeAllRanges();
this.fireEvent('blur')
},
/**
* 判断编辑器当前是否获得了焦点
*/
isFocus : function(){
if(this.fireEvent('isfocus')===true){
return true;
}
return this.selection.isFocus();
},
/**
* 初始化UE事件及部分事件代理
* @private
* @ignore
*/
_initEvents: function () {
var me = this,
cont = me.body,
_proxyDomEvent = function(){
me._proxyDomEvent.apply(me, arguments);
};
$(cont)
.on( 'click contextmenu mousedown keydown keyup keypress mouseup mouseover mouseout selectstart', _proxyDomEvent)
.on( 'focus blur', _proxyDomEvent)
.on('mouseup keydown', function (evt) {
//特殊键不触发selectionchange
if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
return;
}
if (evt.button == 2)return;
me._selectionChange(250, evt);
});
},
/**
* 触发事件代理
* @private
* @ignore
*/
_proxyDomEvent: function (evt) {
return this.fireEvent(evt.type.replace(/^on/, ''), evt);
},
/**
* 变化选区
* @private
* @ignore
*/
_selectionChange: function (delay, evt) {
var me = this;
//有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
// if ( !me.selection.isFocus() ){
// return;
// }
var hackForMouseUp = false;
var mouseX, mouseY;
if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
var range = this.selection.getRange();
if (!range.collapsed) {
hackForMouseUp = true;
mouseX = evt.clientX;
mouseY = evt.clientY;
}
}
clearTimeout(_selectionChangeTimer);
_selectionChangeTimer = setTimeout(function () {
if (!me.selection.getNative()) {
return;
}
//修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
//IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
var ieRange;
if (hackForMouseUp && me.selection.getNative().type == 'None') {
ieRange = me.document.body.createTextRange();
try {
ieRange.moveToPoint(mouseX, mouseY);
} catch (ex) {
ieRange = null;
}
}
var bakGetIERange;
if (ieRange) {
bakGetIERange = me.selection.getIERange;
me.selection.getIERange = function () {
return ieRange;
};
}
me.selection.cache();
if (bakGetIERange) {
me.selection.getIERange = bakGetIERange;
}
if (me.selection._cachedRange && me.selection._cachedStartElement) {
me.fireEvent('beforeselectionchange');
// 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
me.fireEvent('selectionchange', !!evt);
me.fireEvent('afterselectionchange');
me.selection.clear();
}
}, delay || 50);
},
_callCmdFn: function (fnName, args) {
args = Array.prototype.slice.call(args,0);
var cmdName = args.shift().toLowerCase(),
cmd, cmdFn;
cmd = this.commands[cmdName] || UM.commands[cmdName];
cmdFn = cmd && cmd[fnName];
//没有querycommandstate或者没有command的都默认返回0
if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
return 0;
} else if (cmdFn) {
return cmdFn.apply(this, [cmdName].concat(args));
}
},
/**
* 执行编辑命令cmdName,完成富文本编辑效果
* @name execCommand
* @grammar editor.execCommand(cmdName) => {*}
*/
execCommand: function (cmdName) {
if(!this.isFocus()){
var bakRange = this.selection._bakRange;
if(bakRange){
bakRange.select()
}else{
this.focus(true)
}
}
cmdName = cmdName.toLowerCase();
var me = this,
result,
cmd = me.commands[cmdName] || UM.commands[cmdName];
if (!cmd || !cmd.execCommand) {
return null;
}
if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
me.__hasEnterExecCommand = true;
if (me.queryCommandState.apply(me,arguments) != -1) {
me.fireEvent('saveScene');
me.fireEvent('beforeexeccommand', cmdName);
result = this._callCmdFn('execCommand', arguments);
(!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
me.fireEvent('afterexeccommand', cmdName);
me.fireEvent('saveScene');
}
me.__hasEnterExecCommand = false;
} else {
result = this._callCmdFn('execCommand', arguments);
(!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange')
}
(!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange();
return result;
},
/**
* 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
* @name queryCommandState
* @grammar editor.queryCommandState(cmdName) => (-1|0|1)
* @desc
* * ''-1'' 当前命令不可用
* * ''0'' 当前命令可用
* * ''1'' 当前命令已经执行过了
*/
queryCommandState: function (cmdName) {
try{
return this._callCmdFn('queryCommandState', arguments);
}catch(e){
return 0
}
},
/**
* 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
* @name queryCommandValue
* @grammar editor.queryCommandValue(cmdName) => {*}
*/
queryCommandValue: function (cmdName) {
try{
return this._callCmdFn('queryCommandValue', arguments);
}catch(e){
return null
}
},
/**
* 检查编辑区域中是否有内容,若包含tags中的节点类型,直接返回true
* @name hasContents
* @desc
* 默认有文本内容,或者有以下节点都不认为是空
* {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 ? '' : '
') + '
';
me.removeListener('firstBeforeExecCommand focus', clear);
setTimeout(function () {
me.focus();
me._selectionChange();
}, 0)
}
}
return function (cont) {
var me = this;
me.body.innerHTML = '' + 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(/]*>[\s\S]*?.<\/v:shape>/gi,function(str){
//opera能自己解析出image所这里直接返回空
if(browser.opera){
return '';
}
try{
//有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
if(/Bitmap/i.test(str)){
return '';
}
var width = str.match(/width:([ \d.]*p[tx])/i)[1],
height = str.match(/height:([ \d.]*p[tx])/i)[1],
src = str.match(/src=\s*"([^"]*)"/i)[1];
return '';
} catch(e){
return '';
}
})
//针对wps添加的多余标签处理
.replace(/<\/?div[^>]*>/g,'')
//去掉多余的属性
.replace( /v:\w+=(["']?)[^'"]+\1/g, '' )
.replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" )
.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(!/]/.test(html)) {
return UM.htmlparser(html).children[0]
} else {
return new uNode({
type:'element',
children:[],
tagName:html
})
}
};
uNode.createText = function (data,noTrans) {
return new UM.uNode({
type:'text',
'data':noTrans ? data : utils.unhtml(data || '')
})
};
function nodeToHtml(node, arr, formatter, current) {
switch (node.type) {
case 'root':
for (var i = 0, ci; ci = node.children[i++];) {
//插入新行
if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
insertLine(arr, current, true);
insertIndent(arr, current)
}
nodeToHtml(ci, arr, formatter, current)
}
break;
case 'text':
isText(node, arr);
break;
case 'element':
isElement(node, arr, formatter, current);
break;
case 'comment':
isComment(node, arr, formatter);
}
return arr;
}
function isText(node, arr) {
if(node.parentNode.tagName == 'pre'){
//源码模式下输入html标签,不能做转换处理,直接输出
arr.push(node.data)
}else{
arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,' '))
}
}
function isElement(node, arr, formatter, current) {
var attrhtml = '';
if (node.attrs) {
attrhtml = [];
var attrs = node.attrs;
for (var a in attrs) {
//这里就针对
//'
//这里边的\"做转换,要不用innerHTML直接被截断了,属性src
//有可能做的不够
attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
return '"'
}) : utils.unhtml(attrs[a])) + '"' : ''))
}
attrhtml = attrhtml.join(' ');
}
arr.push('<' + node.tagName +
(attrhtml ? ' ' + attrhtml : '') +
(dtd.$empty[node.tagName] ? '\/' : '' ) + '>'
);
//插入新行
if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
if(node.children && node.children.length){
current = insertLine(arr, current, true);
insertIndent(arr, current)
}
}
if (node.children && node.children.length) {
for (var i = 0, ci; ci = node.children[i++];) {
if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
insertLine(arr, current);
insertIndent(arr, current)
}
nodeToHtml(ci, arr, formatter, current)
}
}
if (!dtd.$empty[node.tagName]) {
if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
if(node.children && node.children.length){
current = insertLine(arr, current);
insertIndent(arr, current)
}
}
arr.push('<\/' + node.tagName + '>');
}
}
function isComment(node, arr) {
arr.push('');
}
function getNodeById(root, id) {
var node;
if (root.type == 'element' && root.getAttr('id') == id) {
return root;
}
if (root.children && root.children.length) {
for (var i = 0, ci; ci = root.children[i++];) {
if (node = getNodeById(ci, id)) {
return node;
}
}
}
}
function getNodesByTagName(node, tagName, arr) {
if (node.type == 'element' && node.tagName == tagName) {
arr.push(node);
}
if (node.children && node.children.length) {
for (var i = 0, ci; ci = node.children[i++];) {
getNodesByTagName(ci, tagName, arr)
}
}
}
function nodeTraversal(root,fn){
if(root.children && root.children.length){
for(var i= 0,ci;ci=root.children[i];){
nodeTraversal(ci,fn);
//ci被替换的情况,这里就不再走 fn了
if(ci.parentNode ){
if(ci.children && ci.children.length){
fn(ci)
}
if(ci.parentNode) i++
}
}
}else{
fn(root)
}
}
uNode.prototype = {
/**
* 当前节点对象,转换成html文本
* @method toHtml
* @return { String } 返回转换后的html字符串
* @example
* ```javascript
* node.toHtml();
* ```
*/
/**
* 当前节点对象,转换成html文本
* @method toHtml
* @param { Boolean } formatter 是否格式化返回值
* @return { String } 返回转换后的html字符串
* @example
* ```javascript
* node.toHtml( true );
* ```
*/
toHtml:function (formatter) {
var arr = [];
nodeToHtml(this, arr, formatter, 0);
return arr.join('')
},
/**
* 获取节点的html内容
* @method innerHTML
* @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
* @return { String } 返回节点的html内容
* @example
* ```javascript
* var htmlstr = node.innerHTML();
* ```
*/
/**
* 设置节点的html内容
* @method innerHTML
* @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
* @param { String } htmlstr 传入要设置的html内容
* @return { UM.uNode } 返回节点本身
* @example
* ```javascript
* node.innerHTML('text');
* ```
*/
innerHTML:function (htmlstr) {
if (this.type != 'element' || dtd.$empty[this.tagName]) {
return this;
}
if (utils.isString(htmlstr)) {
if(this.children){
for (var i = 0, ci; ci = this.children[i++];) {
ci.parentNode = null;
}
}
this.children = [];
var tmpRoot = UM.htmlparser(htmlstr);
for (var i = 0, ci; ci = tmpRoot.children[i++];) {
this.children.push(ci);
ci.parentNode = this;
}
return this;
} else {
var tmpRoot = new UM.uNode({
type:'root',
children:this.children
});
return tmpRoot.toHtml();
}
},
/**
* 获取节点的纯文本内容
* @method innerText
* @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
* @return { String } 返回节点的存文本内容
* @example
* ```javascript
* var textStr = node.innerText();
* ```
*/
/**
* 设置节点的纯文本内容
* @method innerText
* @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
* @param { String } textStr 传入要设置的文本内容
* @return { UM.uNode } 返回节点本身
* @example
* ```javascript
* node.innerText('text');
* ```
*/
innerText:function (textStr,noTrans) {
if (this.type != 'element' || dtd.$empty[this.tagName]) {
return this;
}
if (textStr) {
if(this.children){
for (var i = 0, ci; ci = this.children[i++];) {
ci.parentNode = null;
}
}
this.children = [];
this.appendChild(uNode.createText(textStr,noTrans));
return this;
} else {
return this.toHtml().replace(/<[^>]+>/g, '');
}
},
/**
* 获取当前对象的data属性
* @method getData
* @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
* @example
* ```javascript
* node.getData();
* ```
*/
getData:function () {
if (this.type == 'element')
return '';
return this.data
},
/**
* 获取当前节点下的第一个子节点
* @method firstChild
* @return { UM.uNode } 返回第一个子节点
* @example
* ```javascript
* node.firstChild(); //返回第一个子节点
* ```
*/
firstChild:function () {
// if (this.type != 'element' || dtd.$empty[this.tagName]) {
// return this;
// }
return this.children ? this.children[0] : null;
},
/**
* 获取当前节点下的最后一个子节点
* @method lastChild
* @return { UM.uNode } 返回最后一个子节点
* @example
* ```javascript
* node.lastChild(); //返回最后一个子节点
* ```
*/
lastChild:function () {
// if (this.type != 'element' || dtd.$empty[this.tagName] ) {
// return this;
// }
return this.children ? this.children[this.children.length - 1] : null;
},
/**
* 获取和当前节点有相同父亲节点的前一个节点
* @method previousSibling
* @return { UM.uNode } 返回前一个节点
* @example
* ```javascript
* node.children[2].previousSibling(); //返回子节点node.children[1]
* ```
*/
previousSibling : function(){
var parent = this.parentNode;
for (var i = 0, ci; ci = parent.children[i]; i++) {
if (ci === this) {
return i == 0 ? null : parent.children[i-1];
}
}
},
/**
* 获取和当前节点有相同父亲节点的后一个节点
* @method nextSibling
* @return { UM.uNode } 返回后一个节点,找不到返回null
* @example
* ```javascript
* node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
* ```
*/
nextSibling : function(){
var parent = this.parentNode;
for (var i = 0, ci; ci = parent.children[i++];) {
if (ci === this) {
return parent.children[i];
}
}
},
/**
* 用新的节点替换当前节点
* @method replaceChild
* @param { UM.uNode } target 要替换成该节点参数
* @param { UM.uNode } source 要被替换掉的节点
* @return { UM.uNode } 返回替换之后的节点对象
* @example
* ```javascript
* node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
* ```
*/
replaceChild:function (target, source) {
if (this.children) {
if(target.parentNode){
target.parentNode.removeChild(target);
}
for (var i = 0, ci; ci = this.children[i]; i++) {
if (ci === source) {
this.children.splice(i, 1, target);
source.parentNode = null;
target.parentNode = this;
return target;
}
}
}
},
/**
* 在节点的子节点列表最后位置插入一个节点
* @method appendChild
* @param { UM.uNode } node 要插入的节点
* @return { UM.uNode } 返回刚插入的子节点
* @example
* ```javascript
* node.appendChild( newNode ); //在node内插入子节点newNode
* ```
*/
appendChild:function (node) {
if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
if (!this.children) {
this.children = []
}
if(node.parentNode){
node.parentNode.removeChild(node);
}
for (var i = 0, ci; ci = this.children[i]; i++) {
if (ci === node) {
this.children.splice(i, 1);
break;
}
}
this.children.push(node);
node.parentNode = this;
return node;
}
},
/**
* 在传入节点的前面插入一个节点
* @method insertBefore
* @param { UM.uNode } target 要插入的节点
* @param { UM.uNode } source 在该参数节点前面插入
* @return { UM.uNode } 返回刚插入的子节点
* @example
* ```javascript
* node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
* ```
*/
insertBefore:function (target, source) {
if (this.children) {
if(target.parentNode){
target.parentNode.removeChild(target);
}
for (var i = 0, ci; ci = this.children[i]; i++) {
if (ci === source) {
this.children.splice(i, 0, target);
target.parentNode = this;
return target;
}
}
}
},
/**
* 在传入节点的后面插入一个节点
* @method insertAfter
* @param { UM.uNode } target 要插入的节点
* @param { UM.uNode } source 在该参数节点后面插入
* @return { UM.uNode } 返回刚插入的子节点
* @example
* ```javascript
* node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
* ```
*/
insertAfter:function (target, source) {
if (this.children) {
if(target.parentNode){
target.parentNode.removeChild(target);
}
for (var i = 0, ci; ci = this.children[i]; i++) {
if (ci === source) {
this.children.splice(i + 1, 0, target);
target.parentNode = this;
return target;
}
}
}
},
/**
* 从当前节点的子节点列表中,移除节点
* @method removeChild
* @param { UM.uNode } node 要移除的节点引用
* @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
* @return { * } 返回刚移除的子节点
* @example
* ```javascript
* node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
* ```
*/
removeChild:function (node,keepChildren) {
if (this.children) {
for (var i = 0, ci; ci = this.children[i]; i++) {
if (ci === node) {
this.children.splice(i, 1);
ci.parentNode = null;
if(keepChildren && ci.children && ci.children.length){
for(var j= 0,cj;cj=ci.children[j];j++){
this.children.splice(i+j,0,cj);
cj.parentNode = this;
}
}
return ci;
}
}
}
},
/**
* 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
* @method getAttr
* @param { String } attrName 要获取的属性名称
* @return { * } 返回attrs对象下的属性值
* @example
* ```javascript
* node.getAttr('title');
* ```
*/
getAttr:function (attrName) {
return this.attrs && this.attrs[attrName.toLowerCase()]
},
/**
* 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
* @method setAttr
* @param { String } attrName 要设置的属性名称
* @param { * } attrVal 要设置的属性值,类型视设置的属性而定
* @return { * } 返回attrs对象下的属性值
* @example
* ```javascript
* node.setAttr('title','标题');
* ```
*/
setAttr:function (attrName, attrVal) {
if (!attrName) {
delete this.attrs;
return;
}
if(!this.attrs){
this.attrs = {};
}
if (utils.isObject(attrName)) {
for (var a in attrName) {
if (!attrName[a]) {
delete this.attrs[a]
} else {
this.attrs[a.toLowerCase()] = attrName[a];
}
}
} else {
if (!attrVal) {
delete this.attrs[attrName]
} else {
this.attrs[attrName.toLowerCase()] = attrVal;
}
}
},
hasAttr: function( attrName ){
var attrVal = this.getAttr( attrName );
return ( attrVal !== null ) && ( attrVal !== undefined );
},
/**
* 获取当前节点在父节点下的位置索引
* @method getIndex
* @return { Number } 返回索引数值,如果没有父节点,返回-1
* @example
* ```javascript
* node.getIndex();
* ```
*/
getIndex:function(){
var parent = this.parentNode;
for(var i= 0,ci;ci=parent.children[i];i++){
if(ci === this){
return i;
}
}
return -1;
},
/**
* 在当前节点下,根据id查找节点
* @method getNodeById
* @param { String } id 要查找的id
* @return { UM.uNode } 返回找到的节点
* @example
* ```javascript
* node.getNodeById('textId');
* ```
*/
getNodeById:function (id) {
var node;
if (this.children && this.children.length) {
for (var i = 0, ci; ci = this.children[i++];) {
if (node = getNodeById(ci, id)) {
return node;
}
}
}
},
/**
* 在当前节点下,根据元素名称查找节点列表
* @method getNodesByTagName
* @param { String } tagNames 要查找的元素名称
* @return { Array } 返回找到的节点列表
* @example
* ```javascript
* node.getNodesByTagName('span');
* ```
*/
getNodesByTagName:function (tagNames) {
tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' ');
var arr = [], me = this;
utils.each(tagNames, function (tagName) {
if (me.children && me.children.length) {
for (var i = 0, ci; ci = me.children[i++];) {
getNodesByTagName(ci, tagName, arr)
}
}
});
return arr;
},
/**
* 根据样式名称,获取节点的样式值
* @method getStyle
* @param { String } name 要获取的样式名称
* @return { String } 返回样式值
* @example
* ```javascript
* node.getStyle('font-size');
* ```
*/
getStyle:function (name) {
var cssStyle = this.getAttr('style');
if (!cssStyle) {
return ''
}
var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i');
var match = cssStyle.match(reg);
if (match && match[0]) {
return match[2]
}
return '';
},
/**
* 给节点设置样式
* @method setStyle
* @param { String } name 要设置的的样式名称
* @param { String } val 要设置的的样值
* @example
* ```javascript
* node.setStyle('font-size', '12px');
* ```
*/
setStyle:function (name, val) {
function exec(name, val) {
var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi');
cssStyle = cssStyle.replace(reg, '$1');
if (val) {
cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
}
}
var cssStyle = this.getAttr('style');
if (!cssStyle) {
cssStyle = '';
}
if (utils.isObject(name)) {
for (var a in name) {
exec(a, name[a])
}
} else {
exec(name, val)
}
this.setAttr('style', utils.trim(cssStyle))
},
hasClass: function( className ){
if( this.hasAttr('class') ) {
var classNames = this.getAttr('class').split(/\s+/),
hasClass = false;
$.each(classNames, function(key, item){
if( item === className ) {
hasClass = true;
}
});
return hasClass;
} else {
return false;
}
},
addClass: function( className ){
var classes = null,
hasClass = false;
if( this.hasAttr('class') ) {
classes = this.getAttr('class');
classes = classes.split(/\s+/);
classes.forEach( function( item ){
if( item===className ) {
hasClass = true;
return;
}
} );
!hasClass && classes.push( className );
this.setAttr('class', classes.join(" "));
} else {
this.setAttr('class', className);
}
},
removeClass: function( className ){
if( this.hasAttr('class') ) {
var cl = this.getAttr('class');
cl = cl.replace(new RegExp('\\b' + className + '\\b', 'g'),'');
this.setAttr('class', utils.trim(cl).replace(/[ ]{2,}/g,' '));
}
},
/**
* 传入一个函数,递归遍历当前节点下的所有节点
* @method traversal
* @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
* @example
* ```javascript
* traversal(node, function(){
* console.log(node.type);
* });
* ```
*/
traversal:function(fn){
if(this.children && this.children.length){
nodeTraversal(this,fn);
}
return this;
}
}
})();
//html字符串转换成uNode节点
//by zhanyi
var htmlparser = UM.htmlparser = function (htmlstr,ignoreBlank) {
//todo 原来的方式 [^"'<>\/] 有\/就不能配对上 这样的标签了
//先去掉了,加上的原因忘了,这里先记录
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;
}
//如果结束是文本,就有可能丢掉,所以这里手动判断一下
//例如 sdfsdfsdf sdfsdfsdfsdf
if (nextIndex < htmlstr.length) {
text(currentParent, htmlstr.slice(nextIndex));
}
return root;
};
/**
* @file
* @name UM.filterNode
* @short filterNode
* @desc 根据给定的规则过滤节点
* @import editor.js,core/utils.js
* @anthor zhanyi
*/
var filterNode = UM.filterNode = function () {
function filterNode(node,rules){
switch (node.type) {
case 'text':
break;
case 'element':
var val;
if(val = rules[node.tagName]){
if(val === '-'){
node.parentNode.removeChild(node)
}else if(utils.isFunction(val)){
var parentNode = node.parentNode,
index = node.getIndex();
val(node);
if(node.parentNode){
if(node.children){
for(var i = 0,ci;ci=node.children[i];){
filterNode(ci,rules);
if(ci.parentNode){
i++;
}
}
}
}else{
for(var i = index,ci;ci=parentNode.children[i];){
filterNode(ci,rules);
if(ci.parentNode){
i++;
}
}
}
}else{
var attrs = val['$'];
if(attrs && node.attrs){
var tmpAttrs = {},tmpVal;
for(var a in attrs){
tmpVal = node.getAttr(a);
//todo 只先对style单独处理
if(a == 'style' && utils.isArray(attrs[a])){
var tmpCssStyle = [];
utils.each(attrs[a],function(v){
var tmp;
if(tmp = node.getStyle(v)){
tmpCssStyle.push(v + ':' + tmp);
}
});
tmpVal = tmpCssStyle.join(';')
}
if(tmpVal){
tmpAttrs[a] = tmpVal;
}
}
node.attrs = tmpAttrs;
}
if(node.children){
for(var i = 0,ci;ci=node.children[i];){
filterNode(ci,rules);
if(ci.parentNode){
i++;
}
}
}
}
}else{
//如果不在名单里扣出子节点并删除该节点,cdata除外
if(dtd.$cdata[node.tagName]){
node.parentNode.removeChild(node)
}else{
var parentNode = node.parentNode,
index = node.getIndex();
node.parentNode.removeChild(node,true);
for(var i = index,ci;ci=parentNode.children[i];){
filterNode(ci,rules);
if(ci.parentNode){
i++;
}
}
}
}
break;
case 'comment':
node.parentNode.removeChild(node)
}
}
return function(root,rules){
if(utils.isEmptyObject(rules)){
return root;
}
var val;
if(val = rules['-']){
utils.each(val.split(' '),function(k){
rules[k] = '-'
})
}
for(var i= 0,ci;ci=root.children[i];){
filterNode(ci,rules);
if(ci.parentNode){
i++;
}
}
return root;
}
}();
///import core
/**
* @description 插入内容
* @name baidu.editor.execCommand
* @param {String} cmdName inserthtml插入内容的命令
* @param {String} html 要插入的内容
* @author zhanyi
*/
UM.commands['inserthtml'] = {
execCommand: function (command,html,notNeedFilter){
var me = this,
range,
div;
if(!html){
return;
}
if(me.fireEvent('beforeinserthtml',html) === true){
return;
}
range = me.selection.getRange();
div = range.document.createElement( 'div' );
div.style.display = 'inline';
if (!notNeedFilter) {
var root = UM.htmlparser(html);
//如果给了过滤规则就先进行过滤
if(me.options.filterRules){
UM.filterNode(root,me.options.filterRules);
}
//执行默认的处理
me.filterInputRule(root);
html = root.toHtml()
}
div.innerHTML = utils.trim( html );
if ( !range.collapsed ) {
var tmpNode = range.startContainer;
if(domUtils.isFillChar(tmpNode)){
range.setStartBefore(tmpNode)
}
tmpNode = range.endContainer;
if(domUtils.isFillChar(tmpNode)){
range.setEndAfter(tmpNode)
}
range.txtToElmBoundary();
//结束边界可能放到了br的前边,要把br包含进来
// x[xxx]
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 ? '' : '
')+'
';
range.setStart(me.body.firstChild,0).collapse(true)
}
}
}
!range.collapsed && range.deleteContents();
if(range.startContainer.nodeType == 1){
var child = range.startContainer.childNodes[range.startOffset],pre;
if(child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)){
range.setEnd(pre,pre.childNodes.length).collapse();
while(child.firstChild){
pre.appendChild(child.firstChild);
}
domUtils.remove(child);
}
}
}
var child,parent,pre,tmp,hadBreak = 0, nextNode;
//如果当前位置选中了fillchar要干掉,要不会产生空行
if(range.inFillChar()){
child = range.startContainer;
if(domUtils.isFillChar(child)){
range.setStartBefore(child).collapse(true);
domUtils.remove(child);
}else if(domUtils.isFillChar(child,true)){
child.nodeValue = child.nodeValue.replace(fillCharReg,'');
range.startOffset--;
range.collapsed && range.collapse(true)
}
}
while ( child = div.firstChild ) {
if(hadBreak){
var p = me.document.createElement('p');
while(child && (child.nodeType == 3 || !dtd.$block[child.tagName])){
nextNode = child.nextSibling;
p.appendChild(child);
child = nextNode;
}
if(p.firstChild){
child = p
}
}
range.insertNode( child );
nextNode = child.nextSibling;
if ( !hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm( child ) ){
parent = domUtils.findParent( child,function ( node ){ return domUtils.isBlockElm( node ); } );
if ( parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)){
if(!dtd[parent.tagName][child.nodeName]){
pre = parent;
}else{
tmp = child.parentNode;
while (tmp !== parent){
pre = tmp;
tmp = tmp.parentNode;
}
}
domUtils.breakParent( child, pre || tmp );
//去掉break后前一个多余的节点 |<[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];
ci.src=ci.src.replace("http://"+window.location.host,"").replace("https://"+window.location.host,"");
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 ? "" : "
")+"
";
range.setStart(me.body.firstChild,0).setCursor(false,true);
setTimeout(function(){
me.fireEvent("clearDoc");
},0);
}
};
///import core
///commands 撤销和重做
///commandsName Undo,Redo
///commandsTitle 撤销,重做
/**
* @description 回退
* @author zhanyi
*/
UM.plugins['undo'] = function () {
var saveSceneTimer;
var me = this,
maxUndoCount = me.options.maxUndoCount || 20,
maxInputCount = me.options.maxInputCount || 20,
fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的
var noNeedFillCharTags = {
ol:1,ul:1,table:1,tbody:1,tr:1,body:1
};
var orgState = me.options.autoClearEmptyNode;
function compareAddr(indexA, indexB) {
if (indexA.length != indexB.length)
return 0;
for (var i = 0, l = indexA.length; i < l; i++) {
if (indexA[i] != indexB[i])
return 0
}
return 1;
}
function compareRangeAddress(rngAddrA, rngAddrB) {
if (rngAddrA.collapsed != rngAddrB.collapsed) {
return 0;
}
if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) {
return 0;
}
return 1;
}
function UndoManager() {
this.list = [];
this.index = 0;
this.hasUndo = false;
this.hasRedo = false;
this.undo = function () {
if (this.hasUndo) {
if (!this.list[this.index - 1] && this.list.length == 1) {
this.reset();
return;
}
while (this.list[this.index].content == this.list[this.index - 1].content) {
this.index--;
if (this.index == 0) {
return this.restore(0);
}
}
this.restore(--this.index);
}
};
this.redo = function () {
if (this.hasRedo) {
while (this.list[this.index].content == this.list[this.index + 1].content) {
this.index++;
if (this.index == this.list.length - 1) {
return this.restore(this.index);
}
}
this.restore(++this.index);
}
};
this.restore = function () {
var me = this.editor;
var scene = this.list[this.index];
var root = UM.htmlparser(scene.content.replace(fillchar, ''));
me.options.autoClearEmptyNode = false;
me.filterInputRule(root,true);
me.options.autoClearEmptyNode = orgState;
//trace:873
//去掉展位符
me.body.innerHTML = root.toHtml();
me.fireEvent('afterscencerestore');
//处理undo后空格不展位的问题
if (browser.ie) {
utils.each(domUtils.getElementsByTagName(me.document,'td th caption p'),function(node){
if(domUtils.isEmptyNode(node)){
domUtils.fillNode(me.document, node);
}
})
}
try{
var rng = new dom.Range(me.document,me.body).moveToAddress(scene.address);
if(browser.ie && rng.collapsed && rng.startContainer.nodeType == 1){
var tmpNode = rng.startContainer.childNodes[rng.startOffset];
if( !tmpNode || tmpNode.nodeType == 1 && dtd.$empty[tmpNode]){
rng.insertNode(me.document.createTextNode(' ')).collapse(true);
}
}
rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]);
}catch(e){}
this.update();
this.clearKey();
//不能把自己reset了
me.fireEvent('reset', true);
};
this.getScene = function () {
var me = this.editor;
var rng = me.selection.getRange(),
rngAddress = rng.createAddress(false,true);
me.fireEvent('beforegetscene');
var root = UM.htmlparser(me.body.innerHTML,true);
me.options.autoClearEmptyNode = false;
me.filterOutputRule(root,true);
me.options.autoClearEmptyNode = orgState;
var cont = root.toHtml();
browser.ie && (cont = cont.replace(/> <').replace(/\s*\s*/g, '>'));
me.fireEvent('aftergetscene');
return {
address:rngAddress,
content:cont
}
};
this.save = function (notCompareRange,notSetCursor) {
clearTimeout(saveSceneTimer);
var currentScene = this.getScene(notSetCursor),
lastScene = this.list[this.index];
//内容相同位置相同不存
if (lastScene && lastScene.content == currentScene.content &&
( notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address) )
) {
return;
}
this.list = this.list.slice(0, this.index + 1);
this.list.push(currentScene);
//如果大于最大数量了,就把最前的剔除
if (this.list.length > maxUndoCount) {
this.list.shift();
}
this.index = this.list.length - 1;
this.clearKey();
//跟新undo/redo状态
this.update();
};
this.update = function () {
this.hasRedo = !!this.list[this.index + 1];
this.hasUndo = !!this.list[this.index - 1];
};
this.reset = function () {
this.list = [];
this.index = 0;
this.hasUndo = false;
this.hasRedo = false;
this.clearKey();
};
this.clearKey = function () {
keycont = 0;
lastKeyCode = null;
};
}
me.undoManger = new UndoManager();
me.undoManger.editor = me;
function saveScene() {
this.undoManger.save();
}
me.addListener('saveScene', function () {
var args = Array.prototype.splice.call(arguments,1);
this.undoManger.save.apply(this.undoManger,args);
});
me.addListener('beforeexeccommand', saveScene);
me.addListener('afterexeccommand', saveScene);
me.addListener('reset', function (type, exclude) {
if (!exclude) {
this.undoManger.reset();
}
});
me.commands['redo'] = me.commands['undo'] = {
execCommand:function (cmdName) {
this.undoManger[cmdName]();
},
queryCommandState:function (cmdName) {
return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1;
},
notNeedUndo:1
};
var keys = {
// /*Backspace*/ 8:1, /*Delete*/ 46:1,
/*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
37:1, 38:1, 39:1, 40:1
},
keycont = 0,
lastKeyCode;
//输入法状态下不计算字符数
var inputType = false;
me.addListener('ready', function () {
$(this.body).on('compositionstart', function () {
inputType = true;
}).on('compositionend', function () {
inputType = false;
})
});
//快捷键
me.addshortcutkey({
"Undo":"ctrl+90", //undo
"Redo":"ctrl+89,shift+ctrl+z" //redo
});
var isCollapsed = true;
me.addListener('keydown', function (type, evt) {
var me = this;
var keyCode = evt.keyCode || evt.which;
if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
if (inputType)
return;
if(!me.selection.getRange().collapsed){
me.undoManger.save(false,true);
isCollapsed = false;
return;
}
if (me.undoManger.list.length == 0) {
me.undoManger.save(true);
}
clearTimeout(saveSceneTimer);
function save(cont){
if (cont.selection.getRange().collapsed)
cont.fireEvent('contentchange');
cont.undoManger.save(false,true);
cont.fireEvent('selectionchange');
}
saveSceneTimer = setTimeout(function(){
if(inputType){
var interalTimer = setInterval(function(){
if(!inputType){
save(me);
clearInterval(interalTimer)
}
},300)
return;
}
save(me);
},200);
lastKeyCode = keyCode;
keycont++;
if (keycont >= maxInputCount ) {
save(me)
}
}
});
me.addListener('keyup', function (type, evt) {
var keyCode = evt.keyCode || evt.which;
if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
if (inputType)
return;
if(!isCollapsed){
this.undoManger.save(false,true);
isCollapsed = true;
}
}
});
};
///import core
///import plugins/inserthtml.js
///import plugins/undo.js
///import plugins/serialize.js
///commands 粘贴
///commandsName PastePlain
///commandsTitle 纯文本粘贴模式
/**
* @description 粘贴
* @author zhanyi
*/
UM.plugins['paste'] = function () {
function getClipboardData(callback) {
var doc = this.document;
if (doc.getElementById('baidu_pastebin')) {
return;
}
var range = this.selection.getRange(),
bk = range.createBookmark(),
//创建剪贴的容器div
pastebin = doc.createElement('div');
pastebin.id = 'baidu_pastebin';
// Safari 要求div必须有内容,才能粘贴内容进来
browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar));
this.body.appendChild(pastebin);
//trace:717 隐藏的span不能得到top
//bk.start.innerHTML = ' ';
bk.start.style.display = '';
pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
//要在现在光标平行的位置加入,否则会出现跳动的问题
$(bk.start).position().top + 'px';
range.selectNodeContents(pastebin).select(true);
setTimeout(function () {
if (browser.webkit) {
for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) {
if (domUtils.isEmptyNode(pi)) {
domUtils.remove(pi);
} else {
pastebin = pi;
break;
}
}
}
try {
pastebin.parentNode.removeChild(pastebin);
} catch (e) {
}
range.moveToBookmark(bk).select(true);
callback(pastebin);
}, 0);
}
var me = this;
function filter(div) {
var html;
if (div.firstChild) {
//去掉cut中添加的边界值
var nodes = domUtils.getElementsByTagName(div, 'span');
for (var i = 0, ni; ni = nodes[i++];) {
if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
domUtils.remove(ni);
}
}
if (browser.webkit) {
var brs = div.querySelectorAll('div br');
for (var i = 0, bi; bi = brs[i++];) {
var pN = bi.parentNode;
if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
pN.innerHTML = '
';
domUtils.remove(pN);
}
}
var divs = div.querySelectorAll('#baidu_pastebin');
for (var i = 0, di; di = divs[i++];) {
var tmpP = me.document.createElement('p');
di.parentNode.insertBefore(tmpP, di);
while (di.firstChild) {
tmpP.appendChild(di.firstChild);
}
domUtils.remove(di);
}
var metas = div.querySelectorAll('meta');
for (var i = 0, ci; ci = metas[i++];) {
domUtils.remove(ci);
}
var brs = div.querySelectorAll('br');
for (i = 0; ci = brs[i++];) {
if (/^apple-/i.test(ci.className)) {
domUtils.remove(ci);
}
}
}
if (browser.gecko) {
var dirtyNodes = div.querySelectorAll('[_moz_dirty]');
for (i = 0; ci = dirtyNodes[i++];) {
ci.removeAttribute('_moz_dirty');
}
}
if (!browser.ie) {
var spans = div.querySelectorAll('span.Apple-style-span');
for (var i = 0, ci; ci = spans[i++];) {
domUtils.remove(ci, true);
}
}
//ie下使用innerHTML会产生多余的\r\n字符,也会产生 这里过滤掉
html = div.innerHTML;//.replace(/>(?:(\s| )*?)<');
//过滤word粘贴过来的冗余属性
html = UM.filterWord(html);
//取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
var root = UM.htmlparser(html);
//如果给了过滤规则就先进行过滤
if (me.options.filterRules) {
UM.filterNode(root, me.options.filterRules);
}
//执行默认的处理
me.filterInputRule(root);
//针对chrome的处理
if (browser.webkit) {
var br = root.lastChild();
if (br && br.type == 'element' && br.tagName == 'br') {
root.removeChild(br)
}
utils.each(me.body.querySelectorAll('div'), function (node) {
if (domUtils.isEmptyBlock(node)) {
domUtils.remove(node)
}
})
}
html = {'html': root.toHtml()};
me.fireEvent('beforepaste', html, root);
//抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
if(!html.html){
return;
}
me.execCommand('insertHtml', html.html, true);
me.fireEvent("afterpaste", html);
}
}
me.addListener('ready', function () {
$(me.body).on( 'cut', function () {
var range = me.selection.getRange();
if (!range.collapsed && me.undoManger) {
me.undoManger.save();
}
}).on(browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
//ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
return;
}
getClipboardData.call(me, function (div) {
filter(div);
});
});
});
};
///import core
///commands 有序列表,无序列表
///commandsName InsertOrderedList,InsertUnorderedList
///commandsTitle 有序列表,无序列表
/**
* 有序列表
* @function
* @name UM.execCommand
* @param {String} cmdName insertorderlist插入有序列表
* @param {String} style 值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman
* @author zhanyi
*/
/**
* 无序链接
* @function
* @name UM.execCommand
* @param {String} cmdName insertunorderlist插入无序列表
* * @param {String} style 值为:circle,disc,square
* @author zhanyi
*/
UM.plugins['list'] = function () {
var me = this;
me.setOpt( {
'insertorderedlist':{
'decimal':'',
'lower-alpha':'',
'lower-roman':'',
'upper-alpha':'',
'upper-roman':''
},
'insertunorderedlist':{
'circle':'',
'disc':'',
'square':''
}
} );
this.addInputRule(function(root){
utils.each(root.getNodesByTagName('li'), function (node) {
if(node.children.length == 0){
node.parentNode.removeChild(node);
}
})
});
me.commands['insertorderedlist'] =
me.commands['insertunorderedlist'] = {
execCommand:function (cmdName) {
this.document.execCommand(cmdName);
var rng = this.selection.getRange(),
bk = rng.createBookmark(true);
this.$body.find('ol,ul').each(function(i,n){
var parent = n.parentNode;
if(parent.tagName == 'P' && parent.lastChild === parent.firstChild){
$(n).children().each(function(j,li){
var p = parent.cloneNode(false);
$(p).append(li.innerHTML);
$(li).html('').append(p);
});
$(n).insertBefore(parent);
$(parent).remove();
}
if(dtd.$inline[parent.tagName]){
if(parent.tagName == 'SPAN'){
$(n).children().each(function(k,li){
var span = parent.cloneNode(false);
if(li.firstChild.nodeName != 'P'){
while(li.firstChild){
span.appendChild(li.firstChild)
};
$('').appendTo(li).append(span);
}else{
while(li.firstChild){
span.appendChild(li.firstChild)
};
$(li.firstChild).append(span);
}
})
}
domUtils.remove(parent,true)
}
});
rng.moveToBookmark(bk).select();
return true;
},
queryCommandState:function (cmdName) {
return this.document.queryCommandState(cmdName);
}
};
};
///import core
///import plugins/serialize.js
///import plugins/undo.js
///commands 查看源码
///commandsName Source
///commandsTitle 查看源码
(function (){
var sourceEditors = {
textarea: function (editor, holder){
var textarea = holder.ownerDocument.createElement('textarea');
textarea.style.cssText = 'resize:none;border:0;padding:0;margin:0;overflow-y:auto;outline:0';
// todo: IE下只有onresize属性可用... 很纠结
if (browser.ie && browser.version < 8) {
textarea.style.width = holder.offsetWidth + 'px';
textarea.style.height = holder.offsetHeight + 'px';
holder.onresize = function (){
textarea.style.width = holder.offsetWidth + 'px';
textarea.style.height = holder.offsetHeight + 'px';
};
}
holder.appendChild(textarea);
return {
container : textarea,
setContent: function (content){
textarea.value = content;
},
getContent: function (){
return textarea.value;
},
select: function (){
var range;
if (browser.ie) {
range = textarea.createTextRange();
range.collapse(true);
range.select();
} else {
//todo: chrome下无法设置焦点
textarea.setSelectionRange(0, 0);
textarea.focus();
}
},
dispose: function (){
holder.removeChild(textarea);
// todo
holder.onresize = null;
textarea = null;
holder = null;
}
};
}
};
UM.plugins['source'] = function (){
var me = this;
var opt = this.options;
var sourceMode = false;
var sourceEditor;
opt.sourceEditor = 'textarea';
me.setOpt({
sourceEditorFirst:false
});
function createSourceEditor(holder){
return sourceEditors.textarea(me, holder);
}
var bakCssText;
//解决在源码模式下getContent不能得到最新的内容问题
var oldGetContent = me.getContent,
bakAddress;
me.commands['source'] = {
execCommand: function (){
sourceMode = !sourceMode;
if (sourceMode) {
bakAddress = me.selection.getRange().createAddress(false,true);
me.undoManger && me.undoManger.save(true);
if(browser.gecko){
me.body.contentEditable = false;
}
// bakCssText = me.body.style.cssText;
me.body.style.cssText += ';position:absolute;left:-32768px;top:-32768px;';
me.fireEvent('beforegetcontent');
var root = UM.htmlparser(me.body.innerHTML);
me.filterOutputRule(root);
root.traversal(function (node) {
if (node.type == 'element') {
switch (node.tagName) {
case 'td':
case 'th':
case 'caption':
if(node.children && node.children.length == 1){
if(node.firstChild().tagName == 'br' ){
node.removeChild(node.firstChild())
}
};
break;
case 'pre':
node.innerText(node.innerText().replace(/ /g,' '))
}
}
});
me.fireEvent('aftergetcontent');
var content = root.toHtml(true);
sourceEditor = createSourceEditor(me.body.parentNode);
sourceEditor.setContent(content);
var getStyleValue=function(attr){
return parseInt($(me.body).css(attr));
};
$(sourceEditor.container).width($(me.body).width()+getStyleValue("padding-left")+getStyleValue("padding-right"))
.height($(me.body).height());
setTimeout(function (){
sourceEditor.select();
});
//重置getContent,源码模式下取值也能是最新的数据
me.getContent = function (){
return sourceEditor.getContent() || '' + (browser.ie ? '' : '
')+'
';
};
} else {
me.$body.css({
'position':'',
'left':'',
'top':''
});
// me.body.style.cssText = bakCssText;
var cont = sourceEditor.getContent() || '' + (browser.ie ? '' : '
')+'
';
//处理掉block节点前后的空格,有可能会误命中,暂时不考虑
cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>','g'), function(a,b){
if(b && !dtd.$inlineWithA[b.toLowerCase()]){
return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g,'');
}
return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g,'')
});
me.setContent(cont);
sourceEditor.dispose();
sourceEditor = null;
//还原getContent方法
me.getContent = oldGetContent;
var first = me.body.firstChild;
//trace:1106 都删除空了,下边会报错,所以补充一个p占位
if(!first){
me.body.innerHTML = ''+(browser.ie?'':'
')+'
';
}
//要在ifm为显示时ff才能取到selection,否则报错
//这里不能比较位置了
me.undoManger && me.undoManger.save(true);
if(browser.gecko){
me.body.contentEditable = true;
}
try{
me.selection.getRange().moveToAddress(bakAddress).select();
}catch(e){}
}
this.fireEvent('sourcemodechanged', sourceMode);
},
queryCommandState: function (){
return sourceMode|0;
},
notNeedUndo : 1
};
var oldQueryCommandState = me.queryCommandState;
me.queryCommandState = function (cmdName){
cmdName = cmdName.toLowerCase();
if (sourceMode) {
//源码模式下可以开启的命令
return cmdName in {
'source' : 1,
'fullscreen' : 1
} ? oldQueryCommandState.apply(this, arguments) : -1
}
return oldQueryCommandState.apply(this, arguments);
};
};
})();
///import core
///import plugins/undo.js
///commands 设置回车标签p或br
///commandsName EnterKey
///commandsTitle 设置回车标签p或br
/**
* @description 处理回车
* @author zhanyi
*/
UM.plugins['enterkey'] = function() {
var hTag,
me = this,
tag = me.options.enterTag;
me.addListener('keyup', function(type, evt) {
var keyCode = evt.keyCode || evt.which;
if (keyCode == 13) {
var range = me.selection.getRange(),
start = range.startContainer,
doSave;
//修正在h1-h6里边回车后不能嵌套p的问题
if (!browser.ie) {
if (/h\d/i.test(hTag)) {
if (browser.gecko) {
var h = domUtils.findParentByTagName(start, [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption','table'], true);
if (!h) {
me.document.execCommand('formatBlock', false, '');
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 ?
''
:
'