o: ActiveSupport::Cache::Entry :@compressedF:@expires_in0:@created_atf1364460361.022398:@value"!5{I" class:EFI"ProcessedAsset; FI"logical_path; FI"ueditor/editor_all.js; TI" pathname; FI"]/Users/jasl/workspace/ruby/ueditor_rails/vendor/assets/javascripts/ueditor/editor_all.js; TI"content_type; FI"application/javascript; FI" mtime; FI"2013-02-04T14:01:38+08:00; FI"length; Fi1I"digest; F"%db0c7cdfb45b1451cb57c5633fc55f68I"source; FI"1UEDITOR_CONFIG = window.UEDITOR_CONFIG || {}; var baidu = window.baidu || {}; window.baidu = baidu; window.UE = baidu.editor = {}; UE.plugins = {}; UE.commands = {}; UE.instants = {}; UE.I18N = {}; UE.version = "1.2.5.0"; var dom = UE.dom = {};/** * @file * @name UE.browser * @short Browser * @desc UEditor中采用的浏览器判断模块 */ var browser = UE.browser = function(){ var agent = navigator.userAgent.toLowerCase(), opera = window.opera, browser = { /** * 检测浏览器是否为IE * @name ie * @grammar UE.browser.ie => true|false */ ie : !!window.ActiveXObject, /** * 检测浏览器是否为Opera * @name opera * @grammar UE.browser.opera => true|false */ opera : ( !!opera && opera.version ), /** * 检测浏览器是否为webkit内核 * @name webkit * @grammar UE.browser.webkit => true|false */ webkit : ( agent.indexOf( ' applewebkit/' ) > -1 ), /** * 检测浏览器是否为mac系统下的浏览器 * @name mac * @grammar UE.browser.mac => true|false */ mac : ( agent.indexOf( 'macintosh' ) > -1 ), /** * 检测浏览器是否处于怪异模式 * @name quirks * @grammar UE.browser.quirks => true|false */ quirks : ( document.compatMode == 'BackCompat' ) }; /** * 检测浏览器是否处为gecko内核 * @name gecko * @grammar UE.browser.gecko => true|false */ browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera ); var version = 0; // Internet Explorer 6.0+ if ( browser.ie ){ version = parseFloat( agent.match( /msie (\d+)/ )[1] ); /** * 检测浏览器是否为 IE9 模式 * @name ie9Compat * @grammar UE.browser.ie9Compat => true|false */ browser.ie9Compat = document.documentMode == 9; /** * 检测浏览器是否为 IE8 浏览器 * @name ie8 * @grammar UE.browser.ie8 => true|false */ browser.ie8 = !!document.documentMode; /** * 检测浏览器是否为 IE8 模式 * @name ie8Compat * @grammar UE.browser.ie8Compat => true|false */ browser.ie8Compat = document.documentMode == 8; /** * 检测浏览器是否运行在 兼容IE7模式 * @name ie7Compat * @grammar UE.browser.ie7Compat => true|false */ browser.ie7Compat = ( ( version == 7 && !document.documentMode ) || document.documentMode == 7 ); /** * 检测浏览器是否IE6模式或怪异模式 * @name ie6Compat * @grammar UE.browser.ie6Compat => true|false */ browser.ie6Compat = ( version < 7 || browser.quirks ); } // Gecko. if ( browser.gecko ){ var geckoRelease = agent.match( /rv:([\d\.]+)/ ); if ( geckoRelease ) { geckoRelease = geckoRelease[1].split( '.' ); version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1; } } /** * 检测浏览器是否为chrome * @name chrome * @grammar UE.browser.chrome => true|false */ if (/chrome\/(\d+\.\d)/i.test(agent)) { browser.chrome = + RegExp['\x241']; } /** * 检测浏览器是否为safari * @name safari * @grammar UE.browser.safari => true|false */ 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] ); /** * 浏览器版本判断 * IE系列返回值为5,6,7,8,9,10等 * gecko系列会返回10900,158900等. * webkit系列会返回其build号 (如 522等). * @name version * @grammar UE.browser.version => number * @example * if ( UE.browser.ie && UE.browser.version == 6 ){ * alert( "Ouch!居然是万恶的IE6!" ); * } */ browser.version = version; /** * 是否是兼容模式的浏览器 * @name isCompatible * @grammar UE.browser.isCompatible => true|false * @example * if ( UE.browser.isCompatible ){ * alert( "你的浏览器相当不错哦!" ); * } */ browser.isCompatible = !browser.mobile && ( ( browser.ie && version >= 6 ) || ( browser.gecko && version >= 10801 ) || ( browser.opera && version >= 9.5 ) || ( browser.air && version >= 1 ) || ( browser.webkit && version >= 522 ) || false ); return browser; }(); //快捷方式 var ie = browser.ie, webkit = browser.webkit, gecko = browser.gecko, opera = browser.opera;/** * @file * @name UE.Utils * @short Utils * @desc UEditor封装使用的静态工具函数 * @import editor.js */ var utils = UE.utils = { /** * 遍历数组,对象,nodeList * @name each * @grammar UE.utils.each(obj,iterator,[context]) * @since 1.2.4+ * @desc * * obj 要遍历的对象 * * iterator 遍历的方法,方法的第一个是遍历的值,第二个是索引,第三个是obj * * context iterator的上下文 * @example * UE.utils.each([1,2],function(v,i){ * console.log(v)//值 * console.log(i)//索引 * }) * UE.utils.each(document.getElementsByTagName('*'),function(n){ * console.log(n.tagName) * }) */ each : function(obj, iterator, context) { if (obj == null) return; if (obj.length === +obj.length) { for (var i = 0, l = obj.length; i < l; i++) { if(iterator.call(context, obj[i], i, obj) === false) return false; } } else { for (var key in obj) { if (obj.hasOwnProperty(key)) { if(iterator.call(context, obj[key], key, obj) === false) return false; } } } }, makeInstance:function (obj) { var noop = new Function(); noop.prototype = obj; obj = new noop; noop.prototype = null; return obj; }, /** * 将source对象中的属性扩展到target对象上 * @name extend * @grammar UE.utils.extend(target,source) => Object //覆盖扩展 * @grammar UE.utils.extend(target,source,true) ==> Object //保留扩展 */ extend:function (t, s, b) { if (s) { for (var k in s) { if (!b || !t.hasOwnProperty(k)) { t[k] = s[k]; } } } return t; }, /** * 模拟继承机制,subClass继承superClass * @name inherits * @grammar UE.utils.inherits(subClass,superClass) => subClass * @example * function SuperClass(){ * this.name = "小李"; * } * SuperClass.prototype = { * hello:function(str){ * console.log(this.name + str); * } * } * function SubClass(){ * this.name = "小张"; * } * UE.utils.inherits(SubClass,SuperClass); * var sub = new SubClass(); * sub.hello("早上好!"); ==> "小张早上好!" */ inherits:function (subClass, superClass) { var oldP = subClass.prototype, newP = utils.makeInstance(superClass.prototype); utils.extend(newP, oldP, true); subClass.prototype = newP; return (newP.constructor = subClass); }, /** * 用指定的context作为fn上下文,也就是this * @name bind * @grammar UE.utils.bind(fn,context) => fn */ bind:function (fn, context) { return function () { return fn.apply(context, arguments); }; }, /** * 创建延迟delay执行的函数fn * @name defer * @grammar UE.utils.defer(fn,delay) =>fn //延迟delay毫秒执行fn,返回fn * @grammar UE.utils.defer(fn,delay,exclusion) =>fn //延迟delay毫秒执行fn,若exclusion为真,则互斥执行fn * @example * function test(){ * console.log("延迟输出!"); * } * //非互斥延迟执行 * var testDefer = UE.utils.defer(test,1000); * testDefer(); => "延迟输出!"; * testDefer(); => "延迟输出!"; * //互斥延迟执行 * var testDefer1 = UE.utils.defer(test,1000,true); * testDefer1(); => //本次不执行 * testDefer1(); => "延迟输出!"; */ defer:function (fn, delay, exclusion) { var timerID; return function () { if (exclusion) { clearTimeout(timerID); } timerID = setTimeout(fn, delay); }; }, /** * 查找元素item在数组array中的索引, 若找不到返回-1 * @name indexOf * @grammar UE.utils.indexOf(array,item) => index|-1 //默认从数组开头部开始搜索 * @grammar UE.utils.indexOf(array,item,start) => index|-1 //start指定开始查找的位置 */ indexOf:function (array, item, start) { var index = -1; start = this.isNumber(start) ? start : 0; this.each(array,function(v,i){ if(i >= start && v === item){ index = i; return false; } }); return index; }, /** * 移除数组array中的元素item * @name removeItem * @grammar UE.utils.removeItem(array,item) */ removeItem:function (array, item) { for (var i = 0, l = array.length; i < l; i++) { if (array[i] === item) { array.splice(i, 1); i--; } } }, /** * 删除字符串str的首尾空格 * @name trim * @grammar UE.utils.trim(str) => String */ trim:function (str) { return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, ''); }, /** * 将字符串list(以','分隔)或者数组list转成哈希对象 * @name listToMap * @grammar UE.utils.listToMap(list) => Object //Object形如{test:1,br:1,textarea:1} */ listToMap:function (list) { if (!list)return {}; list = utils.isArray(list) ? list : list.split(','); for (var i = 0, ci, obj = {}; ci = list[i++];) { obj[ci.toUpperCase()] = obj[ci] = 1; } return obj; }, /** * 将str中的html符号转义,默认将转义''&<">''四个字符,可自定义reg来确定需要转义的字符 * @name unhtml * @grammar UE.utils.unhtml(str); => String * @grammar UE.utils.unhtml(str,reg) => String * @example * var html = '
You say:"你好!Baidu & UEditor!"'; * UE.utils.unhtml(html); ==> <body>You say:"你好!Baidu & UEditor!"</body> * UE.utils.unhtml(html,/[<>]/g) ==> <body>You say:"你好!Baidu & UEditor!"</body> */ unhtml:function (str, reg) { return str ? str.replace(reg || /[&<">]/g, function (m) { return { '<':'<', '&':'&', '"':'"', '>':'>' }[m] }) : ''; }, /** * 将str中的转义字符还原成html字符 * @name html * @grammar UE.utils.html(str) => String //详细参见unhtml
*/
html:function (str) {
return str ? str.replace(/&((g|l|quo)t|amp);/g, function (m) {
return {
'<':'<',
'&':'&',
'"':'"',
'>':'>'
}[m]
}) : '';
},
/**
* 将css样式转换为驼峰的形式。如font-size => fontSize
* @name cssStyleToDomStyle
* @grammar UE.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 UE.utils.loadFile(doc,obj)
* @grammar UE.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 editor_config.js ')
};
doc.getElementsByTagName("head")[0].appendChild(element);
}
}(),
/**
* 判断obj对象是否为空
* @name isEmptyObject
* @grammar UE.utils.isEmptyObject(obj) => true|false
* @example
* UE.utils.isEmptyObject({}) ==>true
* UE.utils.isEmptyObject([]) ==>true
* UE.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 UE.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;
},
/**
* 只针对border,padding,margin做了处理,因为性能问题
* @public
* @function
* @param {String} val style字符串
*/
optCss:function (val) {
var padding, margin, border;
val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function (str, key, name, val) {
if (val.split(' ').length == 1) {
switch (key) {
case 'padding':
!padding && (padding = {});
padding[name] = val;
return '';
case 'margin':
!margin && (margin = {});
margin[name] = val;
return '';
case 'border':
return val == 'initial' ? '' : str;
}
}
return str;
});
function opt(obj, name) {
if (!obj) {
return '';
}
var t = obj.top , b = obj.bottom, l = obj.left, r = obj.right, val = '';
if (!t || !l || !b || !r) {
for (var p in obj) {
val += ';' + name + '-' + p + ':' + obj[p] + ';';
}
} else {
val += ';' + name + ':' +
(t == b && b == l && l == r ? t :
t == b && l == r ? (t + ' ' + l) :
l == r ? (t + ' ' + l + ' ' + b) : (t + ' ' + r + ' ' + b + ' ' + l)) + ';'
}
return val;
}
val += opt(padding, 'padding') + opt(margin, 'margin');
return val.replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '').replace(/;([ \n\r\t]+)|\1;/g, ';')
.replace(/(&((l|g)t|quot|#39))?;{2,}/g, function (a, b) {
return b ? b + ";;" : ';'
});
},
/**
* 深度克隆对象,从source到target
* @name clone
* @grammar UE.utils.clone(source) => anthorObj 新的对象是完整的source的副本
* @grammar UE.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 UE.utils.transUnitToPx('20pt') => '27px'
* @grammar UE.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':'');
},
/**
* DomReady方法,回调函数将在dom树ready完成后执行
* @name domReady
* @grammar UE.utils.domReady(fn) => fn //返回一个延迟执行的方法
*/
domReady:function () {
var fnArr = [];
function doReady(doc) {
//确保onready只执行一次
doc.isReady = true;
for (var ci; ci = fnArr.pop();ci()){}
}
return function (onready,win) {
win = win || window;
var doc = win.document;
onready && fnArr.push(onready);
if (doc.readyState === "complete") {
doReady(doc);
}else{
doc.isReady && doReady(doc);
if (browser.ie) {
(function () {
if (doc.isReady) return;
try {
doc.documentElement.doScroll("left");
} catch (error) {
setTimeout(arguments.callee, 0);
return;
}
doReady(doc);
})();
win.attachEvent('onload', function(){
doReady(doc)
});
} else {
doc.addEventListener("DOMContentLoaded", function () {
doc.removeEventListener("DOMContentLoaded", arguments.callee, false);
doReady(doc);
}, false);
win.addEventListener('load', function(){doReady(doc)}, false);
}
}
}
}(),
/**
* 动态添加css样式
* @name cssRule
* @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
* @grammar UE.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
* @grammar UE.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
* @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
*/
cssRule : browser.ie ? 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 UE.utils.isString(str) => true|false
*/
/**
* 判断array是否为数组
* @name isArray
* @grammar UE.utils.isArray(obj) => true|false
*/
/**
* 判断obj对象是否为方法
* @name isFunction
* @grammar UE.utils.isFunction(obj) => true|false
*/
/**
* 判断obj对象是否为数字
* @name isNumber
* @grammar UE.utils.isNumber(obj) => true|false
*/
utils.each(['String','Function','Array','Number','RegExp'],function(v){
UE.utils['is' + v] = function(obj){
return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
}
});/**
* @file
* @name UE.EventBase
* @short EventBase
* @import editor.js,core/utils.js
* @desc UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
* 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
*/
var EventBase = UE.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 (types) {
types = utils.trim(types).split(' ');
for (var i = 0, ti; ti = types[i++];) {
var listeners = getListener(this, ti),
r, t, k;
if (listeners) {
k = listeners.length;
while (k--) {
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] = [] ) ) );
}
/**
* @file
* @name UE.ajax
* @short Ajax
* @desc UEditor内置的ajax请求模块
* @import core/utils.js
* @user: taoqili
* @date: 11-8-18
* @time: 下午3:18
*/
UE.ajax = function() {
/**
* 创建一个ajaxRequest对象
*/
var fnStr = 'XMLHttpRequest()';
try {
new ActiveXObject("Msxml2.XMLHTTP");
fnStr = 'ActiveXObject(\'Msxml2.XMLHTTP\')';
} catch (e) {
try {
new ActiveXObject("Microsoft.XMLHTTP");
fnStr = 'ActiveXObject(\'Microsoft.XMLHTTP\')'
} catch (e) {
}
}
var creatAjaxRequest = new Function('return new ' + fnStr);
/**
* 将json参数转化成适合ajax提交的参数列表
* @param json
*/
function json2str(json) {
var strArr = [];
for (var i in json) {
//忽略默认的几个参数
if(i=="method" || i=="timeout" || i=="async") continue;
//传递过来的对象和函数不在提交之列
if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) );
}
}
return strArr.join("&");
}
return {
/**
* @name request
* @desc 发出ajax请求,ajaxOpt中默认包含method,timeout,async,data,onsuccess以及onerror等六个,支持自定义添加参数
* @grammar UE.ajax.request(url,ajaxOpt);
* @example
* UE.ajax.request('http://www.xxxx.com/test.php',{
* //可省略,默认POST
* method:'POST',
* //可以自定义参数
* content:'这里是提交的内容',
* //也可以直接传json,但是只能命名为data,否则当做一般字符串处理
* data:{
* name:'UEditor',
* age:'1'
* }
* onsuccess:function(xhr){
* console.log(xhr.responseText);
* },
* onerror:function(xhr){
* console.log(xhr.responseText);
* }
* })
* @param ajaxOptions
*/
request:function(url, ajaxOptions) {
var ajaxRequest = creatAjaxRequest(),
//是否超时
timeIsOut = false,
//默认参数
defaultAjaxOptions = {
method:"POST",
timeout:5000,
async:true,
data:{},//需要传递对象的话只能覆盖
onsuccess:function() {
},
onerror:function() {
}
};
if (typeof url === "object") {
ajaxOptions = url;
url = ajaxOptions.url;
}
if (!ajaxRequest || !url) return;
var ajaxOpts = ajaxOptions ? utils.extend(defaultAjaxOptions,ajaxOptions) : defaultAjaxOptions;
var submitStr = json2str(ajaxOpts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
//如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
if (!utils.isEmptyObject(ajaxOpts.data)){
submitStr += (submitStr? "&":"") + json2str(ajaxOpts.data);
}
//超时检测
var timerID = setTimeout(function() {
if (ajaxRequest.readyState != 4) {
timeIsOut = true;
ajaxRequest.abort();
clearTimeout(timerID);
}
}, ajaxOpts.timeout);
var method = ajaxOpts.method.toUpperCase();
var str = url + (url.indexOf("?")==-1?"?":"&") + (method=="POST"?"":submitStr+ "&noCache=" + +new Date);
ajaxRequest.open(method, str, ajaxOpts.async);
ajaxRequest.onreadystatechange = function() {
if (ajaxRequest.readyState == 4) {
if (!timeIsOut && ajaxRequest.status == 200) {
ajaxOpts.onsuccess(ajaxRequest);
} else {
ajaxOpts.onerror(ajaxRequest);
}
}
};
if (method == "POST") {
ajaxRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
ajaxRequest.send(submitStr);
} else {
ajaxRequest.send(null);
}
}
};
}();
/**
* @file
* @name UE.filterWord
* @short filterWord
* @desc 用来过滤word粘贴过来的字符串
* @import editor.js,core/utils.js
* @anthor zhanyi
*/
var filterWord = UE.filterWord = function () {
//是否是word过来的内容
function isWordDocument( str ) {
return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "
$1
" ) //去掉多余的属性 .replace( /\s+(class|lang|align)\s*=\s*(['"]?)[\w-]+\2/ig, "" ) //清除多余的font/span不能匹配 有可能是空格 .replace( /<(font|span)[^>]*>\s*<\/\1>/gi, '' ) //处理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(!/on
*/
un:function (element, type, handler) {
var types = utils.isArray(type) ? type : [type],
k = types.length;
if (k) while (k--) {
type = types[k];
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else {
var key = type + handler.toString();
try{
element.detachEvent('on' + type, handler._d ? handler._d[key] : handler);
}catch(e){}
if (handler._d && handler._d[key]) {
var index = utils.indexOf(handler._d.els,element);
if(index!=-1){
handler._d.els.splice(index,1);
}
handler._d.els.length == 0 && delete handler._d[key];
}
}
}
},
/**
* 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
* @name isSameElement
* @grammar UE.dom.domUtils.isSameElement(nodeA,nodeB) => true|false
* @example
* ssss and bbbbb => true
* ssss and bbbbb => false
*/
isSameElement:function (nodeA, nodeB) {
if (nodeA.tagName != nodeB.tagName) {
return false;
}
var thisAttrs = nodeA.attributes,
otherAttrs = nodeB.attributes;
if (!ie && thisAttrs.length != otherAttrs.length) {
return false;
}
var attrA, attrB, al = 0, bl = 0;
for (var i = 0; attrA = thisAttrs[i++];) {
if (attrA.nodeName == 'style') {
if (attrA.specified) {
al++;
}
if (domUtils.isSameStyle(nodeA, nodeB)) {
continue;
} else {
return false;
}
}
if (ie) {
if (attrA.specified) {
al++;
attrB = otherAttrs.getNamedItem(attrA.nodeName);
} else {
continue;
}
} else {
attrB = nodeB.attributes[attrA.nodeName];
}
if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
return false;
}
}
// 有可能attrB的属性包含了attrA的属性之外还有自己的属性
if (ie) {
for (i = 0; attrB = otherAttrs[i++];) {
if (attrB.specified) {
bl++;
}
}
if (al != bl) {
return false;
}
}
return true;
},
/**
* 判断节点nodeA与节点nodeB的元素属性是否一致
* @name isSameStyle
* @grammar UE.dom.domUtils.isSameStyle(nodeA,nodeB) => true|false
*/
isSameStyle:function (nodeA, nodeB) {
var styleA = nodeA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'),
styleB = nodeB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':');
if (browser.opera) {
styleA = nodeA.style;
styleB = nodeB.style;
if (styleA.length != styleB.length)
return false;
for (var p in styleA) {
if (/^(\d+|csstext)$/i.test(p)) {
continue;
}
if (styleA[p] != styleB[p]) {
return false;
}
}
return true;
}
if (!styleA || !styleB) {
return styleA == styleB;
}
styleA = styleA.split(';');
styleB = styleB.split(';');
if (styleA.length != styleB.length) {
return false;
}
for (var i = 0, ci; ci = styleA[i++];) {
if (utils.indexOf(styleB, ci) == -1) {
return false;
}
}
return true;
},
/**
* 检查节点node是否为块元素
* @name isBlockElm
* @grammar UE.dom.domUtils.isBlockElm(node) => true|false
*/
isBlockElm:function (node) {
return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
},
/**
* 检测node节点是否为body节点
* @name isBody
* @grammar UE.dom.domUtils.isBody(node) => true|false
*/
isBody:function (node) {
return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
},
/**
* 以node节点为中心,将该节点的指定祖先节点parent拆分成2块
* @name breakParent
* @grammar UE.dom.domUtils.breakParent(node,parent) => node
* @desc
* ooo是node节点
* xxxxoooxxx
==> xxx
oooxxx
* xxxxxxxxxoooxxxxxx
=> xxxxxxxxx
oooxxxxxx
*/
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;
},
/**
* 检查节点node是否是空inline节点
* @name isEmptyInlineElement
* @grammar UE.dom.domUtils.isEmptyInlineElement(node) => 1|0
* @example
* => 1
* => 1
* => 1
* xx => 0
*/
isEmptyInlineElement:function (node) {
if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
return 0;
}
node = node.firstChild;
while (node) {
//如果是创建的bookmark就跳过
if (domUtils.isBookmarkNode(node)) {
return 0;
}
if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
node.nodeType == 3 && !domUtils.isWhitespace(node)
) {
return 0;
}
node = node.nextSibling;
}
return 1;
},
/**
* 删除node节点下的左右空白文本子节点
* @name trimWhiteTextNode
* @grammar UE.dom.domUtils.trimWhiteTextNode(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');
},
/**
* 合并node节点下相同的子节点
* @name mergeChild
* @desc
* UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
* @example
* xxaaxx
* ==> UE.dom.domUtils.mergeChild(node,'span') *xxaaxx
*/ mergeChild:function (node, tagName, attrs) { var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase()); for (var i = 0, ci; ci = list[i++];) { if (!ci.parentNode || domUtils.isBookmarkNode(ci)) { continue; } //span单独处理 if (ci.tagName.toLowerCase() == 'span') { if (node === ci.parentNode) { domUtils.trimWhiteTextNode(node); if (node.childNodes.length == 1) { node.style.cssText = ci.style.cssText + ";" + node.style.cssText; domUtils.remove(ci, true); continue; } } ci.style.cssText = node.style.cssText + ';' + ci.style.cssText; if (attrs) { var style = attrs.style; if (style) { style = style.split(';'); for (var j = 0, s; s = style[j++];) { ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] = s.split(':')[1]; } } } if (domUtils.isSameStyle(ci, node)) { domUtils.remove(ci, true); } continue; } if (domUtils.isSameElement(node, ci)) { domUtils.remove(ci, true); } } }, /** * 原生方法getElementsByTagName的封装 * @name getElementsByTagName * @grammar UE.dom.domUtils.getElementsByTagName(node,tagName) => Array //节点集合数组 */ getElementsByTagName:function (node, name,filter) { if(filter && utils.isString(filter)){ var className = filter; filter = function(node){return domUtils.hasClass(node,className)} } name = utils.trim(name).replace(/[ ]{2,}/g,' ').split(' '); var arr = []; for(var n = 0,ni;ni=name[n++];){ var list = node.getElementsByTagName(ni); for (var i = 0, ci; ci = list[i++];) { if(!filter || filter(ci)) arr.push(ci); } } return arr; }, /** * 将节点node合并到父节点上 * @name mergeToParent * @grammar UE.dom.domUtils.mergeToParent(node) * @example * xxx ==> xxx */ mergeToParent:function (node) { var parent = node.parentNode; while (parent && dtd.$removeEmpty[parent.tagName]) { if (parent.tagName == node.tagName || parent.tagName == 'A') {//针对a标签单独处理 domUtils.trimWhiteTextNode(parent); //span需要特殊处理 不处理这样的情况 xxxxxxxxx if (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node) || (parent.tagName == 'A' && node.tagName == 'SPAN')) { if (parent.childNodes.length > 1 || parent !== node.parentNode) { node.style.cssText = parent.style.cssText + ";" + node.style.cssText; parent = parent.parentNode; continue; } else { parent.style.cssText += ";" + node.style.cssText; //trace:952 a标签要保持下划线 if (parent.tagName == 'A') { parent.style.textDecoration = 'underline'; } } } if (parent.tagName != 'A') { parent === node.parentNode && domUtils.remove(node, true); break; } } parent = parent.parentNode; } }, /** * 合并节点node的左右兄弟节点 * @name mergeSibling * @grammar UE.dom.domUtils.mergeSibling(node) * @grammar UE.dom.domUtils.mergeSibling(node,ignorePre) //ignorePre指定是否忽略左兄弟 * @grammar UE.dom.domUtils.mergeSibling(node,ignorePre,ignoreNext) //ignoreNext指定是否忽略右兄弟 * @example * xxxxoooxxxx ==> xxxxoooxxxx */ mergeSibling:function (node, ignorePre, ignoreNext) { function merge(rtl, start, node) { var next; if ((next = node[rtl]) && !domUtils.isBookmarkNode(next) && next.nodeType == 1 && domUtils.isSameElement(node, next)) { while (next.firstChild) { if (start == 'firstChild') { node.insertBefore(next.lastChild, node.firstChild); } else { node.appendChild(next.firstChild); } } domUtils.remove(next); } } !ignorePre && merge('previousSibling', 'firstChild', node); !ignoreNext && merge('nextSibling', 'lastChild', node); }, /** * 设置节点node及其子节点不会被选中 * @name unSelectable * @grammar UE.dom.domUtils.unSelectable(node) */ unSelectable:ie || browser.opera ? function (node) { //for ie9 node.onselectstart = function () { return false; }; node.onclick = node.onkeyup = node.onkeydown = function () { return false; }; node.unselectable = 'on'; node.setAttribute("unselectable", "on"); for (var i = 0, ci; ci = node.all[i++];) { switch (ci.tagName.toLowerCase()) { case 'iframe' : case 'textarea' : case 'input' : case 'select' : break; default : ci.unselectable = 'on'; node.setAttribute("unselectable", "on"); } } } : function (node) { node.style.MozUserSelect = node.style.webkitUserSelect = node.style.KhtmlUserSelect = 'none'; }, /** * 删除节点node上的属性attrNames,attrNames为属性名称数组 * @name removeAttributes * @grammar UE.dom.domUtils.removeAttributes(node,attrNames) * @example * //Before remove * xxxxx * //Remove * UE.dom.domUtils.removeAttributes(node,["id","name"]); * //After remove * xxxxx */ removeAttributes:function (node, attrNames) { attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g,' ').split(' '); for (var i = 0, ci; ci = attrNames[i++];) { ci = attrFix[ci] || ci; switch (ci) { case 'className': node[ci] = ''; break; case 'style': node.style.cssText = ''; !browser.ie && node.removeAttributeNode(node.getAttributeNode('style')) } node.removeAttribute(ci); } }, /** * 在doc下创建一个标签名为tag,属性为attrs的元素 * @name createElement * @grammar UE.dom.domUtils.createElement(doc,tag,attrs) => Node //返回创建的节点 */ createElement:function (doc, tag, attrs) { return domUtils.setAttributes(doc.createElement(tag), attrs) }, /** * 为节点node添加属性attrs,attrs为属性键值对 * @name setAttributes * @grammar UE.dom.domUtils.setAttributes(node,attrs) => node */ setAttributes:function (node, attrs) { for (var attr in attrs) { if(attrs.hasOwnProperty(attr)){ var value = attrs[attr]; switch (attr) { case 'class': //ie下要这样赋值,setAttribute不起作用 node.className = value; break; case 'style' : node.style.cssText = node.style.cssText + ";" + value; break; case 'innerHTML': node[attr] = value; break; case 'value': node.value = value; break; default: node.setAttribute(attrFix[attr] || attr, value); } } } return node; }, /** * 获取元素element的计算样式 * @name getComputedStyle * @grammar UE.dom.domUtils.getComputedStyle(element,styleName) => String //返回对应样式名称的样式值 * @example * getComputedStyle(document.body,"font-size") => "15px" * getComputedStyle(form,"color") => "#ffccdd" */ getComputedStyle:function (element, styleName) { //一下的属性单独处理 var pros = 'width height top left'; if(pros.indexOf(styleName) > -1){ return element['offset' + styleName.replace(/^\w/,function(s){return s.toUpperCase()})] + 'px'; } //忽略文本节点 if (element.nodeType == 3) { element = element.parentNode; } //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改. if (browser.ie && browser.version < 9 && styleName == 'font-size' && !element.style.fontSize && !dtd.$empty[element.tagName] && !dtd.$nonChild[element.tagName]) { var span = element.ownerDocument.createElement('span'); span.style.cssText = 'padding:0;border:0;font-family:simsun;'; span.innerHTML = '.'; element.appendChild(span); var result = span.offsetHeight; element.removeChild(span); span = null; return result + 'px'; } try { var value = domUtils.getStyle(element, styleName) || (window.getComputedStyle ? domUtils.getWindow(element).getComputedStyle(element, '').getPropertyValue(styleName) : ( element.currentStyle || element.style )[utils.cssStyleToDomStyle(styleName)]); } catch (e) { return ""; } return utils.transUnitToPx(utils.fixColor(styleName, value)); }, /** * 在元素element上删除classNames,支持同时删除多个 * @name removeClasses * @grammar UE.dom.domUtils.removeClasses(element,classNames) * @example * //执行方法前的dom结构 * xxx * //执行方法 * UE.dom.domUtils.removeClasses(element,["test1","test3"]) * //执行方法后的dom结构 * xxx */ removeClasses:function (elm, classNames) { classNames = utils.isArray(classNames) ? classNames : utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' '); for(var i = 0,ci,cls = elm.className;ci=classNames[i++];){ cls = cls.replace(new RegExp('\\b' + ci + '\\b'),'') } cls = utils.trim(cls).replace(/[ ]{2,}/g,' '); if(cls){ elm.className = cls; }else{ domUtils.removeAttributes(elm,['class']); } }, /** * 在元素element上增加一个样式类className,支持以空格分开的多个类名 * 如果相同的类名将不会添加 * @name addClass * @grammar UE.dom.domUtils.addClass(element,classNames) */ addClass:function (elm, classNames) { if(!elm)return; classNames = utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' '); for(var i = 0,ci,cls = elm.className;ci=classNames[i++];){ if(!new RegExp('\\b' + ci + '\\b').test(cls)){ elm.className += ' ' + ci; } } }, /** * 判断元素element是否包含样式类名className,支持以空格分开的多个类名,多个类名顺序不同也可以比较 * @name hasClass * @grammar UE.dom.domUtils.hasClass(element,className) =>true|false */ hasClass:function (element, className) { if(utils.isRegExp(className)){ return className.test(element.className) } className = utils.trim(className).replace(/[ ]{2,}/g,' ').split(' '); for(var i = 0,ci,cls = element.className;ci=className[i++];){ if(!new RegExp('\\b' + ci + '\\b','i').test(cls)){ return false; } } return i - 1 == className.length; }, /** * 阻止事件默认行为 * @param {Event} evt 需要组织的事件对象 */ preventDefault:function (evt) { evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false); }, /** * 删除元素element的样式 * @grammar UE.dom.domUtils.removeStyle(element,name) 删除的样式名称 */ removeStyle:function (element, name) { if(browser.ie && browser.version > 8){ element.style.cssText = element.style.cssText.replace(new RegExp(name + '\s*:\s*[^;]+;?'),'') }else{ if (element.style.removeProperty) { element.style.removeProperty (name); }else { element.style.removeAttribute (utils.cssStyleToDomStyle(name)); } } if (!element.style.cssText) { domUtils.removeAttributes(element, ['style']); } }, /** * 获取元素element的某个样式值 * @name getStyle * @grammar UE.dom.domUtils.getStyle(element,name) => String */ getStyle:function (element, name) { var value = element.style[ utils.cssStyleToDomStyle(name) ]; return utils.fixColor(name, value); }, /** * 为元素element设置样式属性值 * @name setStyle * @grammar UE.dom.domUtils.setStyle(element,name,value) */ setStyle:function (element, name, value) { element.style[utils.cssStyleToDomStyle(name)] = value; }, /** * 为元素element设置样式属性值 * @name setStyles * @grammar UE.dom.domUtils.setStyle(element,styles) //styles为样式键值对 */ setStyles:function (element, styles) { for (var name in styles) { if (styles.hasOwnProperty(name)) { domUtils.setStyle(element, name, styles[name]); } } }, /** * 删除_moz_dirty属性 * @function */ removeDirtyAttr:function (node) { for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) { ci.removeAttribute('_moz_dirty'); } node.removeAttribute('_moz_dirty'); }, /** * 返回子节点的数量 * @function * @param {Node} node 父节点 * @param {Function} fn 过滤子节点的规则,若为空,则得到所有子节点的数量 * @return {Number} 符合条件子节点的数量 */ getChildCount:function (node, fn) { var count = 0, first = node.firstChild; fn = fn || function () { return 1; }; while (first) { if (fn(first)) { count++; } first = first.nextSibling; } return count; }, /** * 判断是否为空节点 * @function * @param {Node} node 节点 * @return {Boolean} 是否为空节点 */ isEmptyNode:function (node) { return !node.firstChild || domUtils.getChildCount(node, function (node) { return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node) }) == 0 }, /** * 清空节点所有的className * @function * @param {Array} nodes 节点数组 */ clearSelectedArr:function (nodes) { var node; while (node = nodes.pop()) { domUtils.removeAttributes(node, ['class']); } }, /** * 将显示区域滚动到显示节点的位置 * @function * @param {Node} node 节点 * @param {window} win window对象 * @param {Number} offsetTop 距离上方的偏移量 */ scrollToView:function (node, win, offsetTop) { var getViewPaneSize = function () { var doc = win.document, mode = doc.compatMode == 'CSS1Compat'; return { width:( mode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0, height:( mode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0 }; }, getScrollPosition = function (win) { if ('pageXOffset' in win) { return { x:win.pageXOffset || 0, y:win.pageYOffset || 0 }; } else { var doc = win.document; return { x:doc.documentElement.scrollLeft || doc.body.scrollLeft || 0, y:doc.documentElement.scrollTop || doc.body.scrollTop || 0 }; } }; var winHeight = getViewPaneSize().height, offset = winHeight * -1 + offsetTop; offset += (node.offsetHeight || 0); var elementPosition = domUtils.getXY(node); offset += elementPosition.y; var currentScroll = getScrollPosition(win).y; // offset += 50; if (offset > currentScroll || offset < currentScroll - winHeight) { win.scrollTo(0, offset + (offset < 0 ? -20 : 20)); } }, /** * 判断节点是否为br * @function * @param {Node} node 节点 */ isBr:function (node) { return node.nodeType == 1 && node.tagName == 'BR'; }, isFillChar:function (node,isInStart) { return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length }, isStartInblock:function (range) { var tmpRange = range.cloneRange(), flag = 0, start = tmpRange.startContainer, tmp; if(start.nodeType == 1 && start.childNodes[tmpRange.startOffset]){ start = start.childNodes[tmpRange.startOffset]; var pre = start.previousSibling; while(pre && domUtils.isFillChar(pre)){ start = pre; pre = pre.previousSibling; } } if(this.isFillChar(start,true) && tmpRange.startOffset == 1){ tmpRange.setStartBefore(start); start = tmpRange.startContainer; } while (start && domUtils.isFillChar(start)) { tmp = start; start = start.previousSibling } if (tmp) { tmpRange.setStartBefore(tmp); start = tmpRange.startContainer; } if (start.nodeType == 1 && domUtils.isEmptyNode(start) && tmpRange.startOffset == 1) { tmpRange.setStart(start, 0).collapse(true); } while (!tmpRange.startOffset) { start = tmpRange.startContainer; if (domUtils.isBlockElm(start) || domUtils.isBody(start)) { flag = 1; break; } var pre = tmpRange.startContainer.previousSibling, tmpNode; if (!pre) { tmpRange.setStartBefore(tmpRange.startContainer); } else { while (pre && domUtils.isFillChar(pre)) { tmpNode = pre; pre = pre.previousSibling; } if (tmpNode) { tmpRange.setStartBefore(tmpNode); } else { tmpRange.setStartBefore(tmpRange.startContainer); } } } return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0; }, isEmptyBlock:function (node) { var reg = new RegExp('[ \t\r\n' + domUtils.fillChar + ']', 'g'); if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) { return 0; } for (var n in dtd.$isNotEmpty) { if (node.getElementsByTagName(n).length) { return 0; } } return 1; }, setViewportOffset:function (element, offset) { var left = parseInt(element.style.left) | 0; var top = parseInt(element.style.top) | 0; var rect = element.getBoundingClientRect(); var offsetLeft = offset.left - rect.left; var offsetTop = offset.top - rect.top; if (offsetLeft) { element.style.left = left + offsetLeft + 'px'; } if (offsetTop) { element.style.top = top + offsetTop + 'px'; } }, fillNode:function (doc, node) { var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br'); node.innerHTML = ''; node.appendChild(tmpNode); }, moveChild:function (src, tag, dir) { while (src.firstChild) { if (dir && tag.firstChild) { tag.insertBefore(src.lastChild, tag.firstChild); } else { tag.appendChild(src.firstChild); } } }, //判断是否有额外属性 hasNoAttributes:function (node) { return browser.ie ? /^<\w+\s*?>/.test(node.outerHTML) : node.attributes.length == 0; }, //判断是否是编辑器自定义的参数 isCustomeNode:function (node) { return node.nodeType == 1 && node.getAttribute('_ue_custom_node_'); }, isTagNode:function (node, tagName) { return node.nodeType == 1 && new RegExp(node.tagName,'i').test(tagName) }, /** * 对于nodelist用filter进行过滤 * @name filterNodeList * @since 1.2.4+ * @grammar UE.dom.domUtils.filterNodeList(nodelist,filter,onlyFirst) => 节点 * @example * UE.dom.domUtils.filterNodeList(document.getElementsByTagName('*'),'div p') //返回第一个是div或者p的节点 * UE.dom.domUtils.filterNodeList(document.getElementsByTagName('*'),function(n){return n.getAttribute('src')}) * //返回第一个带src属性的节点 * UE.dom.domUtils.filterNodeList(document.getElementsByTagName('*'),'i',true) //返回数组,里边都是i节点 */ filterNodeList : function(nodelist,filter,forAll){ var results = []; if(!utils .isFunction(filter)){ var str = filter; filter = function(n){ return utils.indexOf(utils.isArray(str) ? str:str.split(' '), n.tagName.toLowerCase()) != -1 }; } utils.each(nodelist,function(n){ filter(n) && results.push(n) }); return results.length == 0 ? null : results.length == 1 || !forAll ? results[0] : results }, isInNodeEndBoundary : function (rng,node){ var start = rng.startContainer; if(start.nodeType == 3 && rng.startOffset != start.nodeValue.length){ return 0; } if(start.nodeType == 1 && rng.startOffset != start.childNodes.length){ return 0; } while(start !== node){ if(start.nextSibling){ return 0 }; start = start.parentNode; } return 1; }, isBoundaryNode : function (node,dir){ var tmp; while(!domUtils.isBody(node)){ tmp = node; node = node.parentNode; if(tmp !== node[dir]){ return false; } } return true; } }; var fillCharReg = new RegExp(domUtils.fillChar, 'g');///import editor.js ///import core/utils.js ///import core/browser.js ///import core/dom/dom.js ///import core/dom/dtd.js ///import core/dom/domUtils.js /** * @file * @name UE.dom.Range * @anthor zhanyi * @short Range * @import editor.js,core/utils.js,core/browser.js,core/dom/domUtils.js,core/dom/dtd.js * @desc Range范围实现类,本类是UEditor底层核心类,统一w3cRange和ieRange之间的差异,包括接口和属性 */ (function () { var guid = 0, fillChar = domUtils.fillChar, fillData; /** * 更新range的collapse状态 * @param {Range} range range对象 */ function updateCollapse(range) { range.collapsed = range.startContainer && range.endContainer && range.startContainer === range.endContainer && range.startOffset == range.endOffset; } function selectOneNode(rng){ return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1 } function setEndPoint(toStart, node, offset, range) { //如果node是自闭合标签要处理 if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) { offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1); node = node.parentNode; } if (toStart) { range.startContainer = node; range.startOffset = offset; if (!range.endContainer) { range.collapse(true); } } else { range.endContainer = node; range.endOffset = offset; if (!range.startContainer) { range.collapse(false); } } updateCollapse(range); return range; } function execContentsAction(range, action) { //调整边界 //range.includeBookmark(); var start = range.startContainer, end = range.endContainer, startOffset = range.startOffset, endOffset = range.endOffset, doc = range.document, frag = doc.createDocumentFragment(), tmpStart, tmpEnd; if (start.nodeType == 1) { start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode(''))); } if (end.nodeType == 1) { end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode(''))); } if (start === end && start.nodeType == 3) { frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset))); //is not clone if (action) { start.deleteData(startOffset, endOffset - startOffset); range.collapse(true); } return frag; } var current, currentLevel, clone = frag, startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true); for (var i = 0; startParents[i] == endParents[i];) { i++; } for (var j = i, si; si = startParents[j]; j++) { current = si.nextSibling; if (si == start) { if (!tmpStart) { if (range.startContainer.nodeType == 3) { clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset))); //is not clone if (action) { start.deleteData(startOffset, start.nodeValue.length - startOffset); } } else { clone.appendChild(!action ? start.cloneNode(true) : start); } } } else { currentLevel = si.cloneNode(false); clone.appendChild(currentLevel); } while (current) { if (current === end || current === endParents[j]) { break; } si = current.nextSibling; clone.appendChild(!action ? current.cloneNode(true) : current); current = si; } clone = currentLevel; } clone = frag; if (!startParents[i]) { clone.appendChild(startParents[i - 1].cloneNode(false)); clone = clone.firstChild; } for (var j = i, ei; ei = endParents[j]; j++) { current = ei.previousSibling; if (ei == end) { if (!tmpEnd && range.endContainer.nodeType == 3) { clone.appendChild(doc.createTextNode(end.substringData(0, endOffset))); //is not clone if (action) { end.deleteData(0, endOffset); } } } else { currentLevel = ei.cloneNode(false); clone.appendChild(currentLevel); } //如果两端同级,右边第一次已经被开始做了 if (j != i || !startParents[i]) { while (current) { if (current === start) { break; } ei = current.previousSibling; clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild); current = ei; } } clone = currentLevel; } if (action) { range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true); } tmpStart && domUtils.remove(tmpStart); tmpEnd && domUtils.remove(tmpEnd); return frag; } /** * @name Range * @grammar new UE.dom.Range(document) => Range 实例 * @desc 创建一个跟document绑定的空的Range实例 * - ***startContainer*** 开始边界的容器节点,可以是elementNode或者是textNode * - ***startOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符 * - ***endContainer*** 结束边界的容器节点,可以是elementNode或者是textNode * - ***endOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符 * - ***document*** 跟range关联的document对象 * - ***collapsed*** 是否是闭合状态 */ var Range = dom.Range = function (document) { var me = this; me.startContainer = me.startOffset = me.endContainer = me.endOffset = null; me.document = document; me.collapsed = true; }; /** * 删除fillData * @param doc * @param excludeNode */ function removeFillData(doc, excludeNode) { try { if (fillData && domUtils.inDoc(fillData, doc)) { if (!fillData.nodeValue.replace(fillCharReg, '').length) { var tmpNode = fillData.parentNode; domUtils.remove(fillData); while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) && //safari的contains有bug (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode)) ) { fillData = tmpNode.parentNode; domUtils.remove(tmpNode); tmpNode = fillData; } } else { fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, ''); } } } catch (e) { } } /** * * @param node * @param dir */ function mergeSibling(node, dir) { var tmpNode; node = node[dir]; while (node && domUtils.isFillChar(node)) { tmpNode = node[dir]; domUtils.remove(node); node = tmpNode; } } Range.prototype = { /** * @name cloneContents * @grammar range.cloneContents() => DocumentFragment * @desc 克隆选中的内容到一个fragment里,如果选区是空的将返回null */ cloneContents:function () { return this.collapsed ? null : execContentsAction(this, 0); }, /** * @name deleteContents * @grammar range.deleteContents() => Range * @desc 删除当前选区范围中的所有内容并返回range实例,这时的range已经变成了闭合状态 * @example * DOM Element : * xx[xxx]x * //执行方法后 * xx|x * 注意range改变了 * range.startContainer => b * range.startOffset => 2 * range.endContainer => b * range.endOffset => 2 * range.collapsed => true */ deleteContents:function () { var txt; if (!this.collapsed) { execContentsAction(this, 1); } if (browser.webkit) { txt = this.startContainer; if (txt.nodeType == 3 && !txt.nodeValue.length) { this.setStartBefore(txt).collapse(true); domUtils.remove(txt); } } return this; }, /** * @name extractContents * @grammar range.extractContents() => DocumentFragment * @desc 将当前的内容放到一个fragment里并返回这个fragment,这时的range已经变成了闭合状态 * @example * DOM Element : * xx[xxx]x * //执行方法后 * 返回的fragment里的 dom结构是 * xxx * dom树上的结构是 * xx|x * 注意range改变了 * range.startContainer => b * range.startOffset => 2 * range.endContainer => b * range.endOffset => 2 * range.collapsed => true */ extractContents:function () { return this.collapsed ? null : execContentsAction(this, 2); }, /** * @name setStart * @grammar range.setStart(node,offset) => Range * @desc 设置range的开始位置位于node节点内,偏移量为offset * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符 */ setStart:function (node, offset) { return setEndPoint(true, node, offset, this); }, /** * 设置range的结束位置位于node节点,偏移量为offset * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符 * @name setEnd * @grammar range.setEnd(node,offset) => Range */ setEnd:function (node, offset) { return setEndPoint(false, node, offset, this); }, /** * 将Range开始位置设置到node节点之后 * @name setStartAfter * @grammar range.setStartAfter(node) => Range * @example * xxx|xx * 执行setStartAfter(i)后 * range.startContainer =>b * range.startOffset =>2 */ setStartAfter:function (node) { return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1); }, /** * 将Range开始位置设置到node节点之前 * @name setStartBefore * @grammar range.setStartBefore(node) => Range * @example * xxx|xx * 执行setStartBefore(i)后 * range.startContainer =>b * range.startOffset =>1 */ setStartBefore:function (node) { return this.setStart(node.parentNode, domUtils.getNodeIndex(node)); }, /** * 将Range结束位置设置到node节点之后 * @name setEndAfter * @grammar range.setEndAfter(node) => Range * @example * xxx|xx * setEndAfter(i)后 * range.endContainer =>b * range.endtOffset =>2 */ setEndAfter:function (node) { return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1); }, /** * 将Range结束位置设置到node节点之前 * @name setEndBefore * @grammar range.setEndBefore(node) => Range * @example * xxx|xx * 执行setEndBefore(i)后 * range.endContainer =>b * range.endtOffset =>1 */ setEndBefore:function (node) { return this.setEnd(node.parentNode, domUtils.getNodeIndex(node)); }, /** * 将Range开始位置设置到node节点内的开始位置 * @name setStartAtFirst * @grammar range.setStartAtFirst(node) => Range */ setStartAtFirst:function (node) { return this.setStart(node, 0); }, /** * 将Range开始位置设置到node节点内的结束位置 * @name setStartAtLast * @grammar range.setStartAtLast(node) => Range */ setStartAtLast:function (node) { return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length); }, /** * 将Range结束位置设置到node节点内的开始位置 * @name setEndAtFirst * @grammar range.setEndAtFirst(node) => Range */ setEndAtFirst:function (node) { return this.setEnd(node, 0); }, /** * 将Range结束位置设置到node节点内的结束位置 * @name setEndAtLast * @grammar range.setEndAtLast(node) => Range */ setEndAtLast:function (node) { return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length); }, /** * 选中完整的指定节点,并返回包含该节点的range * @name selectNode * @grammar range.selectNode(node) => Range */ selectNode:function (node) { return this.setStartBefore(node).setEndAfter(node); }, /** * 选中node内部的所有节点,并返回对应的range * @name selectNodeContents * @grammar range.selectNodeContents(node) => Range * @example * xx[xxxx]xxx * 执行后 * [xxxxxxxxx] * range.startContainer =>b * range.startOffset =>0 * range.endContainer =>b * range.endOffset =>3 */ selectNodeContents:function (node) { return this.setStart(node, 0).setEndAtLast(node); }, /** * 克隆一个新的range对象 * @name cloneRange * @grammar range.cloneRange() => Range */ cloneRange:function () { var me = this; return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset); }, /** * 让选区闭合到尾部,若toStart为真,则闭合到头部 * @name collapse * @grammar range.collapse() => Range * @grammar range.collapse(true) => Range //闭合选区到头部 */ collapse:function (toStart) { var me = this; if (toStart) { me.endContainer = me.startContainer; me.endOffset = me.startOffset; } else { me.startContainer = me.endContainer; me.startOffset = me.endOffset; } me.collapsed = true; return me; }, /** * 调整range的边界,使其"收缩"到最小的位置 * @name shrinkBoundary * @grammar range.shrinkBoundary() => Range //range开始位置和结束位置都调整,参见adjustmentBoundary
* @grammar range.shrinkBoundary(true) => Range //仅调整开始位置,忽略结束位置
* @example
* xx[xxxxx] ==> xx[xxxxx]
* x[xx]xxx ==> x[xx]xxx
* [xxxxxxxxxxx] ==> [xxxxxxxxxxx]
*/
shrinkBoundary:function (ignoreEnd) {
var me = this, child,
collapsed = me.collapsed;
function check(node){
return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
}
while (me.startContainer.nodeType == 1 //是element
&& (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
&& check(child)) {
me.setStart(child, 0);
}
if (collapsed) {
return me.collapse(true);
}
if (!ignoreEnd) {
while (me.endContainer.nodeType == 1//是element
&& me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
&& (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
&& check(child)) {
me.setEnd(child, child.childNodes.length);
}
}
return me;
},
/**
* 获取当前range所在位置的公共祖先节点,当前range位置可以位于文本节点内,也可以包含整个元素节点,也可以位于两个节点之间
* @name getCommonAncestor
* @grammar range.getCommonAncestor([includeSelf, ignoreTextNode]) => Element
* @example
* xx[xxxx]xxxx ==>getCommonAncestor() ==> b
* []
* range.startContainer ==> b
* range.startOffset ==> 0
* range.endContainer ==> b
* range.endOffset ==> 1
* range.getCommonAncestor() ==> b
* range.getCommonAncestor(true) ==> img
* xxx|xx
* range.startContainer ==> textNode
* range.startOffset ==> 3
* range.endContainer ==> textNode
* range.endOffset ==> 3
* range.getCommonAncestor() ==> textNode
* range.getCommonAncestor(null,true) ==> b
*/
getCommonAncestor:function (includeSelf, ignoreTextNode) {
var me = this,
start = me.startContainer,
end = me.endContainer;
if (start === end) {
if (includeSelf && selectOneNode(this)) {
start = start.childNodes[me.startOffset];
if(start.nodeType == 1)
return start;
}
//只有在上来就相等的情况下才会出现是文本的情况
return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start;
}
return domUtils.getCommonAncestor(start, end);
},
/**
* 调整边界容器,如果是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 () {
function adjust(r, c) {
var container = r[c + 'Container'],
offset = r[c + 'Offset'];
if (container.nodeType == 3) {
if (!offset) {
r['set' + c.replace(/(\w)/, function (a) {
return a.toUpperCase();
}) + 'Before'](container);
} else if (offset >= container.nodeValue.length) {
r['set' + c.replace(/(\w)/, function (a) {
return a.toUpperCase();
}) + 'After' ](container);
}
}
}
if (!this.collapsed) {
adjust(this, 'start');
adjust(this, 'end');
}
return this;
},
/**
* 在当前选区的开始位置前插入一个节点或者fragment,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的边界,使其"放大"到最近的父block节点 * @name enlarge * @grammar range.enlarge() => Range * @example *xxxx[xxxxxx]
xxx
==> [xxxxxxxxxx
]xxx
*/ enlarge:function (toBlock, stopFn) { var isBody = domUtils.isBody, pre, node, tmp = this.document.createTextNode(''); if (toBlock) { node = this.startContainer; if (node.nodeType == 1) { if (node.childNodes[this.startOffset]) { pre = node = node.childNodes[this.startOffset] } else { node.appendChild(tmp); pre = node = tmp; } } else { pre = node; } while (1) { if (domUtils.isBlockElm(node)) { node = pre; while ((pre = node.previousSibling) && !domUtils.isBlockElm(pre)) { node = pre; } this.setStartBefore(node); break; } pre = node; node = node.parentNode; } node = this.endContainer; if (node.nodeType == 1) { if (pre = node.childNodes[this.endOffset]) { node.insertBefore(tmp, pre); } else { node.appendChild(tmp); } pre = node = tmp; } else { pre = node; } while (1) { if (domUtils.isBlockElm(node)) { node = pre; while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) { node = pre; } this.setEndAfter(node); break; } pre = node; node = node.parentNode; } if (tmp.parentNode === this.endContainer) { this.endOffset--; } domUtils.remove(tmp); } // 扩展边界到最大 if (!this.collapsed) { while (this.startOffset == 0) { if (stopFn && stopFn(this.startContainer)) { break; } if (isBody(this.startContainer)) { break; } this.setStartBefore(this.startContainer); } while (this.endOffset == (this.endContainer.nodeType == 1 ? this.endContainer.childNodes.length : this.endContainer.nodeValue.length)) { if (stopFn && stopFn(this.endContainer)) { break; } if (isBody(this.endContainer)) { break; } this.setEndAfter(this.endContainer); } } return this; }, /** * 调整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.setStartAfter(this.startContainer);
}
while (!domUtils.isBody(this.endContainer) && !this.endOffset) {
this.setEndBefore(this.endContainer);
}
}
return this;
},
/**
* 给range选区中的内容添加给定的标签,主要用于inline标签
* @name applyInlineStyle
* @grammar range.applyInlineStyle(tagName) => Range //tagName为需要添加的样式标签名
* @grammar range.applyInlineStyle(tagName,attrs) => Range //attrs为属性json对象
* @desc
* xxxx[xxxx]x
==> range.applyInlineStyle("strong") ==> xxxx[xxxx]x
* xx[ddyyyy]x
==> range.applyInlineStyle("strong") ==> xx[ddyyyy]x
* xxxx[xxxx]x
==> range.applyInlineStyle("strong",{"style":"font-size:12px"}) ==> xxxx[xxxx]x
*/
applyInlineStyle:function (tagName, attrs, list) {
if (this.collapsed)return this;
this.trimBoundary().enlarge(false,
function (node) {
return node.nodeType == 1 && domUtils.isBlockElm(node)
}).adjustmentBoundary();
var bookmark = this.createBookmark(),
end = bookmark.end,
filterFn = function (node) {
return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node);
},
current = domUtils.getNextDomNode(bookmark.start, false, filterFn),
node,
pre,
range = this.cloneRange();
while (current && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
if (current.nodeType == 3 || dtd[tagName][current.tagName]) {
range.setStartBefore(current);
node = current;
while (node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end) {
pre = node;
node = domUtils.getNextDomNode(node, node.nodeType == 1, null, function (parent) {
return dtd[tagName][parent.tagName];
});
}
var frag = range.setEndAfter(pre).extractContents(), elm;
if (list && list.length > 0) {
var level, top;
top = level = list[0].cloneNode(false);
for (var i = 1, ci; ci = list[i++];) {
level.appendChild(ci.cloneNode(false));
level = level.firstChild;
}
elm = level;
} else {
elm = range.document.createElement(tagName);
}
if (attrs) {
domUtils.setAttributes(elm, attrs);
}
elm.appendChild(frag);
range.insertNode(list ? top : elm);
//处理下滑线在a上的情况
var aNode;
if (tagName == 'span' && attrs.style && /text\-decoration/.test(attrs.style) && (aNode = domUtils.findParentByTagName(elm, 'a', true))) {
domUtils.setAttributes(aNode, attrs);
domUtils.remove(elm, true);
elm = aNode;
} else {
domUtils.mergeSibling(elm);
domUtils.clearEmptySibling(elm);
}
//去除子节点相同的
domUtils.mergeChild(elm, attrs);
current = domUtils.getNextDomNode(elm, false, filterFn);
domUtils.mergeToParent(elm);
if (node === end) {
break;
}
} else {
current = domUtils.getNextDomNode(current, true, filterFn);
}
}
return this.moveToBookmark(bookmark);
},
/**
* 对当前range选中的节点,去掉给定的标签节点,但标签中的内容保留,主要用于处理inline元素
* @name removeInlineStyle
* @grammar range.removeInlineStyle(tagNames) => Range //tagNames 为需要去掉的样式标签名,支持"b"或者["b","i","u"]
* @desc
* xx[xxxxyyyzz]z => range.removeInlineStyle(["em"]) => xx[xxxxyyyzz]z
*/
removeInlineStyle:function (tagNames) {
if (this.collapsed)return this;
tagNames = utils.isArray(tagNames) ? tagNames : [tagNames];
this.shrinkBoundary().adjustmentBoundary();
var start = this.startContainer, end = this.endContainer;
while (1) {
if (start.nodeType == 1) {
if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) {
break;
}
if (start.tagName.toLowerCase() == 'body') {
start = null;
break;
}
}
start = start.parentNode;
}
while (1) {
if (end.nodeType == 1) {
if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) {
break;
}
if (end.tagName.toLowerCase() == 'body') {
end = null;
break;
}
}
end = end.parentNode;
}
var bookmark = this.createBookmark(),
frag,
tmpRange;
if (start) {
tmpRange = this.cloneRange().setEndBefore(bookmark.start).setStartBefore(start);
frag = tmpRange.extractContents();
tmpRange.insertNode(frag);
domUtils.clearEmptySibling(start, true);
start.parentNode.insertBefore(bookmark.start, start);
}
if (end) {
tmpRange = this.cloneRange().setStartAfter(bookmark.end).setEndAfter(end);
frag = tmpRange.extractContents();
tmpRange.insertNode(frag);
domUtils.clearEmptySibling(end, false, true);
end.parentNode.insertBefore(bookmark.end, end.nextSibling);
}
var current = domUtils.getNextDomNode(bookmark.start, false, function (node) {
return node.nodeType == 1;
}), next;
while (current && current !== bookmark.end) {
next = domUtils.getNextDomNode(current, true, function (node) {
return node.nodeType == 1;
});
if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) {
domUtils.remove(current, true);
}
current = next;
}
return this.moveToBookmark(bookmark);
},
/**
* 得到一个自闭合的节点,常用于获取自闭和的节点,例如图片节点
* @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.document.body.focus() : win.focus();
if (sel) {
sel.removeAllRanges();
// trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
// this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
if (this.collapsed && !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;
},
/**
* 滚动条跳到当然range开始的位置
* @name scrollToView
* @grammar range.scrollToView([win,offset]) => Range //针对window对象,若不指定,将以编辑区域的窗口为准,offset偏移量
*/
scrollToView:function (win, offset) {
win = win ? window : domUtils.getWindow(this.document);
var me = this,
span = me.document.createElement('span');
//trace:717
span.innerHTML = ' ';
me.cloneRange().insertNode(span);
domUtils.scrollToView(span, win, offset);
domUtils.remove(span);
return me;
},
inFillChar : function(){
var start = this.startContainer;
if(this.collapsed && start.nodeType == 3
&& start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length
){
return true;
}
return false;
},
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;
while(tmpNode = tmpNode.previousSibling){
if(tmpNode.nodeType == 3){
firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length;
}else{
break;
}
}
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 ? 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.document.body,
parentNode,offset;
for(var i= 0,ci,l=address.length;i