// formats XML into a multi-lined, indented format // a good toString replacement for XML with ignoreWhite = true XMLNode.prototype.format = function(indent){ if (indent == undefined) indent = ""; var str = ""; var currNode = this.firstChild; do{ if (currNode.hasChildNodes()){ str += indent + currNode.cloneNode(0).toString().slice(0,-2) + ">\n"; str += currNode.format(indent+"\t"); str += indent + "\n"; }else{ str += indent + currNode.toString() + "\n"; } }while (currNode = currNode.nextSibling); return str; } // moves a child within its set of children (to before a beforeNode) XMLNode.prototype.moveBefore = function(beforeNode){ beforeNode.parentNode.insertBefore( this.cloneNode(true), beforeNode); this.removeNode(); } // adds a .root property that references the document root // element of an XML instance through any of its nodes XMLNode.prototype.root = function(){ var r = this; while(r.parentNode) r = r.parentNode; return r.firstChild; }; XMLNode.prototype.addProperty("root", function(){ return this.root(); }, function(n){ r = this.root(); r.parentNode.appendChild(n); r.removeNode(); } ); // adds a firstNodes property (read-only) which returns // an array of all the nested first child elements within an // xml node. ex: firstChild.firstChild.firstChild == firstNodes[2] XMLNode.prototype.addProperty("firstNodes", function(){ var c = []; var n = this; while(n = n.firstChild) c.push(n); return c; },null ); // adds a text property that represents the text (nodeValue) // of a text node. This can be called from a text node *or* an // element which contains a text node (retrieves the last child) // making it easy to access text from an element which only // contains one text node XMLNode.prototype.text = function(){ var n = this; if (n.nodeType == 1) n = this.lastChild; return n; } XMLNode.prototype.addProperty("text", function(){ var n = this.text(); if (n.nodeType == 3) return n.nodeValue; return ""; }, function(txt){ var n = this.text(); if (n.nodeType == 3) n.nodeValue = txt; else this.appendChild(new XML().createTextNode(txt)); } ); // Find a child element by name and index (nth element of that name) XMLNode.prototype.findChild = function(name, index){ if (!index) index = 0; var count = 0; var currNode = this.firstChild; do{ if (currNode.nodeName == name){ count++; if (count == index) return currNode; } }while (currNode = currNode.nextSibling); return false; } // Removes and returns an XML node based on index XMLNode.prototype.extractChild = function(i){ var n = this.childNodes[i].cloneNode(true); this.childNodes[i].removeNode(); return n; } // Swap the positions of two child nodes within their parent's childNodes array XMLNode.prototype.swapChildren = function(i, i2){ if (i==i2) return(0); if (i > i2){ var temp = i; i = i2; i2 = temp; } var n = this.extractChild(i); var n2 = this.extractChild(i2-1); this.insertBefore(n2, this.childNodes[i]); this.insertBefore(n, this.childNodes[i2]); } // Sort child nodes based on a compare function (uses a simple quicksort) XMLNode.prototype.sortChildren = function(cmp, low,high) { var a = this.childNodes; if (cmp == undefined) cmp = this.sortChildren.defaultCompare; if (low == undefined) low = 0; if (high == undefined) high = a.length; var p = a[low], w = low+1, h = high; while(w < h) { if (cmp(a[w], p)) w++; else this.swapChildren(w, (--h)); } this.swapChildren(low, (--w)); if (w-low > 1) this.sortChildren(cmp, low, w); if (high-h > 1) this.sortChildren(cmp, h, high); } // A default sort function for use in sortChildren XMLNode.prototype.sortChildren.defaultCompare = function(a,b){ return a.nodeName <= b.nodeName; } // removes a node but leaves all child nodes it contained in its place XMLNode.prototype.explodeIntoParent = function(){ if (!this.parentNode) return false; while (this.hasChildNodes()){ this.parentNode.insertBefore(this.firstChild, this); } this.removeNode(); return true; } // gets the index of the current node in its parent's childNodes array XMLNode.prototype.getIndexInParent = function(){ var i = 0, node = this; while (node = node.previousSibling) i++; return i; } // trims white space around text // can be used to remove white space around formatted text node text _global.TrimWhiteSpace = function(str){ var beg = 0, end = str.length-1; while (str.charAt(beg).isWhite()) beg++; while (str.charAt(end).isWhite()) end--; return str.slice(beg, end+1); } // determines if a string is made up of white space // used by TrimWhiteSpace String.prototype.isWhite = function(){ return !isNaN(" "+this+" 0"); }