//========================================================================= // // xmldom.js - an XML DOM parser in JavaScript. // version 3.0 //========================================================================= // Copyright (C) 2000 - 2002, 2003 Michael Houghton (mike@idle.org), Raymond Irving and David Joham (djoham@yahoo.com) // Modified by Chevalier Media / Matruska 2004 www.matruska.com // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version.
var whitespace="\n\r\t "; var quotes="\"'"; function convertEscapes(str) { var gt; gt=-1; while (str.indexOf("&lt;", gt + 1) > -1) { var gt=str.indexOf("&lt;", gt + 1); var newStr=str.substr(0, gt); newStr +="<"; newStr=newStr + str.substr(gt + 4, str.length); str=newStr; } gt=-1; while (str.indexOf("&gt;", gt + 1) > -1) { var gt=str.indexOf("&gt;", gt + 1); var newStr=str.substr(0, gt); newStr +=">"; newStr=newStr + str.substr(gt + 4, str.length); str=newStr; } gt=-1; while (str.indexOf("&amp;", gt + 1) > -1) { var gt=str.indexOf("&amp;", gt + 1); var newStr=str.substr(0, gt); newStr +="&"; newStr=newStr + str.substr(gt + 5, str.length); str=newStr; } return str; } function convertToEscapes(str) { var gt=-1; while (str.indexOf("&", gt + 1) > -1) { gt=str.indexOf("&", gt + 1); var newStr=str.substr(0, gt); newStr +="&amp;"; newStr=newStr + str.substr(gt + 1, str.length); str=newStr; } gt=-1; while (str.indexOf("<", gt + 1) > -1) { var gt=str.indexOf("<", gt + 1); var newStr=str.substr(0, gt); newStr +="&lt;"; newStr=newStr + str.substr(gt + 1, str.length); str=newStr; } gt=-1; while (str.indexOf(">", gt + 1) > -1) { var gt=str.indexOf(">", gt + 1); var newStr=str.substr(0, gt); newStr +="&gt;"; newStr=newStr + str.substr(gt + 1, str.length); str=newStr; } return str; } function _displayElement(domElement, strRet) { if(domElement==null) { return; } if(!(domElement.nodeType=='ELEMENT')) { return; } var tagName=domElement.tagName; var tagInfo=""; tagInfo="<" + tagName; var attributeList=domElement.getAttributeNames(); for(var intLoop=0; intLoop < attributeList.length; intLoop++) { var attribute=attributeList[intLoop]; tagInfo=tagInfo + " " + attribute + "="; tagInfo=tagInfo + "\"" + domElement.getAttribute(attribute) + "\""; } tagInfo=tagInfo + ">"; strRet=strRet+tagInfo; if(domElement.children!=null) { var domElements=domElement.children; for(var intLoop=0; intLoop < domElements.length; intLoop++) { var childNode=domElements[intLoop]; if(childNode.nodeType=='COMMENT') { strRet=strRet + "<!--" + childNode.content + "-->"; } else if(childNode.nodeType=='TEXT') { var cont=trim(childNode.content,true,true); strRet=strRet + childNode.content; } else if (childNode.nodeType=='CDATA') { var cont=trim(childNode.content,true,true); strRet=strRet + "<![CDATA[" + cont + "]]>"; } else { strRet=_displayElement(childNode, strRet); } } } strRet=strRet + "</" + tagName + ">"; return strRet; } function firstWhiteChar(str,pos) { if (isEmpty(str)) { return -1; } while(pos < str.length) { if (whitespace.indexOf(str.charAt(pos))!=-1) { return pos; } else { pos++; } } return str.length; } function isEmpty(str) { return (str==null) || (str.length==0); } function trim(trimString, leftTrim, rightTrim) { if (isEmpty(trimString)) { return ""; } if (leftTrim==null) { leftTrim=true; } if (rightTrim==null) { rightTrim=true; } var left=0; var right=0; var i=0; var k=0; if (leftTrim==true) { while ((i<trimString.length) && (whitespace.indexOf(trimString.charAt(i++))!=-1)) { left++; } } if (rightTrim==true) { k=trimString.length-1; while((k>=left) && (whitespace.indexOf(trimString.charAt(k--))!=-1)) { right++; } } return trimString.substring(left, trimString.length - right); } function XMLDoc(source, errFn) { this.topNode=null; this.errFn=errFn; this.createXMLNode=_XMLDoc_createXMLNode; this.error=_XMLDoc_error; this.getUnderlyingXMLText=_XMLDoc_getUnderlyingXMLText; this.handleNode=_XMLDoc_handleNode; this.hasErrors=false; this.insertNodeAfter=_XMLDoc_insertNodeAfter; this.insertNodeInto=_XMLDoc_insertNodeInto; this.loadXML=_XMLDoc_loadXML; this.parse=_XMLDoc_parse; this.parseAttribute=_XMLDoc_parseAttribute; this.parseDTD=_XMLDoc_parseDTD; this.parsePI=_XMLDoc_parsePI; this.parseTag=_XMLDoc_parseTag; this.removeNodeFromTree=_XMLDoc_removeNodeFromTree; this.replaceNodeContents=_XMLDoc_replaceNodeContents; this.selectNode=_XMLDoc_selectNode; this.selectNodeText=_XMLDoc_selectNodeText; this.source=source; if (this.parse()) { if (this.topNode!=null) { return this.error("expected close " + this.topNode.tagName); } else { return true; } } } function _XMLDoc_createXMLNode(strXML) { return new XMLDoc(strXML, this.errFn).docNode; } function _XMLDoc_error(str) { this.hasErrors=true; if(this.errFn){ this.errFn("ERROR: " + str); }else if(this.onerror){ this.onerror("ERROR: " + str); } return 0; } function _XMLDoc_getTagNameParams(tag,obj){ var elm=-1,e,s=tag.indexOf('['); var attr=[]; if(s>=0){ e=tag.indexOf(']'); if(e>=0)elm=tag.substr(s+1,(e-s)-1); else obj.error('expected ] near '+tag); tag=tag.substr(0,s); if(isNaN(elm) && elm!='*'){ attr=elm.substr(1,elm.length-1); attr=attr.split('='); if(attr[1]) { s=attr[1].indexOf('"'); attr[1]=attr[1].substr(s+1,attr[1].length-1); e=attr[1].indexOf('"'); if(e>=0) attr[1]=attr[1].substr(0,e); else obj.error('expected " near '+tag) };elm=-1; }else if(elm=='*') elm=-1; } return [tag,elm,attr[0],attr[1]] } function _XMLDoc_getUnderlyingXMLText() { var strRet=""; strRet=strRet + "<?xml version=\"1.0\"?>"; if (this.docNode==null) { return; } strRet=_displayElement(this.docNode, strRet); return strRet; } function _XMLDoc_handleNode(current) { if ((current.nodeType=='COMMENT') && (this.topNode!=null)) { return this.topNode.addElement(current); } else if ((current.nodeType=='TEXT') || (current.nodeType=='CDATA')) { if(this.topNode==null) { if (trim(current.content,true,false)=="") { return true; } else { return this.error("expected document node, found: " + current); } } else { return this.topNode.addElement(current); } } else if ((current.nodeType=='OPEN') || (current.nodeType=='SINGLE')) { var success=false; if(this.topNode==null) { this.docNode=current; current.parent=null; success=true; } else { success=this.topNode.addElement(current); } if (success && (current.nodeType!='SINGLE')) { this.topNode=current; } current.nodeType="ELEMENT"; return success; } else if (current.nodeType=='CLOSE') { if (this.topNode==null) { return this.error("close tag without open: " + current.toString()); } else { if (current.tagName!=this.topNode.tagName) { return this.error("expected closing " + this.topNode.tagName + ", found closing " + current.tagName); } else { this.topNode=this.topNode.getParent(); } } } return true; } function _XMLDoc_insertNodeAfter (referenceNode, newNode) { var parentXMLText=this.getUnderlyingXMLText(); var selectedNodeXMLText=referenceNode.getUnderlyingXMLText(); var originalNodePos=parentXMLText.indexOf(selectedNodeXMLText) + selectedNodeXMLText.length; var newXML=parentXMLText.substr(0,originalNodePos); newXML +=newNode.getUnderlyingXMLText(); newXML +=parentXMLText.substr(originalNodePos); var newDoc=new XMLDoc(newXML, this.errFn); return newDoc; } function _XMLDoc_insertNodeInto (referenceNode, insertNode) { var parentXMLText=this.getUnderlyingXMLText(); var selectedNodeXMLText=referenceNode.getUnderlyingXMLText(); var endFirstTag=selectedNodeXMLText.indexOf(">") + 1; var originalNodePos=parentXMLText.indexOf(selectedNodeXMLText) + endFirstTag; var newXML=parentXMLText.substr(0,originalNodePos); newXML +=insertNode.getUnderlyingXMLText(); newXML +=parentXMLText.substr(originalNodePos); var newDoc=new XMLDoc(newXML, this.errFn); return newDoc; } function _XMLDoc_loadXML(source){ this.topNode=null; this.hasErrors=false; this.source=source; return this.parse(); } function _XMLDoc_parse() { var pos=0; err=false; while(!err) { var closing_tag_prefix=''; var chpos=this.source.indexOf('<',pos); var open_length=1; var open; var close; if (chpos==-1) { break; } open=chpos; var str=this.source.substring(pos, open); if (str.length!=0) { err=!this.handleNode(new XMLNode('TEXT',this, str)); } if (chpos==this.source.indexOf("<?",pos)) { pos=this.parsePI(this.source, pos + 2); if (pos==0) { err=true; } continue; } if (chpos==this.source.indexOf("<!DOCTYPE",pos)) { pos=this.parseDTD(this.source, chpos+ 9); if (pos==0) { err=true; } continue; } if(chpos==this.source.indexOf('<!--',pos)) { open_length=4; closing_tag_prefix='--'; } if (chpos==this.source.indexOf('<![CDATA[',pos)) { open_length=9; closing_tag_prefix=']]'; } chpos=this.source.indexOf(closing_tag_prefix + '>',chpos); if (chpos==-1) { return this.error("expected closing tag sequence: " + closing_tag_prefix + '>'); } close=chpos + closing_tag_prefix.length; str=this.source.substring(open+1, close); var n=this.parseTag(str); if (n) { err=!this.handleNode(n); } pos=close +1; } return !err; } function _XMLDoc_parseAttribute(src,pos,node) { while ((pos<src.length) && (whitespace.indexOf(src.charAt(pos))!=-1)) { pos++; } if (pos >=src.length) { return pos; } var p1=pos; while ((pos < src.length) && (src.charAt(pos)!='=')) { pos++; } var msg="attributes must have values"; if(pos >=src.length) { return this.error(msg); } var paramname=trim(src.substring(p1,pos++),false,true); while ((pos < src.length) && (whitespace.indexOf(src.charAt(pos))!=-1)) { pos++; } if (pos >=src.length) { return this.error(msg); } msg="attribute values must be in quotes"; var quote=src.charAt(pos++); if (quotes.indexOf(quote)==-1) { return this.error(msg); } p1=pos; while ((pos < src.length) && (src.charAt(pos)!=quote)) { pos++; } if (pos >=src.length) { return this.error(msg); } if (!node.addAttribute(paramname,trim(src.substring(p1,pos++),false,true))) { return 0; } return pos; } function _XMLDoc_parseDTD(str,pos) { var firstClose=str.indexOf('>',pos); if (firstClose==-1) { return this.error("error in DTD: expected '>'"); } var closing_tag_prefix=''; var firstOpenSquare=str.indexOf('[',pos); if ((firstOpenSquare!=-1) && (firstOpenSquare < firstClose)) { closing_tag_prefix=']'; } while(true) { var closepos=str.indexOf(closing_tag_prefix + '>',pos); if (closepos==-1) { return this.error("expected closing tag sequence: " + closing_tag_prefix + '>'); } pos=closepos + closing_tag_prefix.length +1; if (str.substring(closepos-1,closepos+2) !=']]>') { break; } } return pos; } function _XMLDoc_parsePI(str,pos) { var closepos=str.indexOf('?>',pos); return closepos + 2; } function _XMLDoc_parseTag(src) { if (src.indexOf('!--')==0) { return new XMLNode('COMMENT', this, src.substring(3,src.length-2)); } if (src.indexOf('![CDATA[')==0) { return new XMLNode('CDATA', this, src.substring(8,src.length-2)); } var n=new XMLNode(); n.doc=this; if (src.charAt(0)=='/') { n.nodeType='CLOSE'; src=src.substring(1); } else { n.nodeType='OPEN'; } if (src.charAt(src.length-1)=='/') { if (n.nodeType=='CLOSE') { return this.error("singleton close tag"); } else { n.nodeType='SINGLE'; } src=src.substring(0,src.length-1); } if (n.nodeType!='CLOSE') { n.attributes=new Array(); } if (n.nodeType=='OPEN') { n.children=new Array(); } src=trim(src,true,true); if (src.length==0) { return this.error("empty tag"); } var endOfName=firstWhiteChar(src,0); if (endOfName==-1) { n.tagName=src; return n; } n.tagName=src.substring(0,endOfName); var pos=endOfName; while(pos< src.length) { pos=this.parseAttribute(src, pos, n); if (this.pos==0) { return null; } } return n; } function _XMLDoc_removeNodeFromTree(node) { var parentXMLText=this.getUnderlyingXMLText(); var selectedNodeXMLText=node.getUnderlyingXMLText(); var originalNodePos=parentXMLText.indexOf(selectedNodeXMLText); var newXML=parentXMLText.substr(0,originalNodePos); newXML +=parentXMLText.substr(originalNodePos + selectedNodeXMLText.length); var newDoc=new XMLDoc(newXML, this.errFn); return newDoc; } function _XMLDoc_replaceNodeContents(referenceNode, newContents) { var newNode=this.createXMLNode("<X>" + newContents + "</X>"); referenceNode.children=newNode.children; return this; } function _XMLDoc_selectNode(tagpath){ tagpath=trim(tagpath, true, true); var srcnode,node,tag,params,elm,rg; var tags,attrName,attrValue,ok; srcnode=node=((this.source)?this.docNode:this); if (!tagpath) return node; if(tagpath.indexOf('/')==0)tagpath=tagpath.substr(1); tagpath=tagpath.replace(tag,''); tags=tagpath.split('/'); tag=tags[0]; if(tag){ if(tagpath.indexOf('/')==0)tagpath=tagpath.substr(1); tagpath=tagpath.replace(tag,''); params=_XMLDoc_getTagNameParams(tag,this); tag=params[0];elm=params[1]; attrName=params[2];attrValue=params[3]; node=(tag=='*')? node.getElements():node.getElements(tag); if (node.length) { if(elm<0){ srcnode=node;var i=0; while(i<srcnode.length){ if(attrName){ if (srcnode[i].getAttribute(attrName)!=attrValue) ok=false; else ok=true; }else ok=true; if(ok){ node=srcnode[i].selectNode(tagpath); if(node) return node; } i++; } }else if (elm<node.length){ node=node[elm].selectNode(tagpath); if(node) return node; } } } } function _XMLDoc_selectNodeText(tagpath){ var node=this.selectNode(tagpath); if (node !=null) { return node.getText(); } else { return null; } } function XMLNode(nodeType,doc, str) { if (nodeType=='TEXT' || nodeType=='CDATA' || nodeType=='COMMENT') { this.content=str; } else { this.content=null; } this.attributes=null; this.children=null; this.doc=doc; this.nodeType=nodeType; this.parent=""; this.tagName=""; this.error=_XMLDoc_error; this.addAttribute=_XMLNode_addAttribute; this.addElement=_XMLNode_addElement; this.getAttribute=_XMLNode_getAttribute; this.getAttributeNames=_XMLNode_getAttributeNames; this.getElementById=_XMLNode_getElementById; this.getElements=_XMLNode_getElements; this.getText=_XMLNode_getText; this.getParent=_XMLNode_getParent; this.getUnderlyingXMLText=_XMLNode_getUnderlyingXMLText; this.removeAttribute=_XMLNode_removeAttribute; this.selectNode=_XMLDoc_selectNode; this.selectNodeText=_XMLDoc_selectNodeText; this.toString=_XMLNode_toString; } function _XMLNode_addAttribute(attributeName,attributeValue) { this.attributes['_' + attributeName]=attributeValue; return true; } function _XMLNode_addElement(node) { node.parent=this; this.children[this.children.length]=node; return true; } function _XMLNode_getAttribute(name) { if (this.attributes==null) { return null; } return this.attributes['_' + name]; } function _XMLNode_getAttributeNames() { if (this.attributes==null) { var ret=new Array(); return ret; } var attlist=new Array(); for (var a in this.attributes) { attlist[attlist.length]=a.substring(1); } return attlist; } function _XMLNode_getElementById(id) { var node=this; var ret; if (node.getAttribute("id")==id) { return node; } else{ var elements=node.getElements(); var intLoop=0; while (intLoop < elements.length) { var element=elements[intLoop]; ret=element.getElementById(id); if (ret !=null) { break; } intLoop++; } } return ret; } function _XMLNode_getElements(byName) { if (this.children==null) { var ret=new Array(); return ret; } var elements=new Array(); for (var i=0; i<this.children.length; i++) { if ((this.children[i].nodeType=='ELEMENT') && ((!byName) || (this.children[i].tagName==byName))) { elements[elements.length]=this.children[i]; } } return elements; } function _XMLNode_getText() { if (this.nodeType=='ELEMENT') { if (this.children==null) { return null; } var str=""; for (var i=0; i < this.children.length; i++) { var t=this.children[i].getText(); str +=(t==null ? "" : t); } return str; } else if (this.nodeType=='TEXT') { return convertEscapes(this.content); } else { return this.content; } } function _XMLNode_getParent() { return this.parent; } function _XMLNode_getUnderlyingXMLText() { var strRet=""; strRet=_displayElement(this, strRet); return strRet; } function _XMLNode_removeAttribute(attributeName) { if(attributeName==null) { return this.doc.error("You must pass an attribute name into the removeAttribute function"); } var attributes=this.getAttributeNames(); var intCount=attributes.length; var tmpAttributeValues=new Array(); for (intLoop=0; intLoop < intCount; intLoop++) { tmpAttributeValues[intLoop]=this.getAttribute(attributes[intLoop]); } this.attributes=new Array(); for (intLoop=0; intLoop < intCount; intLoop++) { if (attributes[intLoop] !=attributeName) { this.addAttribute(attributes[intLoop], tmpAttributeValues[intLoop]); } } return true; } function _XMLNode_toString() { return "" + this.nodeType + ":" + (this.nodeType=='TEXT' || this.nodeType=='CDATA' || this.nodeType=='COMMENT' ? this.content : this.tagName); }

