/source/ueditor/umeditor.js
JavaScript | 9737 lines | 8379 code | 673 blank | 685 comment | 923 complexity | c1e751c883a0788646a35e7bd90903ad MD5 | raw file
Possible License(s): LGPL-3.0
Large files files are truncated, but you can click here to view the full file
- /*!
- * UEditor Mini版本
- * version: 1.0.0
- * build: Tue Sep 17 2013 15:57:30 GMT+0800 (CST)
- */
- (function(){
- UMEDITOR_CONFIG = window.UMEDITOR_CONFIG || {};
- window.UM = {
- plugins : {},
- commands : {},
- I18N : {},
- version : "1.0.0"
- };
- var dom = UM.dom = {};
- /**
- * @file
- * @name UM.browser
- * @short Browser
- * @desc UEditor中采用的浏览器判断模块
- */
- var browser = UM.browser = function(){
- var agent = navigator.userAgent.toLowerCase(),
- opera = window.opera,
- browser = {
- /**
- * 检测浏览器是否为IE
- * @name ie
- * @grammar UM.browser.ie => true|false
- */
- ie : !!window.ActiveXObject,
- /**
- * 检测浏览器是否为Opera
- * @name opera
- * @grammar UM.browser.opera => true|false
- */
- opera : ( !!opera && opera.version ),
- /**
- * 检测浏览器是否为webkit内核
- * @name webkit
- * @grammar UM.browser.webkit => true|false
- */
- webkit : ( agent.indexOf( ' applewebkit/' ) > -1 ),
- /**
- * 检测浏览器是否为mac系统下的浏览器
- * @name mac
- * @grammar UM.browser.mac => true|false
- */
- mac : ( agent.indexOf( 'macintosh' ) > -1 ),
- /**
- * 检测浏览器是否处于怪异模式
- * @name quirks
- * @grammar UM.browser.quirks => true|false
- */
- quirks : ( document.compatMode == 'BackCompat' )
- };
- /**
- * 检测浏览器是否处为gecko内核
- * @name gecko
- * @grammar UM.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 UM.browser.ie9Compat => true|false
- */
- browser.ie9Compat = document.documentMode == 9;
- /**
- * 检测浏览器是否为 IE8 浏览器
- * @name ie8
- * @grammar UM.browser.ie8 => true|false
- */
- browser.ie8 = !!document.documentMode;
- /**
- * 检测浏览器是否为 IE8 模式
- * @name ie8Compat
- * @grammar UM.browser.ie8Compat => true|false
- */
- browser.ie8Compat = document.documentMode == 8;
- /**
- * 检测浏览器是否运行在 兼容IE7模式
- * @name ie7Compat
- * @grammar UM.browser.ie7Compat => true|false
- */
- browser.ie7Compat = ( ( version == 7 && !document.documentMode )
- || document.documentMode == 7 );
- /**
- * 检测浏览器是否IE6模式或怪异模式
- * @name ie6Compat
- * @grammar UM.browser.ie6Compat => true|false
- */
- 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;
- }
- }
- /**
- * 检测浏览器是否为chrome
- * @name chrome
- * @grammar UM.browser.chrome => true|false
- */
- if (/chrome\/(\d+\.\d)/i.test(agent)) {
- browser.chrome = + RegExp['\x241'];
- }
- /**
- * 检测浏览器是否为safari
- * @name safari
- * @grammar UM.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 UM.browser.version => number
- * @example
- * if ( UM.browser.ie && UM.browser.version == 6 ){
- * alert( "Ouch!居然是万恶的IE6!" );
- * }
- */
- browser.version = version;
- /**
- * 是否是兼容模式的浏览器
- * @name isCompatible
- * @grammar UM.browser.isCompatible => true|false
- * @example
- * if ( UM.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 UM.Utils
- * @short Utils
- * @desc UEditor封装使用的静态工具函数
- * @import editor.js
- */
- var utils = UM.utils = {
- /**
- * 遍历数组,对象,nodeList
- * @name each
- * @grammar UM.utils.each(obj,iterator,[context])
- * @since 1.2.4+
- * @desc
- * * obj 要遍历的对象
- * * iterator 遍历的方法,方法的第一个是遍历的值,第二个是索引,第三个是obj
- * * context iterator的上下文
- * @example
- * UM.utils.each([1,2],function(v,i){
- * console.log(v)//值
- * console.log(i)//索引
- * })
- * UM.utils.each(document.getElementsByTagName('*'),function(n){
- * console.log(n.tagName)
- * })
- */
- each : function(obj, iterator, context) {
- if (obj == null) return;
- if (obj.length === +obj.length) {
- for (var i = 0, l = obj.length; i < l; i++) {
- if(iterator.call(context, obj[i], i, obj) === false)
- return false;
- }
- } else {
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- if(iterator.call(context, obj[key], key, obj) === false)
- return false;
- }
- }
- }
- },
- makeInstance:function (obj) {
- var noop = new Function();
- noop.prototype = obj;
- obj = new noop;
- noop.prototype = null;
- return obj;
- },
- /**
- * 将source对象中的属性扩展到target对象上
- * @name extend
- * @grammar UM.utils.extend(target,source) => Object //覆盖扩展
- * @grammar UM.utils.extend(target,source,true) ==> Object //保留扩展
- */
- extend:function (t, s, b) {
- if (s) {
- for (var k in s) {
- if (!b || !t.hasOwnProperty(k)) {
- t[k] = s[k];
- }
- }
- }
- return t;
- },
- extend2:function (t) {
- var a = arguments;
- for (var i = 1; i < a.length; i++) {
- var x = a[i];
- for (var k in x) {
- if (!t.hasOwnProperty(k)) {
- t[k] = x[k];
- }
- }
- }
- return t;
- },
- /**
- * 模拟继承机制,subClass继承superClass
- * @name inherits
- * @grammar UM.utils.inherits(subClass,superClass) => subClass
- * @example
- * function SuperClass(){
- * this.name = "小李";
- * }
- * SuperClass.prototype = {
- * hello:function(str){
- * console.log(this.name + str);
- * }
- * }
- * function SubClass(){
- * this.name = "小张";
- * }
- * UM.utils.inherits(SubClass,SuperClass);
- * var sub = new SubClass();
- * sub.hello("早上好!"); ==> "小张早上好!"
- */
- inherits:function (subClass, superClass) {
- var oldP = subClass.prototype,
- newP = utils.makeInstance(superClass.prototype);
- utils.extend(newP, oldP, true);
- subClass.prototype = newP;
- return (newP.constructor = subClass);
- },
- /**
- * 用指定的context作为fn上下文,也就是this
- * @name bind
- * @grammar UM.utils.bind(fn,context) => fn
- */
- bind:function (fn, context) {
- return function () {
- return fn.apply(context, arguments);
- };
- },
- /**
- * 创建延迟delay执行的函数fn
- * @name defer
- * @grammar UM.utils.defer(fn,delay) =>fn //延迟delay毫秒执行fn,返回fn
- * @grammar UM.utils.defer(fn,delay,exclusion) =>fn //延迟delay毫秒执行fn,若exclusion为真,则互斥执行fn
- * @example
- * function test(){
- * console.log("延迟输出!");
- * }
- * //非互斥延迟执行
- * var testDefer = UM.utils.defer(test,1000);
- * testDefer(); => "延迟输出!";
- * testDefer(); => "延迟输出!";
- * //互斥延迟执行
- * var testDefer1 = UM.utils.defer(test,1000,true);
- * testDefer1(); => //本次不执行
- * testDefer1(); => "延迟输出!";
- */
- defer:function (fn, delay, exclusion) {
- var timerID;
- return function () {
- if (exclusion) {
- clearTimeout(timerID);
- }
- timerID = setTimeout(fn, delay);
- };
- },
- /**
- * 查找元素item在数组array中的索引, 若找不到返回-1
- * @name indexOf
- * @grammar UM.utils.indexOf(array,item) => index|-1 //默认从数组开头部开始搜索
- * @grammar UM.utils.indexOf(array,item,start) => index|-1 //start指定开始查找的位置
- */
- indexOf:function (array, item, start) {
- var index = -1;
- start = this.isNumber(start) ? start : 0;
- this.each(array, function (v, i) {
- if (i >= start && v === item) {
- index = i;
- return false;
- }
- });
- return index;
- },
- /**
- * 移除数组array中的元素item
- * @name removeItem
- * @grammar UM.utils.removeItem(array,item)
- */
- removeItem:function (array, item) {
- for (var i = 0, l = array.length; i < l; i++) {
- if (array[i] === item) {
- array.splice(i, 1);
- i--;
- }
- }
- },
- /**
- * 删除字符串str的首尾空格
- * @name trim
- * @grammar UM.utils.trim(str) => String
- */
- trim:function (str) {
- return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
- },
- /**
- * 将字符串list(以','分隔)或者数组list转成哈希对象
- * @name listToMap
- * @grammar UM.utils.listToMap(list) => Object //Object形如{test:1,br:1,textarea:1}
- */
- listToMap:function (list) {
- if (!list)return {};
- list = utils.isArray(list) ? list : list.split(',');
- for (var i = 0, ci, obj = {}; ci = list[i++];) {
- obj[ci.toUpperCase()] = obj[ci] = 1;
- }
- return obj;
- },
- /**
- * 将str中的html符号转义,默认将转义''&<">''四个字符,可自定义reg来确定需要转义的字符
- * @name unhtml
- * @grammar UM.utils.unhtml(str); => String
- * @grammar UM.utils.unhtml(str,reg) => String
- * @example
- * var html = '<body>You say:"你好!Baidu & UEditor!"</body>';
- * UM.utils.unhtml(html); ==> <body>You say:"你好!Baidu & UEditor!"</body>
- * UM.utils.unhtml(html,/[<>]/g) ==> <body>You say:"你好!Baidu & UEditor!"</body>
- */
- unhtml:function (str, reg) {
- return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) {
- if (b) {
- return a;
- } else {
- return {
- '<':'<',
- '&':'&',
- '"':'"',
- '>':'>',
- "'":'''
- }[a]
- }
- }) : '';
- },
- /**
- * 将str中的转义字符还原成html字符
- * @name html
- * @grammar UM.utils.html(str) => String //详细参见<code><a href = '#unhtml'>unhtml</a></code>
- */
- 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' : '');
- },
- /**
- * DomReady方法,回调函数将在dom树ready完成后执行
- * @name domReady
- * @grammar UM.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 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 ? 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操作库
- */
- 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是否属于bookmark节点
- * @name isBookmarkNode
- * @grammar UM.dom.domUtils.isBookmarkNode(node) => true|false
- */
- isBookmarkNode: function (node) {
- return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
- },
- /**
- * 获取节点node所在的window对象
- * @name getWindow
- * @grammar UM.dom.domUtils.getWindow(node) => window对象
- */
- getWindow: function (node) {
- var doc = node.ownerDocument || node;
- return doc.defaultView || doc.parentWindow;
- },
- /**
- * 将一个文本节点node拆分成两个文本节点,offset指定拆分位置
- * @name split
- * @grammar UM.dom.domUtils.split(node,offset) => TextNode //返回从切分位置开始的后一个文本节点
- */
- split: function (node, offset) {
- var doc = node.ownerDocument;
- if (browser.ie && offset == node.nodeValue.length) {
- var next = doc.createTextNode('');
- return domUtils.insertAfter(node, next);
- }
- var retval = node.splitText(offset);
- //ie8下splitText不会跟新childNodes,我们手动触发他的更新
- if (browser.ie8) {
- var tmpNode = doc.createTextNode('');
- domUtils.insertAfter(retval, tmpNode);
- domUtils.remove(tmpNode);
- }
- return retval;
- },
- /**
- * 检测节点node是否为空节点(包括空格、换行、占位符等字符)
- * @name isWhitespace
- * @grammar UM.dom.domUtils.isWhitespace(node) => true|false
- */
- isWhitespace: function (node) {
- return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
- },
- /**
- * 获取元素element相对于viewport的位置坐标
- * @name getXY
- * @grammar UM.dom.domUtils.getXY(element) => Object //返回坐标对象{x:left,y:top}
- */
- getXY: function (element) {
- var x = 0, y = 0;
- while (element.offsetParent) {
- y += element.offsetTop;
- x += element.offsetLeft;
- element = element.offsetParent;
- }
- return { 'x': x, 'y': y};
- },
- /**
- * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
- * @name on
- * @grammar UM.dom.domUtils.on(element,type,handler) //type支持数组传入
- * @example
- * UM.dom.domUtils.on(document.body,"click",function(e){
- * //e为事件对象,this为被点击元素对戏那个
- * })
- * @example
- * UM.dom.domUtils.on(document.body,["click","mousedown"],function(evt){
- * //evt为事件对象,this为被点击元素对象
- * })
- */
- on: function (element, type, handler) {
- var types = utils.isArray(type) ? type : [type],
- k = types.length;
- if (k) while (k--) {
- type = types[k];
- if (element.addEventListener) {
- element.addEventListener(type, handler, false);
- } else {
- if (!handler._d) {
- handler._d = {
- els: []
- };
- }
- var key = type + handler.toString(), index = utils.indexOf(handler._d.els, element);
- if (!handler._d[key] || index == -1) {
- if (index == -1) {
- handler._d.els.push(element);
- }
- if (!handler._d[key]) {
- handler._d[key] = function (evt) {
- return handler.call(evt.srcElement, evt || window.event);
- };
- }
- element.attachEvent('on' + type, handler._d[key]);
- }
- }
- }
- element = null;
- },
- /**
- * 解除原生DOM事件绑定
- * @name un
- * @grammar UM.dom.donUtils.un(element,type,handler) //参见<code><a href="#on">on</a></code>
- */
- 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];
- }
- }
- }
- },
- /**
- * 检查节点node是否是空inline节点
- * @name isEmptyInlineElement
- * @grammar UM.dom.domUtils.isEmptyInlineElement(node) => 1|0
- * @example
- * <b><i></i></b> => 1
- * <b><i></i><u></u></b> => 1
- * <b></b> => 1
- * <b>xx<i></i></b> => 0
- */
- isEmptyInlineElement: function (node) {
- if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
- return 0;
- }
- node = node.firstChild;
- while (node) {
- //如果是创建的bookmark就跳过
- if (domUtils.isBookmarkNode(node)) {
- return 0;
- }
- if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
- node.nodeType == 3 && !domUtils.isWhitespace(node)
- ) {
- return 0;
- }
- node = node.nextSibling;
- }
- return 1;
- },
- /**
- * 检查节点node是否为块元素
- * @name isBlockElm
- * @grammar UM.dom.domUtils.isBlockElm(node) => true|false
- */
- isBlockElm: function (node) {
- return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
- },
- /**
- * 原生方法getElementsByTagName的封装
- * @name getElementsByTagName
- * @grammar UM.dom.domUtils.getElementsByTagName(node,tagName) => Array //节点集合数组
- */
- getElementsByTagName: function (node, name, filter) {
- if (filter && utils.isString(filter)) {
- var className = filter;
- filter = function (node) {
- var result = false;
- $.each(utils.trim(className).replace(/[ ]{2,}/g, ' ').split(' '), function (i, v) {
- if ($(node).hasClass(v)) {
- result = true;
- return false;
- }
- })
- return result;
- }
- }
- name = utils.trim(name).replace(/[ ]{2,}/g, ' ').split(' ');
- var arr = [];
- for (var n = 0, ni; ni = name[n++];) {
- var list = node.getElementsByTagName(ni);
- for (var i = 0, ci; ci = list[i++];) {
- if (!filter || filter(ci))
- arr.push(ci);
- }
- }
- return arr;
- },
- /**
- * 设置节点node及其子…
Large files files are truncated, but you can click here to view the full file