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