123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772 |
- /**
- * 编辑器模拟的节点类
- * @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) {
- //这里就针对
- //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
- //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
- //有可能做的不够
- attrhtml.push(
- a +
- (attrs[a] !== undefined
- ? '="' +
- (notTransAttrs[a]
- ? utils.html(attrs[a]).replace(/["]/g, function(a) {
- return """;
- })
- : utils.unhtml(attrs[a])) +
- '"'
- : "")
- );
- }
- attrhtml = attrhtml.join(" ");
- }
- arr.push(
- "<" +
- node.tagName +
- (attrhtml ? " " + attrhtml : "") +
- (dtd.$empty[node.tagName] ? "/" : "") +
- ">"
- );
- //插入新行
- if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != "pre") {
- if (node.children && node.children.length) {
- current = insertLine(arr, current, true);
- insertIndent(arr, current);
- }
- }
- if (node.children && node.children.length) {
- for (var i = 0, ci; (ci = node.children[i++]); ) {
- if (
- formatter &&
- ci.type == "element" &&
- !dtd.$inlineWithA[ci.tagName] &&
- i > 1
- ) {
- insertLine(arr, current);
- insertIndent(arr, current);
- }
- nodeToHtml(ci, arr, formatter, current);
- }
- }
- if (!dtd.$empty[node.tagName]) {
- if (
- formatter &&
- !dtd.$inlineWithA[node.tagName] &&
- node.tagName != "pre"
- ) {
- if (node.children && node.children.length) {
- current = insertLine(arr, current);
- insertIndent(arr, current);
- }
- }
- arr.push("</" + node.tagName + ">");
- }
- }
- function isComment(node, arr) {
- arr.push("<!--" + node.data + "-->");
- }
- function getNodeById(root, id) {
- var node;
- if (root.type == "element" && root.getAttr("id") == id) {
- return root;
- }
- if (root.children && root.children.length) {
- for (var i = 0, ci; (ci = root.children[i++]); ) {
- if ((node = getNodeById(ci, id))) {
- return node;
- }
- }
- }
- }
- function getNodesByTagName(node, tagName, arr) {
- if (node.type == "element" && node.tagName == tagName) {
- arr.push(node);
- }
- if (node.children && node.children.length) {
- for (var i = 0, ci; (ci = node.children[i++]); ) {
- getNodesByTagName(ci, tagName, arr);
- }
- }
- }
- function nodeTraversal(root, fn) {
- if (root.children && root.children.length) {
- for (var i = 0, ci; (ci = root.children[i]); ) {
- nodeTraversal(ci, fn);
- //ci被替换的情况,这里就不再走 fn了
- if (ci.parentNode) {
- if (ci.children && ci.children.length) {
- fn(ci);
- }
- if (ci.parentNode) i++;
- }
- }
- } else {
- fn(root);
- }
- }
- uNode.prototype = {
- /**
- * 当前节点对象,转换成html文本
- * @method toHtml
- * @return { String } 返回转换后的html字符串
- * @example
- * ```javascript
- * node.toHtml();
- * ```
- */
- /**
- * 当前节点对象,转换成html文本
- * @method toHtml
- * @param { Boolean } formatter 是否格式化返回值
- * @return { String } 返回转换后的html字符串
- * @example
- * ```javascript
- * node.toHtml( true );
- * ```
- */
- toHtml: function(formatter) {
- var arr = [];
- nodeToHtml(this, arr, formatter, 0);
- return arr.join("");
- },
- /**
- * 获取节点的html内容
- * @method innerHTML
- * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
- * @return { String } 返回节点的html内容
- * @example
- * ```javascript
- * var htmlstr = node.innerHTML();
- * ```
- */
- /**
- * 设置节点的html内容
- * @method innerHTML
- * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
- * @param { String } htmlstr 传入要设置的html内容
- * @return { UE.uNode } 返回节点本身
- * @example
- * ```javascript
- * node.innerHTML('<span>text</span>');
- * ```
- */
- innerHTML: function(htmlstr) {
- if (this.type != "element" || dtd.$empty[this.tagName]) {
- return this;
- }
- if (utils.isString(htmlstr)) {
- if (this.children) {
- for (var i = 0, ci; (ci = this.children[i++]); ) {
- ci.parentNode = null;
- }
- }
- this.children = [];
- var tmpRoot = 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('<span>text</span>');
- * ```
- */
- innerText: function(textStr, noTrans) {
- if (this.type != "element" || dtd.$empty[this.tagName]) {
- return this;
- }
- if (textStr) {
- if (this.children) {
- for (var i = 0, ci; (ci = this.children[i++]); ) {
- ci.parentNode = null;
- }
- }
- this.children = [];
- this.appendChild(uNode.createText(textStr, noTrans));
- return this;
- } else {
- return this.toHtml().replace(/<[^>]+>/g, "");
- }
- },
- /**
- * 获取当前对象的data属性
- * @method getData
- * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
- * @example
- * ```javascript
- * node.getData();
- * ```
- */
- getData: function() {
- if (this.type == "element") return "";
- return this.data;
- },
- /**
- * 获取当前节点下的第一个子节点
- * @method firstChild
- * @return { 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;
- }
- };
- })();
|