/** * 编辑器模拟的节点类 * @file * @module UE * @class uNode * @since 1.2.6.1 */ /** * UEditor公用空间,UEditor所有的功能都挂载在该空间下 * @unfile * @module UE */ (function () { /** * 编辑器模拟的节点类 * @unfile * @module UE * @class uNode */ /** * 通过一个键值对,创建一个uNode对象 * @constructor * @param { Object } attr 传入要创建的uNode的初始属性 * @example * ```javascript * var node = new uNode({ * type:'element', * tagName:'span', * attrs:{style:'font-size:14px;'} * }) * ``` */ var uNode = UE.uNode = function (obj) { this.type = obj.type; this.data = obj.data; this.tagName = obj.tagName; this.parentNode = obj.parentNode; this.attrs = obj.attrs || {}; this.children = obj.children; }; var notTransAttrs = { 'href':1, 'src':1, '_src':1, '_href':1, 'cdata_data':1 }; var notTransTagName = { style:1, script:1 }; var indentChar = ' ', breakChar = '\n'; function insertLine(arr, current, begin) { arr.push(breakChar); return current + (begin ? 1 : -1); } function insertIndent(arr, current) { //插入缩进 for (var i = 0; i < current; i++) { arr.push(indentChar); } } //创建uNode的静态方法 //支持标签和html uNode.createElement = function (html) { if (/[<>]/.test(html)) { return UE.htmlparser(html).children[0] } else { return new uNode({ type:'element', children:[], tagName:html }) } }; uNode.createText = function (data,noTrans) { return new UE.uNode({ type:'text', 'data':noTrans ? data : utils.unhtml(data || '') }) }; function nodeToHtml(node, arr, formatter, current) { switch (node.type) { case 'root': for (var i = 0, ci; ci = node.children[i++];) { //插入新行 if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) { insertLine(arr, current, true); insertIndent(arr, current) } nodeToHtml(ci, arr, formatter, current) } break; case 'text': isText(node, arr); break; case 'element': isElement(node, arr, formatter, current); break; case 'comment': isComment(node, arr, formatter); } return arr; } function isText(node, arr) { if(node.parentNode.tagName == 'pre'){ //源码模式下输入html标签,不能做转换处理,直接输出 arr.push(node.data) }else{ arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,'  ')) } } function isElement(node, arr, formatter, current) { var attrhtml = ''; if (node.attrs) { attrhtml = []; var attrs = node.attrs; for (var a in attrs) { //这里就针对 //

'

//这里边的\"做转换,要不用innerHTML直接被截断了,属性src //有可能做的不够 attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) { return '"' }) : utils.unhtml(attrs[a])) + '"' : '')) } attrhtml = attrhtml.join(' '); } arr.push('<' + node.tagName + (attrhtml ? ' ' + attrhtml : '') + (dtd.$empty[node.tagName] ? '\/' : '' ) + '>' ); //插入新行 if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') { if(node.children && node.children.length){ current = insertLine(arr, current, true); insertIndent(arr, current) } } if (node.children && node.children.length) { for (var i = 0, ci; ci = node.children[i++];) { if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) { insertLine(arr, current); insertIndent(arr, current) } nodeToHtml(ci, arr, formatter, current) } } if (!dtd.$empty[node.tagName]) { if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') { if(node.children && node.children.length){ current = insertLine(arr, current); insertIndent(arr, current) } } arr.push('<\/' + node.tagName + '>'); } } function isComment(node, arr) { arr.push(''); } function getNodeById(root, id) { var node; if (root.type == 'element' && root.getAttr('id') == id) { return root; } if (root.children && root.children.length) { for (var i = 0, ci; ci = root.children[i++];) { if (node = getNodeById(ci, id)) { return node; } } } } function getNodesByTagName(node, tagName, arr) { if (node.type == 'element' && node.tagName == tagName) { arr.push(node); } if (node.children && node.children.length) { for (var i = 0, ci; ci = node.children[i++];) { getNodesByTagName(ci, tagName, arr) } } } function nodeTraversal(root,fn){ if(root.children && root.children.length){ for(var i= 0,ci;ci=root.children[i];){ nodeTraversal(ci,fn); //ci被替换的情况,这里就不再走 fn了 if(ci.parentNode ){ if(ci.children && ci.children.length){ fn(ci) } if(ci.parentNode) i++ } } }else{ fn(root) } } uNode.prototype = { /** * 当前节点对象,转换成html文本 * @method toHtml * @return { String } 返回转换后的html字符串 * @example * ```javascript * node.toHtml(); * ``` */ /** * 当前节点对象,转换成html文本 * @method toHtml * @param { Boolean } formatter 是否格式化返回值 * @return { String } 返回转换后的html字符串 * @example * ```javascript * node.toHtml( true ); * ``` */ toHtml:function (formatter) { var arr = []; nodeToHtml(this, arr, formatter, 0); return arr.join('') }, /** * 获取节点的html内容 * @method innerHTML * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 * @return { String } 返回节点的html内容 * @example * ```javascript * var htmlstr = node.innerHTML(); * ``` */ /** * 设置节点的html内容 * @method innerHTML * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 * @param { String } htmlstr 传入要设置的html内容 * @return { UE.uNode } 返回节点本身 * @example * ```javascript * node.innerHTML('text'); * ``` */ innerHTML:function (htmlstr) { if (this.type != 'element' || dtd.$empty[this.tagName]) { return this; } if (utils.isString(htmlstr)) { if(this.children){ for (var i = 0, ci; ci = this.children[i++];) { ci.parentNode = null; } } this.children = []; var tmpRoot = UE.htmlparser(htmlstr); for (var i = 0, ci; ci = tmpRoot.children[i++];) { this.children.push(ci); ci.parentNode = this; } return this; } else { var tmpRoot = new UE.uNode({ type:'root', children:this.children }); return tmpRoot.toHtml(); } }, /** * 获取节点的纯文本内容 * @method innerText * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 * @return { String } 返回节点的存文本内容 * @example * ```javascript * var textStr = node.innerText(); * ``` */ /** * 设置节点的纯文本内容 * @method innerText * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 * @param { String } textStr 传入要设置的文本内容 * @return { UE.uNode } 返回节点本身 * @example * ```javascript * node.innerText('text'); * ``` */ innerText:function (textStr,noTrans) { if (this.type != 'element' || dtd.$empty[this.tagName]) { return this; } if (textStr) { if(this.children){ for (var i = 0, ci; ci = this.children[i++];) { ci.parentNode = null; } } this.children = []; this.appendChild(uNode.createText(textStr,noTrans)); return this; } else { return this.toHtml().replace(/<[^>]+>/g, ''); } }, /** * 获取当前对象的data属性 * @method getData * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性 * @example * ```javascript * node.getData(); * ``` */ getData:function () { if (this.type == 'element') return ''; return this.data }, /** * 获取当前节点下的第一个子节点 * @method firstChild * @return { UE.uNode } 返回第一个子节点 * @example * ```javascript * node.firstChild(); //返回第一个子节点 * ``` */ firstChild:function () { // if (this.type != 'element' || dtd.$empty[this.tagName]) { // return this; // } return this.children ? this.children[0] : null; }, /** * 获取当前节点下的最后一个子节点 * @method lastChild * @return { UE.uNode } 返回最后一个子节点 * @example * ```javascript * node.lastChild(); //返回最后一个子节点 * ``` */ lastChild:function () { // if (this.type != 'element' || dtd.$empty[this.tagName] ) { // return this; // } return this.children ? this.children[this.children.length - 1] : null; }, /** * 获取和当前节点有相同父亲节点的前一个节点 * @method previousSibling * @return { UE.uNode } 返回前一个节点 * @example * ```javascript * node.children[2].previousSibling(); //返回子节点node.children[1] * ``` */ previousSibling : function(){ var parent = this.parentNode; for (var i = 0, ci; ci = parent.children[i]; i++) { if (ci === this) { return i == 0 ? null : parent.children[i-1]; } } }, /** * 获取和当前节点有相同父亲节点的后一个节点 * @method nextSibling * @return { UE.uNode } 返回后一个节点,找不到返回null * @example * ```javascript * node.children[2].nextSibling(); //如果有,返回子节点node.children[3] * ``` */ nextSibling : function(){ var parent = this.parentNode; for (var i = 0, ci; ci = parent.children[i++];) { if (ci === this) { return parent.children[i]; } } }, /** * 用新的节点替换当前节点 * @method replaceChild * @param { UE.uNode } target 要替换成该节点参数 * @param { UE.uNode } source 要被替换掉的节点 * @return { UE.uNode } 返回替换之后的节点对象 * @example * ```javascript * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点 * ``` */ replaceChild:function (target, source) { if (this.children) { if(target.parentNode){ target.parentNode.removeChild(target); } for (var i = 0, ci; ci = this.children[i]; i++) { if (ci === source) { this.children.splice(i, 1, target); source.parentNode = null; target.parentNode = this; return target; } } } }, /** * 在节点的子节点列表最后位置插入一个节点 * @method appendChild * @param { UE.uNode } node 要插入的节点 * @return { UE.uNode } 返回刚插入的子节点 * @example * ```javascript * node.appendChild( newNode ); //在node内插入子节点newNode * ``` */ appendChild:function (node) { if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) { if (!this.children) { this.children = [] } if(node.parentNode){ node.parentNode.removeChild(node); } for (var i = 0, ci; ci = this.children[i]; i++) { if (ci === node) { this.children.splice(i, 1); break; } } this.children.push(node); node.parentNode = this; return node; } }, /** * 在传入节点的前面插入一个节点 * @method insertBefore * @param { UE.uNode } target 要插入的节点 * @param { UE.uNode } source 在该参数节点前面插入 * @return { UE.uNode } 返回刚插入的子节点 * @example * ```javascript * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode * ``` */ insertBefore:function (target, source) { if (this.children) { if(target.parentNode){ target.parentNode.removeChild(target); } for (var i = 0, ci; ci = this.children[i]; i++) { if (ci === source) { this.children.splice(i, 0, target); target.parentNode = this; return target; } } } }, /** * 在传入节点的后面插入一个节点 * @method insertAfter * @param { UE.uNode } target 要插入的节点 * @param { UE.uNode } source 在该参数节点后面插入 * @return { UE.uNode } 返回刚插入的子节点 * @example * ```javascript * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode * ``` */ insertAfter:function (target, source) { if (this.children) { if(target.parentNode){ target.parentNode.removeChild(target); } for (var i = 0, ci; ci = this.children[i]; i++) { if (ci === source) { this.children.splice(i + 1, 0, target); target.parentNode = this; return target; } } } }, /** * 从当前节点的子节点列表中,移除节点 * @method removeChild * @param { UE.uNode } node 要移除的节点引用 * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置 * @return { * } 返回刚移除的子节点 * @example * ```javascript * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置 * ``` */ removeChild:function (node,keepChildren) { if (this.children) { for (var i = 0, ci; ci = this.children[i]; i++) { if (ci === node) { this.children.splice(i, 1); ci.parentNode = null; if(keepChildren && ci.children && ci.children.length){ for(var j= 0,cj;cj=ci.children[j];j++){ this.children.splice(i+j,0,cj); cj.parentNode = this; } } return ci; } } } }, /** * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值 * @method getAttr * @param { String } attrName 要获取的属性名称 * @return { * } 返回attrs对象下的属性值 * @example * ```javascript * node.getAttr('title'); * ``` */ getAttr:function (attrName) { return this.attrs && this.attrs[attrName.toLowerCase()] }, /** * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值 * @method setAttr * @param { String } attrName 要设置的属性名称 * @param { * } attrVal 要设置的属性值,类型视设置的属性而定 * @return { * } 返回attrs对象下的属性值 * @example * ```javascript * node.setAttr('title','标题'); * ``` */ setAttr:function (attrName, attrVal) { if (!attrName) { delete this.attrs; return; } if(!this.attrs){ this.attrs = {}; } if (utils.isObject(attrName)) { for (var a in attrName) { if (!attrName[a]) { delete this.attrs[a] } else { this.attrs[a.toLowerCase()] = attrName[a]; } } } else { if (!attrVal) { delete this.attrs[attrName] } else { this.attrs[attrName.toLowerCase()] = attrVal; } } }, /** * 获取当前节点在父节点下的位置索引 * @method getIndex * @return { Number } 返回索引数值,如果没有父节点,返回-1 * @example * ```javascript * node.getIndex(); * ``` */ getIndex:function(){ var parent = this.parentNode; for(var i= 0,ci;ci=parent.children[i];i++){ if(ci === this){ return i; } } return -1; }, /** * 在当前节点下,根据id查找节点 * @method getNodeById * @param { String } id 要查找的id * @return { UE.uNode } 返回找到的节点 * @example * ```javascript * node.getNodeById('textId'); * ``` */ getNodeById:function (id) { var node; if (this.children && this.children.length) { for (var i = 0, ci; ci = this.children[i++];) { if (node = getNodeById(ci, id)) { return node; } } } }, /** * 在当前节点下,根据元素名称查找节点列表 * @method getNodesByTagName * @param { String } tagNames 要查找的元素名称 * @return { Array } 返回找到的节点列表 * @example * ```javascript * node.getNodesByTagName('span'); * ``` */ getNodesByTagName:function (tagNames) { tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' '); var arr = [], me = this; utils.each(tagNames, function (tagName) { if (me.children && me.children.length) { for (var i = 0, ci; ci = me.children[i++];) { getNodesByTagName(ci, tagName, arr) } } }); return arr; }, /** * 根据样式名称,获取节点的样式值 * @method getStyle * @param { String } name 要获取的样式名称 * @return { String } 返回样式值 * @example * ```javascript * node.getStyle('font-size'); * ``` */ getStyle:function (name) { var cssStyle = this.getAttr('style'); if (!cssStyle) { return '' } var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i'); var match = cssStyle.match(reg); if (match && match[0]) { return match[2] } return ''; }, /** * 给节点设置样式 * @method setStyle * @param { String } name 要设置的的样式名称 * @param { String } val 要设置的的样值 * @example * ```javascript * node.setStyle('font-size', '12px'); * ``` */ setStyle:function (name, val) { function exec(name, val) { var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi'); cssStyle = cssStyle.replace(reg, '$1'); if (val) { cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle } } var cssStyle = this.getAttr('style'); if (!cssStyle) { cssStyle = ''; } if (utils.isObject(name)) { for (var a in name) { exec(a, name[a]) } } else { exec(name, val) } this.setAttr('style', utils.trim(cssStyle)) }, /** * 传入一个函数,递归遍历当前节点下的所有节点 * @method traversal * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数 * @example * ```javascript * traversal(node, function(){ * console.log(node.type); * }); * ``` */ traversal:function(fn){ if(this.children && this.children.length){ nodeTraversal(this,fn); } return this; } } })();