Basf_EProject/EProject/FineUIPro.Web/res/third-party/umeditor/umeditor.js

10923 lines
379 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* UEditor Mini版本
* version: 1.2.2
* build: Wed Mar 19 2014 17:08:14 GMT+0800 (中国标准时间)
*/
(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
* <ul>
* <li>IE系列返回值为5,6,7,8,9,10等</li>
* <li>gecko系列会返回10900158900等</li>
* <li>webkit系列会返回其build号 (如 522等)</li>
* </ul>
* @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 = '<body>You say:"你好Baidu & UEditor!"</body>';
* UM.utils.unhtml(html); ==> &lt;body&gt;You say:&quot;你好Baidu &amp; UEditor!&quot;&lt;/body&gt;
* UM.utils.unhtml(html,/[<>]/g) ==> &lt;body&gt;You say:"你好Baidu & UEditor!"&lt;/body&gt;
*/
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 {
'<':'&lt;',
'&':'&amp;',
'"':'&quot;',
'>':'&gt;',
"'":'&#39;'
}[a]
}
}) : '';
},
/**
* 将str中的转义字符还原成html字符
* @name html
* @grammar UM.utils.html(str) => String //详细参见<code><a href = '#unhtml'>unhtml</a></code>
*/
html:function (str) {
return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) {
return {
'&lt;':'<',
'&amp;':'&',
'&quot;':'"',
'&gt;':'>',
'&#39;':"'"
}[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
* <body>
* <div id="test">
* <span></span>
* </div>
* <i>xxx</i>
* </body>
* <script>
*
* //output: i节点
* console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
*
* </script>
* ```
* @example
* ```html
* <body>
* <div>
* <span></span>
* <i id="test">xxx</i>
* </div>
* <b>xxx</b>
* </body>
* <script>
*
* //由于id为test的i节点之后没有兄弟节点 则查找其父节点div后面的兄弟节点
* //output: b节点
* console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
*
* </script>
* ```
*/
/**
* 取得node节点的下一个兄弟节点 如果startFromChild的值为ture则先获取其子节点
* 如果有子节点则直接返回第一个子节点如果没有子节点或者startFromChild的值为false
* 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
* @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
* <body>
* <div></div>
* <span id="test"></span>
* <i></i>
* <b></b>
* <em>xxx</em>
* <span></span>
* </body>
* <script>
*
* UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
*
* //output: <div></div><span id="test"></span><em>xxx</em><span></span>
* console.log( document.body.innerHTML );
*
* </script>
* ```
*/
/**
* 清除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
* <b><i></i></b> => 1
* <b><i></i><u></u></b> => 1
* <b></b> => 1
* <b>xx<i></i></b> => 0
*/
isEmptyInlineElement: function (node) {
if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
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上的属性attrNamesattrNames为属性名称数组
* @name removeAttributes
* @grammar UM.dom.domUtils.removeAttributes(node,attrNames)
* @example
* //Before remove
* <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
* //Remove
* UM.dom.domUtils.removeAttributes(node,["id","name"]);
* //After remove
* <span style="font-size:14px;">xxxxx</span>
*/
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添加属性attrsattrs为属性键值对
* @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
* <span id="test" style="color: red; background: blue;"></span>
*
* <script>
*
* var testNode = document.getElementById("test");
*
* UE.dom.domUtils.removeStyle( testNode, 'color' );
*
* //output: background: blue;
* console.log( testNode.style.cssText );
*
* </script>
* ```
*/
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';
},
isFillChar: function (node, isInStart) {
return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length
},
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 :
* <b>x<i>x[x<i>xx]x</b>
* //执行方法后
* <b>x<i>x<i>|x</b>
* 注意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
* <b>xx<i>x|x</i>x</b>
* 执行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
* <b>xx<i>x|x</i>x</b>
* 执行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
* <b>xx<i>x|x</i>x</b>
* 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
* <b>xx<i>x|x</i>x</b>
* 执行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
* <b>xx[x<i>xxx</i>]xxx</b>
* 执行后
* <b>[xxx<i>xxx</i>xxx]</b>
* 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开始位置和结束位置都调整参见<code><a href="#adjustmentboundary">adjustmentBoundary</a></code>
* @grammar range.shrinkBoundary(true) => Range //仅调整开始位置,忽略结束位置
* @example
* <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
* <b>x[xx</b><i>]xxx</i> ==> <b>x[xx]</b><i>xxx</i>
* [<b><i>xxxx</i>xxxxxxx</b>] ==> <b><i>[xxxx</i>xxxxxxx]</b>
*/
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 :
* <b>|xxx</b>
* startContainer = xxx; startOffset = 0
* //执行后本方法后
* startContainer = <b>; startOffset = 0
* @example
* Dom Element :
* <b>xx|x</b>
* startContainer = xxx; startOffset = 2
* //执行本方法后xxx被实实在在地切分成两个TextNode
* startContainer = <b>; 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 :
* <b> |xxx</b>
* startContainer = xxx; startOffset = 0
* //本方法执行后
* startContainer = <b>; startOffset = 0
* @example
* Dom Element :
* <b> xxx| </b>
* startContainer = xxx; startOffset = 3
* //本方法执行后
* startContainer = <b>; 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;
},
/**
* 在当前选区的开始位置前插入一个节点或者fragmentrange的开始位置会在插入节点的前边
* @name insertNode
* @grammar range.insertNode(node) => Range //node可以是textNode,elementNode,fragment
* @example
* Range :
* xxx[x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
* 待插入Node :
* <p>ssss</p>
* 执行本方法后的Range :
* xxx[<p>ssss</p>x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
*/
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 //参见<code><a href="#shrinkboundary">shrinkBoundary</a></code>
* @example
* <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
* <b>x[xx</b><i>]xxx</i> ==> <b>x[xx</b>]<i>xxx</i>
*/
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
* <b>xxxx[<img />]xxx</b>
*/
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) {
//使用<span>|x<span>固定住光标
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);
// }
//
//处理光标落在文本节点的情况
//处理以下的情况
//<b>|xxxx</b>
//<b>xxxx</b>|xxxx
//xxxx<b>|</b>
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<l;i++){
ci = address[i];
parentNode = tmpNode;
tmpNode = tmpNode.childNodes[ci];
if(!tmpNode){
offset = ci;
break;
}
}
if(isStart){
if(tmpNode){
me.setStartBefore(tmpNode)
}else{
me.setStart(parentNode,offset)
}
}else{
if(tmpNode){
me.setEndBefore(tmpNode)
}else{
me.setEnd(parentNode,offset)
}
}
}
getNode(addr.startAddress,true);
!ignoreEnd && addr.endAddress && getNode(addr.endAddress);
return me;
},
equals : function(rng){
for(var p in this){
if(this.hasOwnProperty(p)){
if(this[p] !== rng[p])
return false
}
}
return true;
},
scrollIntoView : function(){
var $span = $('<span style="padding:0;margin:0;display:block;border:0">&nbsp;</span>');
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下<b>x</b>[xx] 闭合后 <b>x|</b>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就马上执行fnfn的中的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 = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
}
//如果要求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 //若编辑器中只包含字符"&lt;p&gt;&lt;br /&gt;&lt;/p/&gt;"会返回空。
* @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 ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />' : '<style>' + si.cssText + '</style>');
});
utils.each(me.document.getElementsByTagName('script'), function (si) {
headHtmlForIE9 += si.outerHTML;
});
}
return '<html><head>' + (me.options.charset ? '<meta http-equiv="Content-Type" content="text/html; charset=' + me.options.charset + '"/>' : '')
+ (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '</head>'
+ '<body ' + (ie && browser.version < 9 ? 'class="view"' : '') + '>' + me.getContent(null, null, true) + '</body></html>';
},
/**
* 得到编辑器的纯文本内容,但会保留段落格式
* @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)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
.replace(/<br\/?>/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(/&nbsp;/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 = '<p>' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>' + 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
* 默认有文本内容,或者有以下节点都不认为是空
* <code>{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}</code>
* @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 = '<p>' + (ie ? '' : '<br/>') + '</p>';
me.removeListener('firstBeforeExecCommand focus', clear);
setTimeout(function () {
me.focus();
me._selectionChange();
}, 0)
}
}
return function (cont) {
var me = this;
me.body.innerHTML = '<p id="initContent">' + cont + '</p>';
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( /<!--[\s\S]*?-->/ig, "" )
//转换图片
.replace(/<v:shape [^>]*>[\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 '<img width="'+ transUnit(width) +'" height="'+transUnit(height) +'" src="' + src + '" />';
} 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( /<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>" )
//去掉多余的属性
.replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){
//保留list的标示
return name == 'class' && val == 'MsoListParagraph' ? str : ''
})
//清除多余的font/span不能匹配&nbsp;有可能是空格
.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(/&#39;/g,'\'')
.replace( /&quot;/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(!/<table/.test(tag))
n[i] = name.replace( /^mso-|-alt$/g, "" ) + ":" + transUnit( value );
continue;
case "horiz-align":
n[i] = "text-align:" + value;
continue;
case "vert-align":
n[i] = "vertical-align:" + value;
continue;
case "font-color":
case "mso-foreground":
n[i] = "color:" + value;
continue;
case "mso-background":
case "mso-highlight":
n[i] = "background:" + value;
continue;
case "mso-default-height":
n[i] = "min-height:" + transUnit( value );
continue;
case "mso-default-width":
n[i] = "min-width:" + transUnit( value );
continue;
case "mso-padding-between-alt":
n[i] = "border-collapse:separate;border-spacing:" + transUnit( value );
continue;
case "text-line-through":
if ( (value == "single") || (value == "double") ) {
n[i] = "text-decoration:line-through";
}
continue;
case "mso-zero-height":
if ( value == "yes" ) {
n[i] = "display:none";
}
continue;
// case 'background':
// break;
case 'margin':
if ( !/[1-9]/.test( value ) ) {
continue;
}
}
if ( /^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?:decor|trans)|top-bar|version|vnd|word-break)/.test( name )
||
/text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value)
) {
continue;
}
n[i] = name + ":" + parts[1];
}
}
return tag + (n.length ? ' style="' + n.join( ';').replace(/;{2,}/g,';') + '"' : '');
})
.replace(/[\d.]+(cm|pt)/g,function(str){
return utils.transUnitToPx(str)
})
}
return function ( html ) {
return (isWordDocument( html ) ? filterPasteWord( html ) : html);
};
}();
///import editor.js
///import core/utils.js
///import core/dom/dom.js
///import core/dom/dtd.js
///import core/htmlparser.js
//模拟的节点类
//by zhanyi
(function () {
var uNode = UM.uNode = function (obj) {
this.type = obj.type;
this.data = obj.data;
this.tagName = obj.tagName;
this.parentNode = obj.parentNode;
this.attrs = obj.attrs || {};
this.children = obj.children;
};
var notTransAttrs = {
'href':1,
'src':1,
'_src':1,
'_href':1,
'cdata_data':1
};
var notTransTagName = {
style:1,
script:1
};
var indentChar = ' ',
breakChar = '\n';
function insertLine(arr, current, begin) {
arr.push(breakChar);
return current + (begin ? 1 : -1);
}
function insertIndent(arr, current) {
//插入缩进
for (var i = 0; i < current; i++) {
arr.push(indentChar);
}
}
//创建uNode的静态方法
//支持标签和html
uNode.createElement = function (html) {
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,' &nbsp;'))
}
}
function isElement(node, arr, formatter, current) {
var attrhtml = '';
if (node.attrs) {
attrhtml = [];
var attrs = node.attrs;
for (var a in attrs) {
//这里就针对
//<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
//这里边的\"做转换要不用innerHTML直接被截断了属性src
//有可能做的不够
attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
return '&quot;'
}) : 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('<!--' + node.data + '-->');
}
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('<span>text</span>');
* ```
*/
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('<span>text</span>');
* ```
*/
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 原来的方式 [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
//先去掉了,加上的原因忘了,这里先记录
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;
}
//如果结束是文本,就有可能丢掉,所以这里手动判断一下
//例如 <li>sdfsdfsdf<li>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]<br/>
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 = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
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>|<[p> ==> <p></p><div></div><p>|</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 : '<br/>';
}catch(e){
range.setStartBefore(child);
domUtils.remove(child)
}
}
}
//加上true因为在删除表情等时会删两次第一次是删的fillData
try{
if(browser.ie9below && range.startContainer.nodeType == 1 && !range.startContainer.childNodes[range.startOffset]){
var start = range.startContainer,pre = start.childNodes[range.startOffset-1];
if(pre && pre.nodeType == 1 && dtd.$empty[pre.tagName]){
var txt = this.document.createTextNode(domUtils.fillChar);
range.insertNode(txt).setStart(txt,0).collapse(true);
}
}
setTimeout(function(){
range.select(true);
})
}catch(e){}
setTimeout(function(){
range = me.selection.getRange();
range.scrollIntoView();
me.fireEvent('afterinserthtml');
},200);
}
};
///import core
///import plugins\inserthtml.js
///commands 插入图片,操作图片的对齐方式
///commandsName InsertImage,ImageNone,ImageLeft,ImageRight,ImageCenter
///commandsTitle 图片,默认,居左,居右,居中
///commandsDialog dialogs\image
/**
* Created by .
* User: zhanyi
* for image
*/
UM.commands['insertimage'] = {
execCommand:function (cmd, opt) {
opt = utils.isArray(opt) ? opt : [opt];
if (!opt.length) {
return;
}
var me = this;
var html = [], str = '', ci;
ci = opt[0];
if (opt.length == 1) {
str = '<img src="' + ci.src + '" ' + (ci._src ? ' _src="' + ci._src + '" ' : '') +
(ci.width ? 'width="' + ci.width + '" ' : '') +
(ci.height ? ' height="' + ci.height + '" ' : '') +
(ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right' ? ' style="float:' + ci['floatStyle'] + ';"' : '') +
(ci.title && ci.title != "" ? ' title="' + ci.title + '"' : '') +
(ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : '') +
(ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : '') +
(ci.hspace && ci.hspace != "0" ? ' hspace = "' + ci.hspace + '"' : '') +
(ci.vspace && ci.vspace != "0" ? ' vspace = "' + ci.vspace + '"' : '') + '/>';
if (ci['floatStyle'] == 'center') {
str = '<p style="text-align: center">' + str + '</p>';
}
html.push(str);
} else {
for (var i = 0; ci = opt[i++];) {
str = '<p ' + (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') + '><img src="' + ci.src + '" ' +
(ci.width ? 'width="' + ci.width + '" ' : '') + (ci._src ? ' _src="' + ci._src + '" ' : '') +
(ci.height ? ' height="' + ci.height + '" ' : '') +
' style="' + (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') +
(ci.border || '') + '" ' +
(ci.title ? ' title="' + ci.title + '"' : '') + ' /></p>';
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 = $('<span></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 = utils.html(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', utils.html(a.getAttr('href')));
})
});
me.commands['link'] = {
execCommand : function( cmdName, opt ) {
var me = this;
var rng = me.selection.getRange();
if(rng.collapsed){
var start = rng.startContainer;
if(start = domUtils.findParentByTagName(start,'a',true)){
$(start).attr(opt);
rng.selectNode(start).select()
}else{
rng.insertNode($('<a>' +opt.href+'</a>').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();
$('<iframe src="" id="' + id + '" name="' + id + '" frameborder="0"></iframe>').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('<html><head></head><body><div>'+this.getContent(null,null,true)+'</div><script>' +
"setTimeout(function(){" +
"window.print();" +
"setTimeout(function(){" +
"window.parent.$('#" + id + "').remove();" +
"},100);" +
"},200);" +
'</script></body></html>');
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 = $('<p></p>').appendTo(rng.startContainer).html(browser.ie ? '&nbsp;' : '<br/>')[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 = "<p>"+(ie ? "" : "<br/>")+"</p>";
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会产生多余的</hr>
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(/>&nbsp;</g, '><').replace(/\s*</g, '<').replace(/>\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 = '&nbsp;';
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 = '<p><br/></p>';
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字符也会产生&nbsp;这里过滤掉
html = div.innerHTML;//.replace(/>(?:(\s|&nbsp;)*?)</g,'><');
//过滤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)
};
$('<p></p>').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(/&nbsp;/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() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
};
} else {
me.$body.css({
'position':'',
'left':'',
'top':''
});
// me.body.style.cssText = bakCssText;
var cont = sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
//处理掉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 = '<p>'+(browser.ie?'':'<br/>')+'</p>';
}
//要在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, '<p>');
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, '<p>');
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 = '&nbsp;'
}
})
})
};
///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 ?
'<link rel="stylesheet" href="' + path + 'third-party/mathquill/mathquill.css"/>' +
'<script src="' + path + 'third-party/jquery.min.js"></script>' +
'<script src="' + path + 'third-party/mathquill/mathquill.min.js"></script>':'';
d.open();
d.write('<html><head>' + formula + '</head><body><div>'+c+'</div></body></html>');
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 ?
'<img ' + (id ? 'id="' + id+'"' : '') + ' width="'+ width +'" height="' + height + '" _url="'+url+'" class="edui-faked-video"' +
' src="' + me.options.UMEDITOR_HOME_URL+'themes/default/images/spacer.gif" style="background:url('+me.options.UMEDITOR_HOME_URL+'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;'+(align ? 'float:' + align + ';': '')+'" />'
:
'<embed type="application/x-shockwave-flash" class="edui-faked-video" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
' src="' + url + '" width="' + width + '" height="' + height + '"' + (align ? ' style="float:' + align + '"': '') +
' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';
}
function switchImgAndEmbed(root,img2embed){
utils.each(root.getNodesByTagName(img2embed ? 'img' : 'embed'),function(node){
if(node.getAttr('class') == 'edui-faked-video'){
var html = creatInsertStr( img2embed ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',img2embed);
node.parentNode.replaceChild(UM.uNode.createElement(html),node)
}
})
}
me.addOutputRule(function(root){
switchImgAndEmbed(root,true)
});
me.addInputRule(function(root){
switchImgAndEmbed(root)
});
me.commands["insertvideo"] = {
execCommand: function (cmd, videoObjs){
videoObjs = utils.isArray(videoObjs)?videoObjs:[videoObjs];
var html = [],id = 'tmpVedio';
for(var i=0,vi,len = videoObjs.length;i<len;i++){
vi = videoObjs[i];
html.push(creatInsertStr( vi.url, vi.width || 420, vi.height || 280, id + i,vi.align,false));
}
me.execCommand("inserthtml",html.join(""),true);
},
queryCommandState : function(){
var img = me.selection.getRange().getClosedNode(),
flag = img && (img.className == "edui-faked-video");
return flag ? 1 : 0;
}
};
};
///import core
///commands 全选
///commandsName SelectAll
///commandsTitle 全选
/**
* 选中所有
* @function
* @name UM.execCommand
* @param {String} cmdName selectall选中编辑器里的所有内容
* @author zhanyi
*/
UM.plugins['selectall'] = function(){
var me = this;
me.commands['selectall'] = {
execCommand : function(){
//去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
var me = this,body = me.body,
range = me.selection.getRange();
range.selectNodeContents(body);
if(domUtils.isEmptyBlock(body)){
//opera不能自动合并到元素的里边要手动处理一下
if(browser.opera && body.firstChild && body.firstChild.nodeType == 1){
range.setStartAtFirst(body.firstChild);
}
range.collapse(true);
}
range.select(true);
},
notNeedUndo : 1
};
//快捷键
me.addshortcutkey({
"selectAll" : "ctrl+65"
});
};
//UM.plugins['removeformat'] = function () {
// var me = this;
// me.commands['removeformat'] = {
// execCommand: function () {
// me.document.execCommand('removeformat');
//
// /* 处理ie8和firefox选区有链接时,清除格式的bug */
// if (browser.gecko || browser.ie8 || browser.webkit) {
// var nativeRange = this.selection.getNative().getRangeAt(0),
// common = nativeRange.commonAncestorContainer,
// rng = me.selection.getRange(),
// bk = rng.createBookmark();
//
// function isEleInBookmark(node, bk){
// if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_FOLLOWING) &&
// (domUtils.getPosition(bk.end, node) & domUtils.POSITION_FOLLOWING) ) {
// return true;
// } else if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_CONTAINS) ||
// (domUtils.getPosition(node, bk.end) & domUtils.POSITION_CONTAINS) ) {
// return true;
// }
// return false;
// }
//
// $(common).find('a').each(function (k, a) {
// if ( isEleInBookmark(a, bk) ) {
// a.removeAttribute('style');
// }
// });
//
// }
// }
// };
//
//};
//
UM.plugins['removeformat'] = function(){
var me = this;
me.setOpt({
'removeFormatTags': 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
'removeFormatAttributes':'class,style,lang,width,height,align,hspace,valign'
});
me.commands['removeformat'] = {
execCommand : function( cmdName, tags, style, attrs,notIncludeA ) {
var tagReg = new RegExp( '^(?:' + (tags || this.options.removeFormatTags).replace( /,/g, '|' ) + ')$', 'i' ) ,
removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split( ',' ),
range = new dom.Range( this.document ),
bookmark,node,parent,
filter = function( node ) {
return node.nodeType == 1;
};
function isRedundantSpan (node) {
if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span'){
return 0;
}
if (browser.ie) {
//ie 下判断实效所以只能简单用style来判断
//return node.style.cssText == '' ? 1 : 0;
var attrs = node.attributes;
if ( attrs.length ) {
for ( var i = 0,l = attrs.length; i<l; i++ ) {
if ( attrs[i].specified ) {
return 0;
}
}
return 1;
}
}
return !node.attributes.length;
}
function doRemove( range ) {
var bookmark1 = range.createBookmark();
if ( range.collapsed ) {
range.enlarge( true );
}
//不能把a标签切了
if(!notIncludeA){
var aNode = domUtils.findParentByTagName(range.startContainer,'a',true);
if(aNode){
range.setStartBefore(aNode);
}
aNode = domUtils.findParentByTagName(range.endContainer,'a',true);
if(aNode){
range.setEndAfter(aNode);
}
}
bookmark = range.createBookmark();
node = bookmark.start;
//切开始
while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
domUtils.breakParent( node, parent );
domUtils.clearEmptySibling( node );
}
if ( bookmark.end ) {
//切结束
node = bookmark.end;
while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
domUtils.breakParent( node, parent );
domUtils.clearEmptySibling( node );
}
//开始去除样式
var current = domUtils.getNextDomNode( bookmark.start, false, filter ),
next;
while ( current ) {
if ( current == bookmark.end ) {
break;
}
next = domUtils.getNextDomNode( current, true, filter );
if ( !dtd.$empty[current.tagName.toLowerCase()] && !domUtils.isBookmarkNode( current ) ) {
if ( tagReg.test( current.tagName ) ) {
if ( style ) {
domUtils.removeStyle( current, style );
if ( isRedundantSpan( current ) && style != 'text-decoration'){
domUtils.remove( current, true );
}
} else {
domUtils.remove( current, true );
}
} else {
//trace:939 不能把list上的样式去掉
if(!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]){
domUtils.removeAttributes( current, removeFormatAttributes );
if ( isRedundantSpan( current ) ){
domUtils.remove( current, true );
}
}
}
}
current = next;
}
}
//trace:1035
//trace:1096 不能把td上的样式去掉比如边框
var pN = bookmark.start.parentNode;
if(domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]){
domUtils.removeAttributes( pN,removeFormatAttributes );
}
pN = bookmark.end.parentNode;
if(bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName]&& !dtd.$list[pN.tagName]){
domUtils.removeAttributes( pN,removeFormatAttributes );
}
range.moveToBookmark( bookmark ).moveToBookmark(bookmark1);
//清除冗余的代码 <b><bookmark></b>
var node = range.startContainer,
tmp,
collapsed = range.collapsed;
while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
tmp = node.parentNode;
range.setStartBefore(node);
//trace:937
//更新结束边界
if(range.startContainer === range.endContainer){
range.endOffset--;
}
domUtils.remove(node);
node = tmp;
}
if(!collapsed){
node = range.endContainer;
while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
tmp = node.parentNode;
range.setEndBefore(node);
domUtils.remove(node);
node = tmp;
}
}
}
range = this.selection.getRange();
if(!range.collapsed) {
doRemove( range );
range.select();
}
}
};
};
/*
* 处理特殊键的兼容性问题
*/
UM.plugins['keystrokes'] = function() {
var me = this;
var collapsed = true;
me.addListener('keydown', function(type, evt) {
var keyCode = evt.keyCode || evt.which,
rng = me.selection.getRange();
//处理全选的情况
if(!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <=90
|| keyCode >= 48 && keyCode <= 57 ||
keyCode >= 96 && keyCode <= 111 || {
13:1,
8:1,
46:1
}[keyCode])
){
var tmpNode = rng.startContainer;
if(domUtils.isFillChar(tmpNode)){
rng.setStartBefore(tmpNode)
}
tmpNode = rng.endContainer;
if(domUtils.isFillChar(tmpNode)){
rng.setEndAfter(tmpNode)
}
rng.txtToElmBoundary();
//结束边界可能放到了br的前边要把br包含进来
// x[xxx]<br/>
if(rng.endContainer && rng.endContainer.nodeType == 1){
tmpNode = rng.endContainer.childNodes[rng.endOffset];
if(tmpNode && domUtils.isBr(tmpNode)){
rng.setEndAfter(tmpNode);
}
}
if(rng.startOffset == 0){
tmpNode = rng.startContainer;
if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
tmpNode = rng.endContainer;
if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
me.fireEvent('saveScene');
me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
rng.setStart(me.body.firstChild,0).setCursor(false,true);
me._selectionChange();
return;
}
}
}
}
//处理backspace
if (keyCode == 8) {
rng = me.selection.getRange();
collapsed = rng.collapsed;
if(me.fireEvent('delkeydown',evt)){
return;
}
var start,end;
//避免按两次删除才能生效的问题
if(rng.collapsed && rng.inFillChar()){
start = rng.startContainer;
if(domUtils.isFillChar(start)){
rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
domUtils.remove(start)
}else{
start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),'');
rng.startOffset--;
rng.collapse(true).select(true)
}
}
//解决选中control元素不能删除的问题
if (start = rng.getClosedNode()) {
me.fireEvent('saveScene');
rng.setStartBefore(start);
domUtils.remove(start);
rng.setCursor();
me.fireEvent('saveScene');
domUtils.preventDefault(evt);
return;
}
//阻止在table上的删除
if (!browser.ie) {
start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
if (start && !end || !start && end || start !== end) {
evt.preventDefault();
return;
}
}
start = rng.startContainer;
if(rng.collapsed && start.nodeType == 1){
var currentNode = start.childNodes[rng.startOffset-1];
if(currentNode && currentNode.nodeType == 1 && currentNode.tagName == 'BR'){
me.fireEvent('saveScene');
rng.setStartBefore(currentNode).collapse(true);
domUtils.remove(currentNode);
rng.select();
me.fireEvent('saveScene');
}
}
//trace:3613
if(browser.chrome){
if(rng.collapsed){
while(rng.startOffset == 0 && !domUtils.isEmptyBlock(rng.startContainer)){
rng.setStartBefore(rng.startContainer)
}
var pre = rng.startContainer.childNodes[rng.startOffset-1];
if(pre && pre.nodeName == 'BR'){
rng.setStartBefore(pre);
me.fireEvent('saveScene');
$(pre).remove();
rng.setCursor();
me.fireEvent('saveScene');
}
}
}
}
//trace:1634
//ff的del键在容器空的时候也会删除
if(browser.gecko && keyCode == 46){
var range = me.selection.getRange();
if(range.collapsed){
start = range.startContainer;
if(domUtils.isEmptyBlock(start)){
var parent = start.parentNode;
while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){
start = parent;
parent = parent.parentNode;
}
if(start === parent.lastChild)
evt.preventDefault();
return;
}
}
}
});
me.addListener('keyup', function(type, evt) {
var keyCode = evt.keyCode || evt.which,
rng,me = this;
if(keyCode == 8){
if(me.fireEvent('delkeyup')){
return;
}
rng = me.selection.getRange();
if(rng.collapsed){
var tmpNode,
autoClearTagName = ['h1','h2','h3','h4','h5','h6'];
if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){
if(domUtils.isEmptyBlock(tmpNode)){
var pre = tmpNode.previousSibling;
if(pre && pre.nodeName != 'TABLE'){
domUtils.remove(tmpNode);
rng.setStartAtLast(pre).setCursor(false,true);
return;
}else{
var next = tmpNode.nextSibling;
if(next && next.nodeName != 'TABLE'){
domUtils.remove(tmpNode);
rng.setStartAtFirst(next).setCursor(false,true);
return;
}
}
}
}
//处理当删除到body时要重新给p标签展位
if(domUtils.isBody(rng.startContainer)){
var tmpNode = domUtils.createElement(me.document,'p',{
'innerHTML' : browser.ie ? domUtils.fillChar : '<br/>'
});
rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true);
}
}
//chrome下如果删除了inline标签浏览器会有记忆在输入文字还是会套上刚才删除的标签所以这里再选一次就不会了
if( !collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){
if(browser.ie){
var span = rng.document.createElement('span');
rng.insertNode(span).setStartBefore(span).collapse(true);
rng.select();
domUtils.remove(span)
}else{
rng.select()
}
}
}
})
};
/**
* 自动保存草稿
*/
UM.plugins['autosave'] = function() {
var me = this,
//无限循环保护
lastSaveTime = new Date(),
//最小保存间隔时间
MIN_TIME = 20,
//auto save key
saveKey = null;
//默认间隔时间
me.setOpt('saveInterval', 500);
//存储媒介封装
var LocalStorage = UM.LocalStorage = ( function () {
var storage = window.localStorage || getUserData() || null,
LOCAL_FILE = "localStorage";
return {
saveLocalData: function ( key, data ) {
if ( storage && data) {
storage.setItem( key, data );
return true;
}
return false;
},
getLocalData: function ( key ) {
if ( storage ) {
return storage.getItem( key );
}
return null;
},
removeItem: function ( key ) {
storage && storage.removeItem( key );
}
};
function getUserData () {
var container = document.createElement( "div" );
container.style.display = "none";
if( !container.addBehavior ) {
return null;
}
container.addBehavior("#default#userdata");
return {
getItem: function ( key ) {
var result = null;
try {
document.body.appendChild( container );
container.load( LOCAL_FILE );
result = container.getAttribute( key );
document.body.removeChild( container );
} catch ( e ) {
}
return result;
},
setItem: function ( key, value ) {
document.body.appendChild( container );
container.setAttribute( key, value );
container.save( LOCAL_FILE );
document.body.removeChild( container );
},
// 暂时没有用到
// clear: function () {
//
// var expiresTime = new Date();
// expiresTime.setFullYear( expiresTime.getFullYear() - 1 );
// document.body.appendChild( container );
// container.expires = expiresTime.toUTCString();
// container.save( LOCAL_FILE );
// document.body.removeChild( container );
//
// },
removeItem: function ( key ) {
document.body.appendChild( container );
container.removeAttribute( key );
container.save( LOCAL_FILE );
document.body.removeChild( container );
}
};
}
} )();
function save ( editor ) {
var saveData = null;
if ( new Date() - lastSaveTime < MIN_TIME ) {
return;
}
if ( !editor.hasContents() ) {
//这里不能调用命令来删除, 会造成事件死循环
saveKey && LocalStorage.removeItem( saveKey );
return;
}
lastSaveTime = new Date();
editor._saveFlag = null;
saveData = me.body.innerHTML;
if ( editor.fireEvent( "beforeautosave", {
content: saveData
} ) === false ) {
return;
}
LocalStorage.saveLocalData( saveKey, saveData );
editor.fireEvent( "afterautosave", {
content: saveData
} );
}
me.addListener('ready', function(){
var _suffix = "-drafts-data",
key = null;
if ( me.key ) {
key = me.key + _suffix;
} else {
key = ( me.container.parentNode.id || 'ue-common' ) + _suffix;
}
//页面地址+编辑器ID 保持唯一
saveKey = ( location.protocol + location.host + location.pathname ).replace( /[.:\/]/g, '_' ) + key;
});
me.addListener('contentchange', function(){
if ( !saveKey ) {
return;
}
if ( me._saveFlag ) {
window.clearTimeout( me._saveFlag );
}
if ( me.options.saveInterval > 0 ) {
me._saveFlag = window.setTimeout( function () {
save( me );
}, me.options.saveInterval );
} else {
save(me);
}
})
me.commands['clearlocaldata'] = {
execCommand:function (cmd, name) {
if ( saveKey && LocalStorage.getLocalData( saveKey ) ) {
LocalStorage.removeItem( saveKey )
}
},
notNeedUndo: true,
ignoreContentChange:true
};
me.commands['getlocaldata'] = {
execCommand:function (cmd, name) {
return saveKey ? LocalStorage.getLocalData( saveKey ) || '' : '';
},
notNeedUndo: true,
ignoreContentChange:true
};
me.commands['drafts'] = {
execCommand:function (cmd, name) {
if ( saveKey ) {
me.body.innerHTML = LocalStorage.getLocalData( saveKey ) || '<p>'+(browser.ie ? '&nbsp;' : '<br/>')+'</p>';
me.focus(true);
}
},
queryCommandState: function () {
return saveKey ? ( LocalStorage.getLocalData( saveKey ) === null ? -1 : 0 ) : -1;
},
notNeedUndo: true,
ignoreContentChange:true
}
};
/**
* @description
* 1.拖放文件到编辑区域,自动上传并插入到选区
* 2.插入粘贴板的图片,自动上传并插入到选区
* @author Jinqn
* @date 2013-10-14
*/
UM.plugins['autoupload'] = function () {
var me = this;
me.setOpt('pasteImageEnabled', true);
me.setOpt('dropFileEnabled', true);
var sendAndInsertImage = function (file, editor) {
//模拟数据
var fd = new FormData();
fd.append(editor.options.imageFieldName || 'upfile', file, file.name || ('blob.' + file.type.substr('image/'.length)));
fd.append('type', 'ajax');
var xhr = new XMLHttpRequest();
xhr.open("post", me.options.imageUrl, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.addEventListener('load', function (e) {
try {
var json = eval('('+e.target.response+')'),
link = json.url,
picLink = me.options.imagePath + link;
editor.execCommand('insertimage', {
src: picLink,
_src: picLink
});
} catch (er) {
}
});
xhr.send(fd);
};
function getPasteImage(e) {
return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items : null;
}
function getDropImage(e) {
return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null;
}
me.addListener('ready', function () {
if (window.FormData && window.FileReader) {
var autoUploadHandler = function (e) {
var hasImg = false,
items;
//获取粘贴板文件列表或者拖放文件列表
items = e.type == 'paste' ? getPasteImage(e.originalEvent) : getDropImage(e.originalEvent);
if (items) {
var len = items.length,
file;
while (len--) {
file = items[len];
if (file.getAsFile) file = file.getAsFile();
if (file && file.size > 0 && /image\/\w+/i.test(file.type)) {
sendAndInsertImage(file, me);
hasImg = true;
}
}
if (hasImg) return false;
}
};
me.getOpt('pasteImageEnabled') && me.$body.on('paste', autoUploadHandler);
me.getOpt('dropFileEnabled') && me.$body.on('drop', autoUploadHandler);
//取消拖放图片时出现的文字光标位置提示
me.$body.on('dragover', function (e) {
if (e.originalEvent.dataTransfer.types[0] == 'Files') {
return false;
}
});
}
});
};
/**
* 公式插件
*/
UM.plugins['formula'] = function () {
var me = this;
function getActiveIframe() {
return me.$body.find('iframe.edui-formula-active')[0] || null;
}
function blurActiveIframe(){
var iframe = getActiveIframe();
iframe && iframe.contentWindow.formula.blur();
}
me.addInputRule(function (root) {
$.each(root.getNodesByTagName('span'), function (i, node) {
if (node.hasClass('mathquill-embedded-latex')) {
var firstChild, latex = '';
while(firstChild = node.firstChild()){
latex += firstChild.data;
node.removeChild(firstChild);
}
node.tagName = 'iframe';
node.setAttr({
'frameborder': '0',
'src': me.getOpt('UMEDITOR_HOME_URL') + 'dialogs/formula/formula.html',
'data-latex': utils.unhtml(latex)
});
}
});
});
me.addOutputRule(function (root) {
$.each(root.getNodesByTagName('iframe'), function (i, node) {
if (node.hasClass('mathquill-embedded-latex')) {
node.tagName = 'span';
node.appendChild(UM.uNode.createText(node.getAttr('data-latex')));
node.setAttr({
'frameborder': '',
'src': '',
'data-latex': ''
});
}
});
});
me.addListener('click', function(){
blurActiveIframe();
});
me.addListener('afterexeccommand', function(type, cmd){
if(cmd != 'formula') {
blurActiveIframe();
}
});
me.commands['formula'] = {
execCommand: function (cmd, latex) {
var iframe = getActiveIframe();
if (iframe) {
iframe.contentWindow.formula.insertLatex(latex);
} else {
me.execCommand('inserthtml', '<span class="mathquill-embedded-latex">' + latex + '</span>');
browser.ie && browser.ie9below && setTimeout(function(){
var rng = me.selection.getRange(),
startContainer = rng.startContainer;
if(startContainer.nodeType == 1 && !startContainer.childNodes[rng.startOffset]){
rng.insertNode(me.document.createTextNode(' '));
rng.setCursor()
}
},100)
}
},
queryCommandState: function (cmd) {
return 0;
},
queryCommandValue: function (cmd) {
var iframe = getActiveIframe();
return iframe && iframe.contentWindow.formula.getLatex();
}
}
};
(function ($) {
//对jquery的扩展
$.parseTmpl = function parse(str, data) {
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g,function (match, code) {
return "'," + code.replace(/\\'/g, "'") + ",'";
}).replace(/<%([\s\S]+?)%>/g,function (match, code) {
return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "__p.push('";
}).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');";
var func = new Function('obj', tmpl);
return data ? func(data) : func;
};
$.extend2 = function (t, s) {
var a = arguments,
notCover = $.type(a[a.length - 1]) == 'boolean' ? a[a.length - 1] : false,
len = $.type(a[a.length - 1]) == 'boolean' ? a.length - 1 : a.length;
for (var i = 1; i < len; i++) {
var x = a[i];
for (var k in x) {
if (!notCover || !t.hasOwnProperty(k)) {
t[k] = x[k];
}
}
}
return t;
};
$.IE6 = !!window.ActiveXObject && parseFloat(navigator.userAgent.match(/msie (\d+)/i)[1]) == 6;
//所有ui的基类
var _eventHandler = [];
var _widget = function () {
};
var _prefix = 'edui';
_widget.prototype = {
on: function (ev, cb) {
this.root().on(ev, $.proxy(cb, this));
return this;
},
off: function (ev, cb) {
this.root().off(ev, $.proxy(cb, this));
return this;
},
trigger: function (ev, data) {
return this.root().trigger(ev, data) === false ? false : this;
},
root: function ($el) {
return this._$el || (this._$el = $el);
},
destroy: function () {
},
data: function (key, val) {
if (val !== undefined) {
this.root().data(_prefix + key, val);
return this;
} else {
return this.root().data(_prefix + key)
}
},
register: function (eventName, $el, fn) {
_eventHandler.push({
'evtname': eventName,
'$els': $.isArray($el) ? $el : [$el],
handler: $.proxy(fn, $el)
})
}
};
//从jq实例上拿到绑定的widget实例
$.fn.edui = function (obj) {
return obj ? this.data('eduiwidget', obj) : this.data('eduiwidget');
};
function _createClass(ClassObj, properties, supperClass) {
ClassObj.prototype = $.extend2(
$.extend({}, properties),
(UM.ui[supperClass] || _widget).prototype,
true
);
ClassObj.prototype.supper = (UM.ui[supperClass] || _widget).prototype;
//父class的defaultOpt 合并
if( UM.ui[supperClass] && UM.ui[supperClass].prototype.defaultOpt ) {
var parentDefaultOptions = UM.ui[supperClass].prototype.defaultOpt,
subDefaultOptions = ClassObj.prototype.defaultOpt;
ClassObj.prototype.defaultOpt = $.extend( {}, parentDefaultOptions, subDefaultOptions || {} );
}
return ClassObj
}
var _guid = 1;
function mergeToJQ(ClassObj, className) {
$[_prefix + className] = ClassObj;
$.fn[_prefix + className] = function (opt) {
var result, args = Array.prototype.slice.call(arguments, 1);
this.each(function (i, el) {
var $this = $(el);
var obj = $this.edui();
if (!obj) {
ClassObj(!opt || !$.isPlainObject(opt) ? {} : opt, $this);
$this.edui(obj)
}
if ($.type(opt) == 'string') {
if (opt == 'this') {
result = obj;
} else {
result = obj[opt].apply(obj, args);
if (result !== obj && result !== undefined) {
return false;
}
result = null;
}
}
});
return result !== null ? result : this;
}
}
UM.ui = {
define: function (className, properties, supperClass) {
var ClassObj = UM.ui[className] = _createClass(function (options, $el) {
var _obj = function () {
};
$.extend(_obj.prototype, ClassObj.prototype, {
guid: className + _guid++,
widgetName: className
}
);
var obj = new _obj;
if ($.type(options) == 'string') {
obj.init && obj.init({});
obj.root().edui(obj);
obj.root().find('a').click(function (evt) {
evt.preventDefault()
});
return obj.root()[_prefix + className].apply(obj.root(), arguments)
} else {
$el && obj.root($el);
obj.init && obj.init(!options || $.isPlainObject(options) ? $.extend2(options || {}, obj.defaultOpt || {}, true) : options);
try{
obj.root().find('a').click(function (evt) {
evt.preventDefault()
});
}catch(e){
}
return obj.root().edui(obj);
}
},properties, supperClass);
mergeToJQ(ClassObj, className);
}
};
$(function () {
$(document).on('click mouseup mousedown dblclick mouseover', function (evt) {
$.each(_eventHandler, function (i, obj) {
if (obj.evtname == evt.type) {
$.each(obj.$els, function (i, $el) {
if ($el[0] !== evt.target && !$.contains($el[0], evt.target)) {
obj.handler(evt);
}
})
}
})
})
})
})(jQuery);
//button 类
UM.ui.define('button', {
tpl: '<<%if(!texttype){%>div class="edui-btn edui-btn-<%=icon%> <%if(name){%>edui-btn-name-<%=name%><%}%>" unselectable="on" onmousedown="return false" <%}else{%>a class="edui-text-btn"<%}%><% if(title) {%> data-original-title="<%=title%>" <%};%>> ' +
'<% if(icon) {%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><% }; %><%if(text) {%><span unselectable="on" onmousedown="return false" class="edui-button-label"><%=text%></span><%}%>' +
'<%if(caret && text){%><span class="edui-button-spacing"></span><%}%>' +
'<% if(caret) {%><span unselectable="on" onmousedown="return false" class="edui-caret"></span><% };%></<%if(!texttype){%>div<%}else{%>a<%}%>>',
defaultOpt: {
text: '',
title: '',
icon: '',
width: '',
caret: false,
texttype: false,
click: function () {
}
},
init: function (options) {
var me = this;
me.root($($.parseTmpl(me.tpl, options)))
.click(function (evt) {
me.wrapclick(options.click, evt)
});
me.root().hover(function () {
if(!me.root().hasClass("edui-disabled")){
me.root().toggleClass('edui-hover')
}
})
return me;
},
wrapclick: function (fn, evt) {
if (!this.disabled()) {
this.root().trigger('wrapclick');
$.proxy(fn, this, evt)()
}
return this;
},
label: function (text) {
if (text === undefined) {
return this.root().find('.edui-button-label').text();
} else {
this.root().find('.edui-button-label').text(text);
return this;
}
},
disabled: function (state) {
if (state === undefined) {
return this.root().hasClass('edui-disabled')
}
this.root().toggleClass('edui-disabled', state);
if(this.root().hasClass('edui-disabled')){
this.root().removeClass('edui-hover')
}
return this;
},
active: function (state) {
if (state === undefined) {
return this.root().hasClass('edui-active')
}
this.root().toggleClass('edui-active', state)
return this;
},
mergeWith: function ($obj) {
var me = this;
me.data('$mergeObj', $obj);
$obj.edui().data('$mergeObj', me.root());
if (!$.contains(document.body, $obj[0])) {
$obj.appendTo(me.root());
}
me.on('click',function () {
me.wrapclick(function () {
$obj.edui().show();
})
}).register('click', me.root(), function (evt) {
$obj.hide()
});
}
});
//toolbar 类
(function () {
UM.ui.define('toolbar', {
tpl: '<div class="edui-toolbar" ><div class="edui-btn-toolbar" unselectable="on" onmousedown="return false" ></div></div>'
,
init: function () {
var $root = this.root($(this.tpl));
this.data('$btnToolbar', $root.find('.edui-btn-toolbar'))
},
appendToBtnmenu : function(data){
var $cont = this.data('$btnToolbar');
data = $.isArray(data) ? data : [data];
$.each(data,function(i,$item){
$cont.append($item)
})
}
});
})();
//menu 类
UM.ui.define('menu',{
show : function($obj,dir,fnname,topOffset,leftOffset){
fnname = fnname || 'position';
if(this.trigger('beforeshow') === false){
return;
}else{
this.root().css($.extend({display:'block'},$obj ? {
top : $obj[fnname]().top + ( dir == 'right' ? 0 : $obj.outerHeight()) - (topOffset || 0),
left : $obj[fnname]().left + (dir == 'right' ? $obj.outerWidth() : 0) - (leftOffset || 0)
}:{}))
this.trigger('aftershow');
}
},
hide : function(all){
var $parentmenu;
if(this.trigger('beforehide') === false){
return;
} else {
if($parentmenu = this.root().data('parentmenu')){
if($parentmenu.data('parentmenu')|| all)
$parentmenu.edui().hide();
}
this.root().css('display','none');
this.trigger('afterhide');
}
},
attachTo : function($obj){
var me = this;
if(!$obj.data('$mergeObj')){
$obj.data('$mergeObj',me.root());
$obj.on('wrapclick',function(evt){
me.show()
});
me.register('click',$obj,function(evt){
me.hide()
});
me.data('$mergeObj',$obj)
}
}
});
//dropmenu 类
UM.ui.define('dropmenu', {
tmpl: '<ul class="edui-dropdown-menu" aria-labelledby="dropdownMenu" >' +
'<%for(var i=0,ci;ci=data[i++];){%>' +
'<%if(ci.divider){%><li class="edui-divider"></li><%}else{%>' +
'<li <%if(ci.active||ci.disabled){%>class="<%= ci.active|| \'\' %> <%=ci.disabled||\'\' %>" <%}%> data-value="<%= ci.value%>">' +
'<a href="#" tabindex="-1"><em class="edui-dropmenu-checkbox"><i class="edui-icon-ok"></i></em><%= ci.label%></a>' +
'</li><%}%>' +
'<%}%>' +
'</ul>',
defaultOpt: {
data: [],
click: function () {
}
},
init: function (options) {
var me = this;
var eventName = {
click: 1,
mouseover: 1,
mouseout: 1
};
this.root($($.parseTmpl(this.tmpl, options))).on('click', 'li[class!="edui-disabled edui-divider edui-dropdown-submenu"]',function (evt) {
$.proxy(options.click, me, evt, $(this).data('value'), $(this))()
}).find('li').each(function (i, el) {
var $this = $(this);
if (!$this.hasClass("edui-disabled edui-divider edui-dropdown-submenu")) {
var data = options.data[i];
$.each(eventName, function (k) {
data[k] && $this[k](function (evt) {
$.proxy(data[k], el)(evt, data, me.root)
})
})
}
})
},
disabled: function (cb) {
$('li[class!=edui-divider]', this.root()).each(function () {
var $el = $(this);
if (cb === true) {
$el.addClass('edui-disabled')
} else if ($.isFunction(cb)) {
$el.toggleClass('edui-disabled', cb(li))
} else {
$el.removeClass('edui-disabled')
}
});
},
val: function (val) {
var currentVal;
$('li[class!="edui-divider edui-disabled edui-dropdown-submenu"]', this.root()).each(function () {
var $el = $(this);
if (val === undefined) {
if ($el.find('em.edui-dropmenu-checked').length) {
currentVal = $el.data('value');
return false
}
} else {
$el.find('em').toggleClass('edui-dropmenu-checked', $el.data('value') == val)
}
});
if (val === undefined) {
return currentVal
}
},
addSubmenu: function (label, menu, index) {
index = index || 0;
var $list = $('li[class!=edui-divider]', this.root());
var $node = $('<li class="edui-dropdown-submenu"><a tabindex="-1" href="#">' + label + '</a></li>').append(menu);
if (index >= 0 && index < $list.length) {
$node.insertBefore($list[index]);
} else if (index < 0) {
$node.insertBefore($list[0]);
} else if (index >= $list.length) {
$node.appendTo($list);
}
}
}, 'menu');
//splitbutton 类
///import button
UM.ui.define('splitbutton',{
tpl :'<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>" unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn" unselectable="on" ><%if(icon){%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><%if(text){%><%=text%><%}%></div>'+
'<div unselectable="on" class="edui-btn edui-dropdown-toggle" >'+
'<div unselectable="on" class="edui-caret"><\/div>'+
'</div>'+
'</div>',
defaultOpt:{
text:'',
title:'',
click:function(){}
},
init : function(options){
var me = this;
me.root( $($.parseTmpl(me.tpl,options)));
me.root().find('.edui-btn:first').click(function(evt){
if(!me.disabled()){
$.proxy(options.click,me)();
}
});
me.root().find('.edui-dropdown-toggle').click(function(){
if(!me.disabled()){
me.trigger('arrowclick')
}
});
me.root().hover(function () {
if(!me.root().hasClass("edui-disabled")){
me.root().toggleClass('edui-hover')
}
});
return me;
},
wrapclick:function(fn,evt){
if(!this.disabled()){
$.proxy(fn,this,evt)()
}
return this;
},
disabled : function(state){
if(state === undefined){
return this.root().hasClass('edui-disabled')
}
this.root().toggleClass('edui-disabled',state).find('.edui-btn').toggleClass('edui-disabled',state);
return this;
},
active:function(state){
if(state === undefined){
return this.root().hasClass('edui-active')
}
this.root().toggleClass('edui-active',state).find('.edui-btn:first').toggleClass('edui-active',state);
return this;
},
mergeWith:function($obj){
var me = this;
me.data('$mergeObj',$obj);
$obj.edui().data('$mergeObj',me.root());
if(!$.contains(document.body,$obj[0])){
$obj.appendTo(me.root());
}
me.root().delegate('.edui-dropdown-toggle','click',function(){
me.wrapclick(function(){
$obj.edui().show();
})
});
me.register('click',me.root().find('.edui-dropdown-toggle'),function(evt){
$obj.hide()
});
}
});
/**
* Created with JetBrains PhpStorm.
* User: hn
* Date: 13-7-10
* Time: 下午3:07
* To change this template use File | Settings | File Templates.
*/
UM.ui.define('colorsplitbutton',{
tpl : '<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>" unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn" unselectable="on" ><%if(icon){%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><div class="edui-splitbutton-color-label" <%if (color) {%>style="background: <%=color%>"<%}%>></div><%if(text){%><%=text%><%}%></div>'+
'<div unselectable="on" class="edui-btn edui-dropdown-toggle" >'+
'<div unselectable="on" class="edui-caret"><\/div>'+
'</div>'+
'</div>',
defaultOpt: {
color: ''
},
init: function( options ){
var me = this;
me.supper.init.call( me, options );
},
colorLabel: function(){
return this.root().find('.edui-splitbutton-color-label');
}
}, 'splitbutton');
//popup 类
UM.ui.define('popup', {
tpl: '<div class="edui-dropdown-menu edui-popup"'+
'<%if(!<%=stopprop%>){%>onmousedown="return false"<%}%>'+
'><div class="edui-popup-body" unselectable="on" onmousedown="return false"><%=subtpl%></div>' +
'<div class="edui-popup-caret"></div>' +
'</div>',
defaultOpt: {
stopprop:false,
subtpl: '',
width: '',
height: ''
},
init: function (options) {
this.root($($.parseTmpl(this.tpl, options)));
return this;
},
mergeTpl: function (data) {
return $.parseTmpl(this.tpl, {subtpl: data});
},
show: function ($obj, posObj) {
if (!posObj) posObj = {};
var fnname = posObj.fnname || 'position';
if (this.trigger('beforeshow') === false) {
return;
} else {
this.root().css($.extend({display: 'block'}, $obj ? {
top: $obj[fnname]().top + ( posObj.dir == 'right' ? 0 : $obj.outerHeight()) - (posObj.offsetTop || 0),
left: $obj[fnname]().left + (posObj.dir == 'right' ? $obj.outerWidth() : 0) - (posObj.offsetLeft || 0),
position: 'absolute'
} : {}));
this.root().find('.edui-popup-caret').css({
top: posObj.caretTop || 0,
left: posObj.caretLeft || 0,
position: 'absolute'
}).addClass(posObj.caretDir || "up")
}
this.trigger("aftershow");
},
hide: function () {
this.root().css('display', 'none');
this.trigger('afterhide')
},
attachTo: function ($obj, posObj) {
var me = this
if (!$obj.data('$mergeObj')) {
$obj.data('$mergeObj', me.root());
$obj.on('wrapclick', function (evt) {
me.show($obj, posObj)
});
me.register('click', $obj, function (evt) {
me.hide()
});
me.data('$mergeObj', $obj)
}
},
getBodyContainer: function () {
return this.root().find(".edui-popup-body");
}
});
//scale 类
UM.ui.define('scale', {
tpl: '<div class="edui-scale" unselectable="on">' +
'<span class="edui-scale-hand0"></span>' +
'<span class="edui-scale-hand1"></span>' +
'<span class="edui-scale-hand2"></span>' +
'<span class="edui-scale-hand3"></span>' +
'<span class="edui-scale-hand4"></span>' +
'<span class="edui-scale-hand5"></span>' +
'<span class="edui-scale-hand6"></span>' +
'<span class="edui-scale-hand7"></span>' +
'</div>',
defaultOpt: {
$doc: $(document),
$wrap: $(document)
},
init: function (options) {
if(options.$doc) this.defaultOpt.$doc = options.$doc;
if(options.$wrap) this.defaultOpt.$wrap = options.$wrap;
this.root($($.parseTmpl(this.tpl, options)));
this.initStyle();
this.startPos = this.prePos = {x: 0, y: 0};
this.dragId = -1;
return this;
},
initStyle: function () {
utils.cssRule('edui-style-scale', '.edui-scale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;}' +
'.edui-scale span{position:absolute;left:0;top:0;width:7px;height:7px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'
+ '.edui-scale .edui-scale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'
+ '.edui-scale .edui-scale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'
+ '.edui-scale .edui-scale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'
+ '.edui-scale .edui-scale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'
+ '.edui-scale .edui-scale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'
+ '.edui-scale .edui-scale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'
+ '.edui-scale .edui-scale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'
+ '.edui-scale .edui-scale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');
},
_eventHandler: function (e) {
var me = this,
$doc = me.defaultOpt.$doc;
switch (e.type) {
case 'mousedown':
var hand = e.target || e.srcElement, hand;
if (hand.className.indexOf('edui-scale-hand') != -1) {
me.dragId = hand.className.slice(-1);
me.startPos.x = me.prePos.x = e.clientX;
me.startPos.y = me.prePos.y = e.clientY;
$doc.bind('mousemove', $.proxy(me._eventHandler, me));
}
break;
case 'mousemove':
if (me.dragId != -1) {
me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
me.prePos.x = e.clientX;
me.prePos.y = e.clientY;
me.updateTargetElement();
}
break;
case 'mouseup':
if (me.dragId != -1) {
me.dragId = -1;
me.updateTargetElement();
var $target = me.data('$scaleTarget');
if ($target.parent()) me.attachTo(me.data('$scaleTarget'));
}
$doc.unbind('mousemove', $.proxy(me._eventHandler, me));
break;
default:
break;
}
},
updateTargetElement: function () {
var me = this,
$root = me.root(),
$target = me.data('$scaleTarget');
$target.css({width: $root.width(), height: $root.height()});
me.attachTo($target);
},
updateContainerStyle: function (dir, offset) {
var me = this,
$dom = me.root(),
tmp,
rect = [
//[left, top, width, height]
[0, 0, -1, -1],
[0, 0, 0, -1],
[0, 0, 1, -1],
[0, 0, -1, 0],
[0, 0, 1, 0],
[0, 0, -1, 1],
[0, 0, 0, 1],
[0, 0, 1, 1]
];
if (rect[dir][0] != 0) {
tmp = parseInt($dom.offset().left) + offset.x;
$dom.css('left', me._validScaledProp('left', tmp));
}
if (rect[dir][1] != 0) {
tmp = parseInt($dom.offset().top) + offset.y;
$dom.css('top', me._validScaledProp('top', tmp));
}
if (rect[dir][2] != 0) {
tmp = $dom.width() + rect[dir][2] * offset.x;
$dom.css('width', me._validScaledProp('width', tmp));
}
if (rect[dir][3] != 0) {
tmp = $dom.height() + rect[dir][3] * offset.y;
$dom.css('height', me._validScaledProp('height', tmp));
}
},
_validScaledProp: function (prop, value) {
var $ele = this.root(),
$wrap = this.defaultOpt.$doc,
calc = function(val, a, b){
return (val + a) > b ? b - a : value;
};
value = isNaN(value) ? 0 : value;
switch (prop) {
case 'left':
return value < 0 ? 0 : calc(value, $ele.width(), $wrap.width());
case 'top':
return value < 0 ? 0 : calc(value, $ele.height(),$wrap.height());
case 'width':
return value <= 0 ? 1 : calc(value, $ele.offset().left, $wrap.width());
case 'height':
return value <= 0 ? 1 : calc(value, $ele.offset().top, $wrap.height());
}
},
show: function ($obj) {
var me = this;
if ($obj) me.attachTo($obj);
me.root().bind('mousedown', $.proxy(me._eventHandler, me));
me.defaultOpt.$doc.bind('mouseup', $.proxy(me._eventHandler, me));
me.root().show();
me.trigger("aftershow");
},
hide: function () {
var me = this;
me.root().unbind('mousedown', $.proxy(me._eventHandler, me));
me.defaultOpt.$doc.unbind('mouseup', $.proxy(me._eventHandler, me));
me.root().hide();
me.trigger('afterhide')
},
attachTo: function ($obj) {
var me = this,
imgPos = $obj.offset(),
$root = me.root(),
$wrap = me.defaultOpt.$wrap,
posObj = $wrap.offset();
me.data('$scaleTarget', $obj);
me.root().css({
position: 'absolute',
width: $obj.width(),
height: $obj.height(),
left: imgPos.left - posObj.left - parseInt($wrap.css('border-left-width')) - parseInt($root.css('border-left-width')),
top: imgPos.top - posObj.top - parseInt($wrap.css('border-top-width')) - parseInt($root.css('border-top-width'))
});
},
getScaleTarget: function () {
return this.data('$scaleTarget')[0];
}
});
//colorpicker 类
UM.ui.define('colorpicker', {
tpl: function (opt) {
var COLORS = (
'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
'7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(',');
var html = '<div unselectable="on" onmousedown="return false" class="edui-colorpicker<%if (name){%> edui-colorpicker-<%=name%><%}%>" >' +
'<table unselectable="on" onmousedown="return false">' +
'<tr><td colspan="10">'+opt.lang_themeColor+'</td> </tr>' +
'<tr class="edui-colorpicker-firstrow" >';
for (var i = 0; i < COLORS.length; i++) {
if (i && i % 10 === 0) {
html += '</tr>' + (i == 60 ? '<tr><td colspan="10">'+opt.lang_standardColor+'</td></tr>' : '') + '<tr' + (i == 60 ? ' class="edui-colorpicker-firstrow"' : '') + '>';
}
html += i < 70 ? '<td><a unselectable="on" onmousedown="return false" title="' + COLORS[i] + '" class="edui-colorpicker-colorcell"' +
' data-color="#' + COLORS[i] + '"' +
' style="background-color:#' + COLORS[i] + ';border:solid #ccc;' +
(i < 10 || i >= 60 ? 'border-width:1px;' :
i >= 10 && i < 20 ? 'border-width:1px 1px 0 1px;' :
'border-width:0 1px 0 1px;') +
'"' +
'></a></td>' : '';
}
html += '</tr></table></div>';
return html;
},
init: function (options) {
var me = this;
me.root($($.parseTmpl(me.supper.mergeTpl(me.tpl(options)),options)));
me.root().on("click",function (e) {
me.trigger('pickcolor', $(e.target).data('color'));
});
}
}, 'popup');
/**
* Created with JetBrains PhpStorm.
* User: hn
* Date: 13-5-29
* Time: 下午8:01
* To change this template use File | Settings | File Templates.
*/
(function(){
var widgetName = 'combobox',
itemClassName = 'edui-combobox-item',
HOVER_CLASS = 'edui-combobox-item-hover',
ICON_CLASS = 'edui-combobox-checked-icon',
labelClassName = 'edui-combobox-item-label';
UM.ui.define( widgetName, ( function(){
return {
tpl: "<ul class=\"dropdown-menu edui-combobox-menu<%if (comboboxName!=='') {%> edui-combobox-<%=comboboxName%><%}%>\" unselectable=\"on\" onmousedown=\"return false\" role=\"menu\" aria-labelledby=\"dropdownMenu\">" +
"<%if(autoRecord) {%>" +
"<%for( var i=0, len = recordStack.length; i<len; i++ ) {%>" +
"<%var index = recordStack[i];%>" +
"<li class=\"<%=itemClassName%><%if( selected == index ) {%> edui-combobox-checked<%}%>\" data-item-index=\"<%=index%>\" unselectable=\"on\" onmousedown=\"return false\">" +
"<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
"<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ index ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=items[index]%></label>" +
"</li>" +
"<%}%>" +
"<%if( i ) {%>" +
"<li class=\"edui-combobox-item-separator\"></li>" +
"<%}%>" +
"<%}%>" +
"<%for( var i=0, label; label = items[i]; i++ ) {%>" +
"<li class=\"<%=itemClassName%><%if( selected == i ) {%> edui-combobox-checked<%}%> edui-combobox-item-<%=i%>\" data-item-index=\"<%=i%>\" unselectable=\"on\" onmousedown=\"return false\">" +
"<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
"<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ i ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=label%></label>" +
"</li>" +
"<%}%>" +
"</ul>",
defaultOpt: {
//记录栈初始列表
recordStack: [],
//可用项列表
items: [],
//item对应的值列表
value: [],
comboboxName: '',
selected: '',
//自动记录
autoRecord: true,
//最多记录条数
recordCount: 5
},
init: function( options ){
var me = this;
$.extend( me._optionAdaptation( options ), me._createItemMapping( options.recordStack, options.items ), {
itemClassName: itemClassName,
iconClass: ICON_CLASS,
labelClassName: labelClassName
} );
this._transStack( options );
me.root( $( $.parseTmpl( me.tpl, options ) ) );
this.data( 'options', options ).initEvent();
},
initEvent: function(){
var me = this;
me.initSelectItem();
this.initItemActive();
},
/**
* 初始化选择项
*/
initSelectItem: function(){
var me = this,
labelClass = "."+labelClassName;
me.root().delegate('.' + itemClassName, 'click', function(){
var $li = $(this),
index = $li.attr('data-item-index');
me.trigger('comboboxselect', {
index: index,
label: $li.find(labelClass).text(),
value: me.data('options').value[ index ]
}).select( index );
me.hide();
return false;
});
},
initItemActive: function(){
var fn = {
mouseenter: 'addClass',
mouseleave: 'removeClass'
};
if ($.IE6) {
this.root().delegate( '.'+itemClassName, 'mouseenter mouseleave', function( evt ){
$(this)[ fn[ evt.type ] ]( HOVER_CLASS );
}).one('afterhide', function(){
});
}
},
/**
* 选择给定索引的项
* @param index 项索引
* @returns {*} 如果存在对应索引的项则返回该项否则返回null
*/
select: function( index ){
var itemCount = this.data('options').itemCount,
items = this.data('options').autowidthitem;
if ( items && !items.length ) {
items = this.data('options').items;
}
if( itemCount == 0 ) {
return null;
}
if( index < 0 ) {
index = itemCount + index % itemCount;
} else if ( index >= itemCount ) {
index = itemCount-1;
}
this.trigger( 'changebefore', items[ index ] );
this._update( index );
this.trigger( 'changeafter', items[ index ] );
return null;
},
selectItemByLabel: function( label ){
var itemMapping = this.data('options').itemMapping,
me = this,
index = null;
!$.isArray( label ) && ( label = [ label ] );
$.each( label, function( i, item ){
index = itemMapping[ item ];
if( index !== undefined ) {
me.select( index );
return false;
}
} );
},
/**
* 转换记录栈
*/
_transStack: function( options ) {
var temp = [],
itemIndex = -1,
selected = -1;
$.each( options.recordStack, function( index, item ){
itemIndex = options.itemMapping[ item ];
if( $.isNumeric( itemIndex ) ) {
temp.push( itemIndex );
//selected的合法性检测
if( item == options.selected ) {
selected = itemIndex;
}
}
} );
options.recordStack = temp;
options.selected = selected;
temp = null;
},
_optionAdaptation: function( options ) {
if( !( 'itemStyles' in options ) ) {
options.itemStyles = [];
for( var i = 0, len = options.items.length; i < len; i++ ) {
options.itemStyles.push('');
}
}
options.autowidthitem = options.autowidthitem || options.items;
options.itemCount = options.items.length;
return options;
},
_createItemMapping: function( stackItem, items ){
var temp = {},
result = {
recordStack: [],
mapping: {}
};
$.each( items, function( index, item ){
temp[ item ] = index;
} );
result.itemMapping = temp;
$.each( stackItem, function( index, item ){
if( temp[ item ] !== undefined ) {
result.recordStack.push( temp[ item ] );
result.mapping[ item ] = temp[ item ];
}
} );
return result;
},
_update: function ( index ) {
var options = this.data("options"),
newStack = [],
newChilds = null;
$.each( options.recordStack, function( i, item ){
if( item != index ) {
newStack.push( item );
}
} );
//压入最新的记录
newStack.unshift( index );
if( newStack.length > options.recordCount ) {
newStack.length = options.recordCount;
}
options.recordStack = newStack;
options.selected = index;
newChilds = $( $.parseTmpl( this.tpl, options ) );
//重新渲染
this.root().html( newChilds.html() );
newChilds = null;
newStack = null;
}
};
} )(), 'menu' );
})();
/**
* Combox 抽象基类
* User: hn
* Date: 13-5-29
* Time: 下午8:01
* To change this template use File | Settings | File Templates.
*/
(function(){
var widgetName = 'buttoncombobox';
UM.ui.define( widgetName, ( function(){
return {
defaultOpt: {
//按钮初始文字
label: '',
title: ''
},
init: function( options ) {
var me = this;
var btnWidget = $.eduibutton({
caret: true,
name: options.comboboxName,
title: options.title,
text: options.label,
click: function(){
me.show( this.root() );
}
});
me.supper.init.call( me, options );
//监听change 改变button显示内容
me.on('changebefore', function( e, label ){
btnWidget.eduibutton('label', label );
});
me.data( 'button', btnWidget );
me.attachTo(btnWidget)
},
button: function(){
return this.data( 'button' );
}
}
} )(), 'combobox' );
})();
/*modal 类*/
UM.ui.define('modal', {
tpl: '<div class="edui-modal" tabindex="-1" >' +
'<div class="edui-modal-header">' +
'<div class="edui-close" data-hide="modal"></div>' +
'<h3 class="edui-title"><%=title%></h3>' +
'</div>' +
'<div class="edui-modal-body" style="<%if(width){%>width:<%=width%>px;<%}%>' +
'<%if(height){%>height:<%=height%>px;<%}%>">' +
' </div>' +
'<% if(cancellabel || oklabel) {%>' +
'<div class="edui-modal-footer">' +
'<div class="edui-modal-tip"></div>' +
'<%if(oklabel){%><div class="edui-btn edui-btn-primary" data-ok="modal"><%=oklabel%></div><%}%>' +
'<%if(cancellabel){%><div class="edui-btn" data-hide="modal"><%=cancellabel%></div><%}%>' +
'</div>' +
'<%}%></div>',
defaultOpt: {
title: "",
cancellabel: "",
oklabel: "",
width: '',
height: '',
backdrop: true,
keyboard: true
},
init: function (options) {
var me = this;
me.root($($.parseTmpl(me.tpl, options || {})));
me.data("options", options);
if (options.okFn) {
me.on('ok', $.proxy(options.okFn, me))
}
if (options.cancelFn) {
me.on('beforehide', $.proxy(options.cancelFn, me))
}
me.root().delegate('[data-hide="modal"]', 'click', $.proxy(me.hide, me))
.delegate('[data-ok="modal"]', 'click', $.proxy(me.ok, me));
$('[data-hide="modal"],[data-ok="modal"]',me.root()).hover(function(){
$(this).toggleClass('edui-hover')
});
},
toggle: function () {
var me = this;
return me[!me.data("isShown") ? 'show' : 'hide']();
},
show: function () {
var me = this;
me.trigger("beforeshow");
if (me.data("isShown")) return;
me.data("isShown", true);
me.escape();
me.backdrop(function () {
me.autoCenter();
me.root()
.show()
.focus()
.trigger('aftershow');
})
},
showTip: function ( text ) {
$( '.edui-modal-tip', this.root() ).html( text ).fadeIn();
},
hideTip: function ( text ) {
$( '.edui-modal-tip', this.root() ).fadeOut( function (){
$(this).html('');
} );
},
autoCenter: function () {
//ie6下不用处理了
!$.IE6 && this.root().css("margin-left", -(this.root().width() / 2));
},
hide: function () {
var me = this;
me.trigger("beforehide");
if (!me.data("isShown")) return;
me.data("isShown", false);
me.escape();
me.hideModal();
},
escape: function () {
var me = this;
if (me.data("isShown") && me.data("options").keyboard) {
me.root().on('keyup', function (e) {
e.which == 27 && me.hide();
})
}
else if (!me.data("isShown")) {
me.root().off('keyup');
}
},
hideModal: function () {
var me = this;
me.root().hide();
me.backdrop(function () {
me.removeBackdrop();
me.trigger('afterhide');
})
},
removeBackdrop: function () {
this.$backdrop && this.$backdrop.remove();
this.$backdrop = null;
},
backdrop: function (callback) {
var me = this;
if (me.data("isShown") && me.data("options").backdrop) {
me.$backdrop = $('<div class="edui-modal-backdrop" />').click(
me.data("options").backdrop == 'static' ?
$.proxy(me.root()[0].focus, me.root()[0])
: $.proxy(me.hide, me)
)
}
me.trigger('afterbackdrop');
callback && callback();
},
attachTo: function ($obj) {
var me = this
if (!$obj.data('$mergeObj')) {
$obj.data('$mergeObj', me.root());
$obj.on('click', function () {
me.toggle($obj)
});
me.data('$mergeObj', $obj)
}
},
ok: function () {
var me = this;
me.trigger('beforeok');
if (me.trigger("ok", me) === false) {
return;
}
me.hide();
},
getBodyContainer: function () {
return this.root().find('.edui-modal-body')
}
});
/*tooltip 类*/
UM.ui.define('tooltip', {
tpl: '<div class="edui-tooltip" unselectable="on" onmousedown="return false">' +
'<div class="edui-tooltip-arrow" unselectable="on" onmousedown="return false"></div>' +
'<div class="edui-tooltip-inner" unselectable="on" onmousedown="return false"></div>' +
'</div>',
init: function (options) {
var me = this;
me.root($($.parseTmpl(me.tpl, options || {})));
},
content: function (e) {
var me = this,
title = $(e.currentTarget).attr("data-original-title");
me.root().find('.edui-tooltip-inner')['text'](title);
},
position: function (e) {
var me = this,
$obj = $(e.currentTarget);
me.root().css($.extend({display: 'block'}, $obj ? {
top: $obj.outerHeight(),
left: (($obj.outerWidth() - me.root().outerWidth()) / 2)
} : {}))
},
show: function (e) {
if ($(e.currentTarget).hasClass('edui-disabled')) return;
var me = this;
me.content(e);
me.root().appendTo($(e.currentTarget));
me.position(e);
me.root().css('display', 'block');
},
hide: function () {
var me = this;
me.root().css('display', 'none')
},
attachTo: function ($obj) {
var me = this;
function tmp($obj) {
var me = this;
if (!$.contains(document.body, me.root()[0])) {
me.root().appendTo($obj);
}
me.data('tooltip', me.root());
$obj.each(function () {
if ($(this).attr("data-original-title")) {
$(this).on('mouseenter', $.proxy(me.show, me))
.on('mouseleave click', $.proxy(me.hide, me))
}
});
}
if ($.type($obj) === "undefined") {
$("[data-original-title]").each(function (i, el) {
tmp.call(me, $(el));
})
} else {
if (!$obj.data('tooltip')) {
tmp.call(me, $obj);
}
}
}
});
/*tab 类*/
UM.ui.define('tab', {
init: function (options) {
var me = this,
slr = options.selector;
if ($.type(slr)) {
me.root($(slr, options.context));
me.data("context", options.context);
$(slr, me.data("context")).on('click', function (e) {
me.show(e);
});
}
},
show: function (e) {
var me = this,
$cur = $(e.target),
$ul = $cur.closest('ul'),
selector,
previous,
$target,
e;
selector = $cur.attr('data-context');
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '');
var $tmp = $cur.parent('li');
if (!$tmp.length || $tmp.hasClass('edui-active')) return;
previous = $ul.find('.edui-active:last a')[0];
e = $.Event('beforeshow', {
target: $cur[0],
relatedTarget: previous
});
me.trigger(e);
if (e.isDefaultPrevented()) return;
$target = $(selector, me.data("context"));
me.activate($cur.parent('li'), $ul);
me.activate($target, $target.parent(), function () {
me.trigger({
type: 'aftershow', relatedTarget: previous
})
});
},
activate: function (element, container, callback) {
if (element === undefined) {
return $(".edui-tab-item.edui-active",this.root()).index();
}
var $active = container.find('> .edui-active');
$active.removeClass('edui-active');
element.addClass('edui-active');
callback && callback();
}
});
//button 类
UM.ui.define('separator', {
tpl: '<div class="edui-separator" unselectable="on" onmousedown="return false" ></div>',
init: function (options) {
var me = this;
me.root($($.parseTmpl(me.tpl, options)));
return me;
}
});
/**
* @file adapter.js
* @desc adapt ui to editor
* @import core/Editor.js, core/utils.js
*/
(function () {
var _editorUI = {},
_editors = {},
_readyFn = [],
_activeWidget = null,
_widgetData = {},
_widgetCallBack = {},
_cacheUI = {},
_maxZIndex = null;
utils.extend(UM, {
defaultWidth : 500,
defaultHeight : 500,
registerUI: function (name, fn) {
utils.each(name.split(/\s+/), function (uiname) {
_editorUI[uiname] = fn;
})
},
setEditor : function(editor){
!_editors[editor.id] && (_editors[editor.id] = editor);
},
registerWidget : function(name,pro,cb){
_widgetData[name] = $.extend2(pro,{
$root : '',
_preventDefault:false,
root:function($el){
return this.$root || (this.$root = $el);
},
preventDefault:function(){
this._preventDefault = true;
},
clear:false
});
if(cb){
_widgetCallBack[name] = cb;
}
},
getWidgetData : function(name){
return _widgetData[name]
},
setWidgetBody : function(name,$widget,editor){
if(!editor._widgetData){
utils.extend(editor,{
_widgetData : {},
getWidgetData : function(name){
return this._widgetData[name];
},
getWidgetCallback : function(widgetName){
var me = this;
return function(){
return _widgetCallBack[widgetName].apply(me,[me,$widget].concat(Array.prototype.slice.call(arguments,0)))
}
}
})
}
var pro = _widgetData[name];
if(!pro){
return null;
}
pro = editor._widgetData[name];
if(!pro){
pro = _widgetData[name];
pro = editor._widgetData[name] = $.type(pro) == 'function' ? pro : utils.clone(pro);
}
pro.root($widget.edui().getBodyContainer());
pro.initContent(editor,$widget);
if(!pro._preventDefault){
pro.initEvent(editor,$widget);
}
pro.width && $widget.width(pro.width);
},
setActiveWidget : function($widget){
_activeWidget = $widget;
},
getEditor: function (id, options) {
var editor = _editors[id] || (_editors[id] = this.createEditor(id, options));
_maxZIndex = _maxZIndex ? Math.max(editor.getOpt('zIndex'), _maxZIndex):editor.getOpt('zIndex');
return editor;
},
setTopEditor: function(editor){
$.each(_editors, function(i, o){
if(editor == o) {
editor.$container && editor.$container.css('zIndex', _maxZIndex + 1);
} else {
o.$container && o.$container.css('zIndex', o.getOpt('zIndex'));
}
});
},
clearCache : function(id){
if ( _editors[id]) {
delete _editors[id]
}
},
delEditor: function (id) {
var editor;
if (editor = _editors[id]) {
editor.destroy();
}
},
ready: function( fn ){
_readyFn.push( fn );
},
createEditor: function (id, opt) {
var editor = new UM.Editor(opt);
var T = this;
editor.langIsReady ? $.proxy(renderUI,T)() : editor.addListener("langReady", $.proxy(renderUI,T));
function renderUI(){
var $container = this.createUI('#' + id, editor);
editor.key=id;
editor.ready(function(){
$.each( _readyFn, function( index, fn ){
$.proxy( fn, editor )();
} );
});
var options = editor.options;
if(options.initialFrameWidth){
options.minFrameWidth = options.initialFrameWidth
}else{
options.minFrameWidth = options.initialFrameWidth = editor.$body.width() || UM.defaultWidth;
}
$container.css({
width: options.initialFrameWidth,
zIndex:editor.getOpt('zIndex')
});
//ie6下缓存图片
UM.browser.ie && UM.browser.version === 6 && document.execCommand("BackgroundImageCache", false, true);
editor.render(id);
//添加tooltip;
$.eduitooltip && $.eduitooltip('attachTo', $("[data-original-title]",$container)).css('z-index',editor.getOpt('zIndex')+1);
$container.find('a').click(function(evt){
evt.preventDefault()
});
editor.fireEvent("afteruiready");
}
return editor;
},
createUI: function (id, editor) {
var $editorCont = $(id),
$container = $('<div class="edui-container"><div class="edui-editor-body"></div></div>').insertBefore($editorCont);
editor.$container = $container;
editor.container = $container[0];
editor.$body = $editorCont;
//修正在ie9+以上的版本中,自动长高收起时的,残影问题
if(browser.ie && browser.ie9above){
var $span = $('<span style="padding:0;margin:0;height:0;width:0"></span>');
$span.insertAfter($container);
}
//初始化注册的ui组件
$.each(_editorUI,function(n,v){
var widget = v.call(editor,n);
if(widget){
_cacheUI[n] = widget;
}
});
$container.find('.edui-editor-body').append($editorCont).before(this.createToolbar(editor.options, editor));
$container.find('.edui-toolbar').append($('<div class="edui-dialog-container"></div>'));
return $container;
},
createToolbar: function (options, editor) {
var $toolbar = $.eduitoolbar(), toolbar = $toolbar.edui();
//创建下来菜单列表
if (options.toolbar && options.toolbar.length) {
var btns = [];
$.each(options.toolbar,function(i,uiNames){
$.each(uiNames.split(/\s+/),function(index,name){
if(name == '|'){
$.eduiseparator && btns.push($.eduiseparator());
}else{
var ui = _cacheUI[name];
if(name=="fullscreen"){
ui&&btns.unshift(ui);
}else{
ui && btns.push(ui);
}
}
});
btns.length && toolbar.appendToBtnmenu(btns);
});
} else {
$toolbar.find('.edui-btn-toolbar').remove()
}
return $toolbar;
}
})
})();
UM.registerUI('bold italic redo undo underline strikethrough superscript subscript insertorderedlist insertunorderedlist ' +
'cleardoc selectall link unlink print preview justifyleft justifycenter justifyright justifyfull removeformat horizontal drafts',
function(name) {
var me = this;
var $btn = $.eduibutton({
icon : name,
click : function(){
me.execCommand(name);
},
title: this.getLang('labelMap')[name] || ''
});
this.addListener('selectionchange',function(){
var state = this.queryCommandState(name);
$btn.edui().disabled(state == -1).active(state == 1)
});
return $btn;
}
);
/**
* 全屏组件
*/
(function(){
//状态缓存
var STATUS_CACHE = {},
//状态值列表
STATUS_LIST = [ 'width', 'height', 'position', 'top', 'left', 'margin', 'padding', 'overflowX', 'overflowY' ],
CONTENT_AREA_STATUS = {},
//页面状态
DOCUMENT_STATUS = {},
DOCUMENT_ELEMENT_STATUS = {},
FULLSCREENS = {};
UM.registerUI('fullscreen', function( name ){
var me = this,
$button = $.eduibutton({
'icon': 'fullscreen',
'title': (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
'click': function(){
//切换
me.execCommand( name );
UM.setTopEditor(me);
}
});
me.addListener( "selectionchange", function () {
var state = this.queryCommandState( name );
$button.edui().disabled( state == -1 ).active( state == 1 );
} );
//切换至全屏
me.addListener('ready', function(){
me.options.fullscreen && Fullscreen.getInstance( me ).toggle();
});
return $button;
});
UM.commands[ 'fullscreen' ] = {
execCommand: function (cmdName) {
Fullscreen.getInstance( this ).toggle();
},
queryCommandState: function (cmdName) {
return this._edui_fullscreen_status;
},
notNeedUndo: 1
};
function Fullscreen( editor ) {
var me = this;
if( !editor ) {
throw new Error('invalid params, notfound editor');
}
me.editor = editor;
//记录初始化的全屏组件
FULLSCREENS[ editor.uid ] = this;
editor.addListener('destroy', function(){
delete FULLSCREENS[ editor.uid ];
me.editor = null;
});
}
Fullscreen.prototype = {
/**
* 全屏状态切换
*/
toggle: function(){
var editor = this.editor,
//当前编辑器的缩放状态
_edui_fullscreen_status = this.isFullState();
editor.fireEvent('beforefullscreenchange', !_edui_fullscreen_status );
//更新状态
this.update( !_edui_fullscreen_status );
!_edui_fullscreen_status ? this.enlarge() : this.revert();
editor.fireEvent('afterfullscreenchange', !_edui_fullscreen_status );
if(editor.body.contentEditable === 'true'){
editor.fireEvent( 'fullscreenchanged', !_edui_fullscreen_status );
}
editor.fireEvent( 'selectionchange' );
},
/**
* 执行放大
*/
enlarge: function(){
this.saveSataus();
this.setDocumentStatus();
this.resize();
},
/**
* 全屏还原
*/
revert: function(){
//还原CSS表达式
var options = this.editor.options,
height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - this.getStyleValue("padding-top")- this.getStyleValue("padding-bottom") - this.getStyleValue('border-width'));
$.IE6 && this.getEditorHolder().style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
//还原容器状态
this.revertContainerStatus();
this.revertContentAreaStatus();
this.revertDocumentStatus();
},
/**
* 更新状态
* @param isFull 当前状态是否是全屏状态
*/
update: function( isFull ) {
this.editor._edui_fullscreen_status = isFull;
},
/**
* 调整当前编辑器的大小, 如果当前编辑器不处于全屏状态, 则不做调整
*/
resize: function(){
var $win = null,
height = 0,
width = 0,
borderWidth = 0,
paddingWidth = 0,
editor = this.editor,
me = this,
bound = null,
editorBody = null;
if( !this.isFullState() ) {
return;
}
$win = $( window );
width = $win.width();
height = $win.height();
editorBody = this.getEditorHolder();
//文本编辑区border宽度
borderWidth = parseInt( domUtils.getComputedStyle( editorBody, 'border-width' ), 10 ) || 0;
//容器border宽度
borderWidth += parseInt( domUtils.getComputedStyle( editor.container, 'border-width' ), 10 ) || 0;
//容器padding
paddingWidth += parseInt( domUtils.getComputedStyle( editorBody, 'padding-left' ), 10 ) + parseInt( domUtils.getComputedStyle( editorBody, 'padding-right' ), 10 ) || 0;
//干掉css表达式
$.IE6 && editorBody.style.setExpression( 'height', null );
bound = this.getBound();
$( editor.container ).css( {
width: width + 'px',
height: height + 'px',
position: !$.IE6 ? 'fixed' : 'absolute',
top: bound.top,
left: bound.left,
margin: 0,
padding: 0,
overflowX: 'hidden',
overflowY: 'hidden'
} );
$( editorBody ).css({
width: width - 2*borderWidth - paddingWidth + 'px',
height: height - 2*borderWidth - ( editor.options.withoutToolbar ? 0 : $( '.edui-toolbar', editor.container ).outerHeight() ) - $( '.edui-bottombar', editor.container).outerHeight() + 'px',
overflowX: 'hidden',
overflowY: 'auto'
});
},
/**
* 保存状态
*/
saveSataus: function(){
var styles = this.editor.container.style,
tmp = null,
cache = {};
for( var i= 0, len = STATUS_LIST.length; i<len; i++ ) {
tmp = STATUS_LIST[ i ];
cache[ tmp ] = styles[ tmp ];
}
STATUS_CACHE[ this.editor.uid ] = cache;
this.saveContentAreaStatus();
this.saveDocumentStatus();
},
saveContentAreaStatus: function(){
var $holder = $(this.getEditorHolder());
CONTENT_AREA_STATUS[ this.editor.uid ] = {
width: $holder.css("width"),
overflowX: $holder.css("overflowX"),
overflowY: $holder.css("overflowY"),
height: $holder.css("height")
};
},
/**
* 保存与指定editor相关的页面的状态
*/
saveDocumentStatus: function(){
var $doc = $( this.getEditorDocumentBody() );
DOCUMENT_STATUS[ this.editor.uid ] = {
overflowX: $doc.css( 'overflowX' ),
overflowY: $doc.css( 'overflowY' )
};
DOCUMENT_ELEMENT_STATUS[ this.editor.uid ] = {
overflowX: $( this.getEditorDocumentElement() ).css( 'overflowX'),
overflowY: $( this.getEditorDocumentElement() ).css( 'overflowY' )
};
},
/**
* 恢复容器状态
*/
revertContainerStatus: function(){
$( this.editor.container ).css( this.getEditorStatus() );
},
/**
* 恢复编辑区状态
*/
revertContentAreaStatus: function(){
var holder = this.getEditorHolder(),
state = this.getContentAreaStatus();
if ( this.supportMin() ) {
delete state.height;
holder.style.height = null;
}
$( holder ).css( state );
},
/**
* 恢复页面状态
*/
revertDocumentStatus: function() {
var status = this.getDocumentStatus();
$( this.getEditorDocumentBody() ).css( 'overflowX', status.body.overflowX );
$( this.getEditorDocumentElement() ).css( 'overflowY', status.html.overflowY );
},
setDocumentStatus: function(){
$(this.getEditorDocumentBody()).css( {
overflowX: 'hidden',
overflowY: 'hidden'
} );
$(this.getEditorDocumentElement()).css( {
overflowX: 'hidden',
overflowY: 'hidden'
} );
},
/**
* 检测当前编辑器是否处于全屏状态全屏状态
* @returns {boolean} 是否处于全屏状态
*/
isFullState: function(){
return !!this.editor._edui_fullscreen_status;
},
/**
* 获取编辑器状态
*/
getEditorStatus: function(){
return STATUS_CACHE[ this.editor.uid ];
},
getContentAreaStatus: function(){
return CONTENT_AREA_STATUS[ this.editor.uid ];
},
getEditorDocumentElement: function(){
return this.editor.container.ownerDocument.documentElement;
},
getEditorDocumentBody: function(){
return this.editor.container.ownerDocument.body;
},
/**
* 获取编辑区包裹对象
*/
getEditorHolder: function(){
return this.editor.body;
},
/**
* 获取编辑器状态
* @returns {*}
*/
getDocumentStatus: function(){
return {
'body': DOCUMENT_STATUS[ this.editor.uid ],
'html': DOCUMENT_ELEMENT_STATUS[ this.editor.uid ]
};
},
supportMin: function () {
var node = null;
if ( !this._support ) {
node = document.createElement("div");
this._support = "minWidth" in node.style;
node = null;
}
return this._support;
},
getBound: function () {
var tags = {
html: true,
body: true
},
result = {
top: 0,
left: 0
},
offsetParent = null;
if ( !$.IE6 ) {
return result;
}
offsetParent = this.editor.container.offsetParent;
if( offsetParent && !tags[ offsetParent.nodeName.toLowerCase() ] ) {
tags = offsetParent.getBoundingClientRect();
result.top = -tags.top;
result.left = -tags.left;
}
return result;
},
getStyleValue: function (attr) {
return parseInt(domUtils.getComputedStyle( this.getEditorHolder() ,attr));
}
};
$.extend( Fullscreen, {
/**
* 监听resize
*/
listen: function(){
var timer = null;
if( Fullscreen._hasFullscreenListener ) {
return;
}
Fullscreen._hasFullscreenListener = true;
$( window ).on( 'resize', function(){
if( timer !== null ) {
window.clearTimeout( timer );
timer = null;
}
timer = window.setTimeout(function(){
for( var key in FULLSCREENS ) {
FULLSCREENS[ key ].resize();
}
timer = null;
}, 50);
} );
},
getInstance: function ( editor ) {
if ( !FULLSCREENS[editor.uid ] ) {
new Fullscreen( editor );
}
return FULLSCREENS[editor.uid ];
}
});
//开始监听
Fullscreen.listen();
})();
UM.registerUI('link image video map formula',function(name){
var me = this, currentRange, $dialog,
opt = {
title: (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
url: me.options.UMEDITOR_HOME_URL + 'dialogs/' + name + '/' + name + '.js'
};
var $btn = $.eduibutton({
icon: name,
title: this.getLang('labelMap')[name] || ''
});
//加载模版数据
utils.loadFile(document,{
src: opt.url,
tag: "script",
type: "text/javascript",
defer: "defer"
},function(){
//调整数据
var data = UM.getWidgetData(name);
if(!data) return;
if(data.buttons){
var ok = data.buttons.ok;
if(ok){
opt.oklabel = ok.label || me.getLang('ok');
if(ok.exec){
opt.okFn = function(){
return $.proxy(ok.exec,null,me,$dialog)()
}
}
}
var cancel = data.buttons.cancel;
if(cancel){
opt.cancellabel = cancel.label || me.getLang('cancel');
if(cancel.exec){
opt.cancelFn = function(){
return $.proxy(cancel.exec,null,me,$dialog)()
}
}
}
}
data.width && (opt.width = data.width);
data.height && (opt.height = data.height);
$dialog = $.eduimodal(opt);
$dialog.attr('id', 'edui-dialog-' + name).addClass('edui-dialog-' + name)
.find('.edui-modal-body').addClass('edui-dialog-' + name + '-body');
$dialog.edui().on('beforehide',function () {
var rng = me.selection.getRange();
if (rng.equals(currentRange)) {
rng.select()
}
}).on('beforeshow', function () {
var $root = this.root(),
win = null,
offset = null;
currentRange = me.selection.getRange();
if (!$root.parent()[0]) {
me.$container.find('.edui-dialog-container').append($root);
}
//IE6下 特殊处理, 通过计算进行定位
if( $.IE6 ) {
win = {
width: $( window ).width(),
height: $( window ).height()
};
offset = $root.parents(".edui-toolbar")[0].getBoundingClientRect();
$root.css({
position: 'absolute',
margin: 0,
left: ( win.width - $root.width() ) / 2 - offset.left,
top: 100 - offset.top
});
}
UM.setWidgetBody(name,$dialog,me);
UM.setTopEditor(me);
}).on('afterbackdrop',function(){
this.$backdrop.css('zIndex',me.getOpt('zIndex')+1).appendTo(me.$container.find('.edui-dialog-container'))
$dialog.css('zIndex',me.getOpt('zIndex')+2)
}).on('beforeok',function(){
try{
currentRange.select()
}catch(e){}
}).attachTo($btn)
});
me.addListener('selectionchange', function () {
var state = this.queryCommandState(name);
$btn.edui().disabled(state == -1).active(state == 1)
});
return $btn;
});
UM.registerUI( 'emotion formula', function( name ){
var me = this,
url = me.options.UMEDITOR_HOME_URL + 'dialogs/' +name+ '/'+name+'.js';
var $btn = $.eduibutton({
icon: name,
title: this.getLang('labelMap')[name] || ''
});
//加载模版数据
utils.loadFile(document,{
src: url,
tag: "script",
type: "text/javascript",
defer: "defer"
},function(){
var opt = {
url : url
};
//调整数据
var data = UM.getWidgetData(name);
data.width && (opt.width = data.width);
data.height && (opt.height = data.height);
$.eduipopup(opt).css('zIndex',me.options.zIndex + 1)
.addClass('edui-popup-' + name)
.edui()
.on('beforeshow',function(){
var $root = this.root();
if(!$root.parent().length){
me.$container.find('.edui-dialog-container').append($root);
}
UM.setWidgetBody(name,$root,me);
UM.setTopEditor(me);
}).attachTo($btn,{
offsetTop:-5,
offsetLeft:10,
caretLeft:11,
caretTop:-8
});
me.addListener('selectionchange', function () {
var state = this.queryCommandState(name);
$btn.edui().disabled(state == -1).active(state == 1);
});
});
return $btn;
} );
UM.registerUI('imagescale',function () {
var me = this,
$imagescale;
me.setOpt('imageScaleEnabled', true);
if (browser.webkit && me.getOpt('imageScaleEnabled')) {
me.addListener('click', function (type, e) {
var range = me.selection.getRange(),
img = range.getClosedNode(),
target = e.target;
/* 点击第一个图片的后面,八个角不消失 fix:3652 */
if (img && img.tagName == 'IMG' && target == img) {
if (!$imagescale) {
$imagescale = $.eduiscale({'$wrap':me.$container}).css('zIndex', me.options.zIndex);
me.$container.append($imagescale);
var _keyDownHandler = function () {
$imagescale.edui().hide();
}, _mouseDownHandler = function (e) {
var ele = e.target || e.srcElement;
if (ele && ele.className.indexOf('edui-scale') == -1) {
_keyDownHandler(e);
}
}, timer;
$imagescale.edui()
.on('aftershow', function () {
$(document).bind('keydown', _keyDownHandler);
$(document).bind('mousedown', _mouseDownHandler);
me.selection.getNative().removeAllRanges();
})
.on('afterhide', function () {
$(document).unbind('keydown', _keyDownHandler);
$(document).unbind('mousedown', _mouseDownHandler);
var target = $imagescale.edui().getScaleTarget();
if (target.parentNode) {
me.selection.getRange().selectNode(target).select();
}
})
.on('mousedown', function (e) {
me.selection.getNative().removeAllRanges();
var ele = e.target || e.srcElement;
if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
timer = setTimeout(function() {
$imagescale.edui().hide();
}, 200);
}
})
.on('mouseup', function (e) {
var ele = e.target || e.srcElement;
if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
clearTimeout(timer);
}
});
}
$imagescale.edui().show($(img));
} else {
if ($imagescale && $imagescale.css('display') != 'none') $imagescale.edui().hide();
}
});
me.addListener('click', function (type, e) {
if (e.target.tagName == 'IMG') {
var range = new dom.Range(me.document, me.body);
range.selectNode(e.target).select();
}
});
}
});
UM.registerUI('autofloat',function(){
var me = this,
lang = me.getLang();
me.setOpt({
autoFloatEnabled: true,
topOffset: 0
});
var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
topOffset = me.options.topOffset;
//如果不固定toolbar的位置则直接退出
if(!optsAutoFloatEnabled){
return;
}
me.ready(function(){
var LteIE6 = browser.ie && browser.version <= 6,
quirks = browser.quirks;
function checkHasUI(){
if(!UM.ui){
alert(lang.autofloatMsg);
return 0;
}
return 1;
}
function fixIE6FixedPos(){
var docStyle = document.body.style;
docStyle.backgroundImage = 'url("about:blank")';
docStyle.backgroundAttachment = 'fixed';
}
var bakCssText,
placeHolder = document.createElement('div'),
toolbarBox,orgTop,
getPosition=function(element){
var bcr;
//trace IE6下在控制编辑器显隐时可能会报错catch一下
try{
bcr = element.getBoundingClientRect();
}catch(e){
bcr={left:0,top:0,height:0,width:0}
}
var rect = {
left: Math.round(bcr.left),
top: Math.round(bcr.top),
height: Math.round(bcr.bottom - bcr.top),
width: Math.round(bcr.right - bcr.left)
};
var doc;
while ((doc = element.ownerDocument) !== document &&
(element = domUtils.getWindow(doc).frameElement)) {
bcr = element.getBoundingClientRect();
rect.left += bcr.left;
rect.top += bcr.top;
}
rect.bottom = rect.top + rect.height;
rect.right = rect.left + rect.width;
return rect;
};
var isFullScreening = false;
function setFloating(){
if(isFullScreening){
return;
}
var toobarBoxPos = domUtils.getXY(toolbarBox),
origalFloat = domUtils.getComputedStyle(toolbarBox,'position'),
origalLeft = domUtils.getComputedStyle(toolbarBox,'left');
toolbarBox.style.width = toolbarBox.offsetWidth + 'px';
toolbarBox.style.zIndex = me.options.zIndex * 1 + 1;
toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox);
if (LteIE6 || (quirks && browser.ie)) {
if(toolbarBox.style.position != 'absolute'){
toolbarBox.style.position = 'absolute';
}
toolbarBox.style.top = (document.body.scrollTop||document.documentElement.scrollTop) - orgTop + topOffset + 'px';
} else {
if(toolbarBox.style.position != 'fixed'){
toolbarBox.style.position = 'fixed';
toolbarBox.style.top = topOffset +"px";
((origalFloat == 'absolute' || origalFloat == 'relative') && parseFloat(origalLeft)) && (toolbarBox.style.left = toobarBoxPos.x + 'px');
}
}
}
function unsetFloating(){
if(placeHolder.parentNode){
placeHolder.parentNode.removeChild(placeHolder);
}
toolbarBox.style.cssText = bakCssText;
}
function updateFloating(){
var rect3 = getPosition(me.container);
var offset=me.options.toolbarTopOffset||0;
if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
setFloating();
}else{
unsetFloating();
}
}
var defer_updateFloating = utils.defer(function(){
updateFloating();
},browser.ie ? 200 : 100,true);
me.addListener('destroy',function(){
$(window).off('scroll resize',updateFloating);
me.removeListener('keydown', defer_updateFloating);
});
if(checkHasUI(me)){
toolbarBox = $('.edui-toolbar',me.container)[0];
me.addListener("afteruiready",function(){
setTimeout(function(){
orgTop = $(toolbarBox).offset().top;
},100);
});
bakCssText = toolbarBox.style.cssText;
placeHolder.style.height = toolbarBox.offsetHeight + 'px';
if(LteIE6){
fixIE6FixedPos();
}
$(window).on('scroll resize',updateFloating);
me.addListener('keydown', defer_updateFloating);
me.addListener('resize', function(){
unsetFloating();
placeHolder.style.height = toolbarBox.offsetHeight + 'px';
updateFloating();
});
me.addListener('beforefullscreenchange', function (t, enabled){
if (enabled) {
unsetFloating();
isFullScreening = enabled;
}
});
me.addListener('fullscreenchanged', function (t, enabled){
if (!enabled) {
updateFloating();
}
isFullScreening = enabled;
});
me.addListener('sourcemodechanged', function (t, enabled){
setTimeout(function (){
updateFloating();
},0);
});
me.addListener("clearDoc",function(){
setTimeout(function(){
updateFloating();
},0);
})
}
})
});
UM.registerUI('source',function(name){
var me = this;
me.addListener('fullscreenchanged',function(){
me.$container.find('textarea').width(me.$body.width() - 10).height(me.$body.height())
});
var $btn = $.eduibutton({
icon : name,
click : function(){
me.execCommand(name);
UM.setTopEditor(me);
},
title: this.getLang('labelMap')[name] || ''
});
this.addListener('selectionchange',function(){
var state = this.queryCommandState(name);
$btn.edui().disabled(state == -1).active(state == 1)
});
return $btn;
});
UM.registerUI('paragraph fontfamily fontsize', function( name ) {
var me = this,
label = (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
options = {
label: label,
title: label,
comboboxName: name,
items: me.options[ name ] || [],
itemStyles: [],
value: [],
autowidthitem: []
},
$combox = null,
comboboxWidget = null;
if(options.items.length == 0){
return null;
}
switch ( name ) {
case 'paragraph':
options = transForParagraph( options );
break;
case 'fontfamily':
options = transForFontfamily( options );
break;
case 'fontsize':
options = transForFontsize( options );
break;
}
//实例化
$combox = $.eduibuttoncombobox(options).css('zIndex',me.getOpt('zIndex') + 1);
comboboxWidget = $combox.edui();
comboboxWidget.on('comboboxselect', function( evt, res ){
me.execCommand( name, res.value );
}).on("beforeshow", function(){
if( $combox.parent().length === 0 ) {
$combox.appendTo( me.$container.find('.edui-dialog-container') );
}
UM.setTopEditor(me);
});
//状态反射
this.addListener('selectionchange',function( evt ){
var state = this.queryCommandState( name ),
value = this.queryCommandValue( name );
//设置按钮状态
comboboxWidget.button().edui().disabled( state == -1 ).active( state == 1 );
if(value){
//设置label
value = value.replace(/['"]/g, '').toLowerCase().split(/['|"]?\s*,\s*[\1]?/);
comboboxWidget.selectItemByLabel( value );
}
});
return comboboxWidget.button().addClass('edui-combobox');
/**
* 宽度自适应工具函数
* @param word 单词内容
* @param hasSuffix 是否含有后缀
*/
function wordCountAdaptive ( word, hasSuffix ) {
var $tmpNode = $('<span>' ).html( word ).css( {
display: 'inline',
position: 'absolute',
top: -10000000,
left: -100000
} ).appendTo( document.body),
width = $tmpNode.width();
$tmpNode.remove();
$tmpNode = null;
if( width < 50 ) {
return word;
} else {
word = word.slice( 0, hasSuffix ? -4 : -1 );
if( !word.length ) {
return '...';
}
return wordCountAdaptive( word + '...', true );
}
}
//段落参数转换
function transForParagraph ( options ) {
var tempItems = [];
for( var key in options.items ) {
options.value.push( key );
tempItems.push( key );
options.autowidthitem.push( wordCountAdaptive( key ) );
}
options.items = tempItems;
options.autoRecord = false;
return options;
}
//字体参数转换
function transForFontfamily ( options ) {
var temp = null,
tempItems = [];
for( var i = 0, len = options.items.length; i < len; i++ ) {
temp = options.items[ i ].val;
tempItems.push( temp.split(/\s*,\s*/)[0] );
options.itemStyles.push('font-family: ' + temp);
options.value.push( temp );
options.autowidthitem.push( wordCountAdaptive( tempItems[ i ] ) );
}
options.items = tempItems;
return options;
}
//字体大小参数转换
function transForFontsize ( options ) {
var temp = null,
tempItems = [];
options.itemStyles = [];
options.value = [];
for( var i = 0, len = options.items.length; i < len; i++ ) {
temp = options.items[ i ];
tempItems.push( temp );
options.itemStyles.push('font-size: ' + temp +'px');
}
options.value = options.items;
options.items = tempItems;
options.autoRecord = false;
return options;
}
});
UM.registerUI('forecolor backcolor', function( name ) {
function getCurrentColor() {
return domUtils.getComputedStyle( $colorLabel[0], 'background-color' );
}
var me = this,
$colorPickerWidget = null,
$colorLabel = null,
$btn = null;
//querycommand
this.addListener('selectionchange', function(){
var state = this.queryCommandState( name );
$btn.edui().disabled( state == -1 ).active( state == 1 );
});
$btn = $.eduicolorsplitbutton({
icon: name,
caret: true,
name: name,
title: me.getLang("labelMap")[name],
click: function() {
me.execCommand( name, getCurrentColor() );
}
});
$colorLabel = $btn.edui().colorLabel();
$colorPickerWidget = $.eduicolorpicker({
name: name,
lang_clearColor: me.getLang('clearColor') || '',
lang_themeColor: me.getLang('themeColor') || '',
lang_standardColor: me.getLang('standardColor') || ''
})
.on('pickcolor', function( evt, color ){
window.setTimeout( function(){
$colorLabel.css("backgroundColor", color);
me.execCommand( name, color );
}, 0 );
})
.on('show',function(){
UM.setActiveWidget( colorPickerWidget.root() );
}).css('zIndex',me.getOpt('zIndex') + 1);
$btn.edui().on('arrowclick',function(){
if(!$colorPickerWidget.parent().length){
me.$container.find('.edui-dialog-container').append($colorPickerWidget);
}
$colorPickerWidget.edui().show($btn,{
caretDir:"down",
offsetTop:-5,
offsetLeft:8,
caretLeft:11,
caretTop:-8
});
UM.setTopEditor(me);
}).register('click', $btn, function () {
$colorPickerWidget.edui().hide()
});
return $btn;
});
})(jQuery)