Introduction to XML in Flash
       by senocular

Additional Helpful Methods
Let's not stop with what Macromedia has provided for us to edit XML in Flash. We can take that framework and build more useful and straight-forward methods to further help in editing XML content while in Flash. In fact, maybe we can take what's been given to us and make them better. The following represents a few examples that add methods to the XMLNode object and/or the XML object in Flash.*

Download AS

 

// 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 + "</" + currNode.nodeName + ">\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){
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");
}

 

* All methods provided are in ActionScript 1.0 format. They will work, for the most part, with AS 2.0 but would require that you not strictly type your XML instances (or, rather, add the methods used into the intrinsic class definition). Extending XMLNode in ActionScript 2.0 is not really an option as it's the XML class that instantiates XMLNodes internally, not you. So you have no way of controlling how they are made and with using which constructor. It's this kind of situation where the flexibility of AS 1.0 really pays off.

You can find more such examples at Layer51 Prototypes. There's also XPath implementation for Flash (AS1, AS2) provided by XFactor Studio. XPath provides an alternate, more user friendly means of addressing XML content.


 




SUPPORTERS:

kirupa.com's fast and reliable hosting provided by Media Temple.