dojo.provide("dojo.dom");
dojo.dom.ELEMENT_NODE = 1;
dojo.dom.ATTRIBUTE_NODE = 2;
dojo.dom.TEXT_NODE = 3;
dojo.dom.CDATA_SECTION_NODE = 4;
dojo.dom.ENTITY_REFERENCE_NODE = 5;
dojo.dom.ENTITY_NODE = 6;
dojo.dom.PROCESSING_INSTRUCTION_NODE = 7;
dojo.dom.COMMENT_NODE = 8;
dojo.dom.DOCUMENT_NODE = 9;
dojo.dom.DOCUMENT_TYPE_NODE = 10;
dojo.dom.DOCUMENT_FRAGMENT_NODE = 11;
dojo.dom.NOTATION_NODE = 12;
dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml";
/**
* comprehensive list of XML namespaces
**/
dojo.dom.xmlns = {
// summary
// aliases for various common XML namespaces
svg : "http://www.w3.org/2000/svg",
smil : "http://www.w3.org/2001/SMIL20/",
mml : "http://www.w3.org/1998/Math/MathML",
cml : "http://www.xml-cml.org",
xlink : "http://www.w3.org/1999/xlink",
xhtml : "http://www.w3.org/1999/xhtml",
xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
xbl : "http://www.mozilla.org/xbl",
fo : "http://www.w3.org/1999/XSL/Format",
xsl : "http://www.w3.org/1999/XSL/Transform",
xslt : "http://www.w3.org/1999/XSL/Transform",
xi : "http://www.w3.org/2001/XInclude",
xforms : "http://www.w3.org/2002/01/xforms",
saxon : "http://icl.com/saxon",
xalan : "http://xml.apache.org/xslt",
xsd : "http://www.w3.org/2001/XMLSchema",
dt: "http://www.w3.org/2001/XMLSchema-datatypes",
xsi : "http://www.w3.org/2001/XMLSchema-instance",
rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
rdfs : "http://www.w3.org/2000/01/rdf-schema#",
dc : "http://purl.org/dc/elements/1.1/",
dcq: "http://purl.org/dc/qualifiers/1.0",
"soap-env" : "http://schemas.xmlsoap.org/soap/envelope/",
wsdl : "http://schemas.xmlsoap.org/wsdl/",
AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
};
dojo.dom.isNode = function(/* object */wh){
// summary:
// checks to see if wh is actually a node.
if(typeof Element == "function") {
try {
return wh instanceof Element; // boolean
} catch(e) {}
} else {
// best-guess
return wh && !isNaN(wh.nodeType); // boolean
}
}
dojo.dom.getUniqueId = function(){
// summary:
// returns a unique string for use with any DOM element
var _document = dojo.doc();
do {
var id = "dj_unique_" + (++arguments.callee._idIncrement);
}while(_document.getElementById(id));
return id; // string
}
dojo.dom.getUniqueId._idIncrement = 0;
dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(/* Element */parentNode, /* string? */tagName){
// summary:
// returns the first child element matching tagName
var node = parentNode.firstChild;
while(node && node.nodeType != dojo.dom.ELEMENT_NODE){
node = node.nextSibling;
}
if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
node = dojo.dom.nextElement(node, tagName);
}
return node; // Element
}
dojo.dom.lastElement = dojo.dom.getLastChildElement = function(/* Element */parentNode, /* string? */tagName){
// summary:
// returns the last child element matching tagName
var node = parentNode.lastChild;
while(node && node.nodeType != dojo.dom.ELEMENT_NODE) {
node = node.previousSibling;
}
if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
node = dojo.dom.prevElement(node, tagName);
}
return node; // Element
}
dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(/* Node */node, /* string? */tagName){
// summary:
// returns the next sibling element matching tagName
if(!node) { return null; }
do {
node = node.nextSibling;
} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
return dojo.dom.nextElement(node, tagName);
}
return node; // Element
}
dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(/* Node */node, /* string? */tagName){
// summary:
// returns the previous sibling element matching tagName
if(!node) { return null; }
if(tagName) { tagName = tagName.toLowerCase(); }
do {
node = node.previousSibling;
} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
return dojo.dom.prevElement(node, tagName);
}
return node; // Element
}
// TODO: hmph
/*this.forEachChildTag = function(node, unaryFunc) {
var child = this.getFirstChildTag(node);
while(child) {
if(unaryFunc(child) == "break") { break; }
child = this.getNextSiblingTag(child);
}
}*/
dojo.dom.moveChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){
// summary:
// Moves children from srcNode to destNode and returns the count of
// children moved; will trim off text nodes if trim == true
var count = 0;
if(trim) {
while(srcNode.hasChildNodes() &&
srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) {
srcNode.removeChild(srcNode.firstChild);
}
while(srcNode.hasChildNodes() &&
srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) {
srcNode.removeChild(srcNode.lastChild);
}
}
while(srcNode.hasChildNodes()){
destNode.appendChild(srcNode.firstChild);
count++;
}
return count; // number
}
dojo.dom.copyChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){
// summary:
// Copies children from srcNde to destNode and returns the count of
// children copied; will trim off text nodes if trim == true
var clonedNode = srcNode.cloneNode(true);
return this.moveChildren(clonedNode, destNode, trim); // number
}
dojo.dom.replaceChildren = function(/*Element*/node, /*Node*/newChild){
// summary:
// Removes all children of node and appends newChild. All the existing
// children will be destroyed.
// FIXME: what if newChild is an array-like object?
var nodes = [];
if(dojo.render.html.ie){
for(var i=0;i<node.childNodes.length;i++){
nodes.push(node.childNodes[i]);
}
}
dojo.dom.removeChildren(node);
node.appendChild(newChild);
for(var i=0;i<nodes.length;i++){
dojo.dom.destroyNode(nodes[i]);
}
}
dojo.dom.removeChildren = function(/*Element*/node){
// summary:
// removes all children from node and returns the count of children removed.
// The children nodes are not destroyed. Be sure to call destroyNode on them
// after they are not used anymore.
var count = node.childNodes.length;
while(node.hasChildNodes()){ dojo.dom.removeNode(node.firstChild); }
return count; // int
}
dojo.dom.replaceNode = function(/*Element*/node, /*Element*/newNode){
// summary:
// replaces node with newNode and returns a reference to the removed node.
// To prevent IE memory leak, call destroyNode on the returned node when
// it is no longer needed.
return node.parentNode.replaceChild(newNode, node); // Node
}
dojo.dom.destroyNode = function(/*Node*/node){
// summary:
// destroy a node (it can not be used any more). For IE, this is the
// right function to call to prevent memory leaks. While for other
// browsers, this is identical to dojo.dom.removeNode
if(node.parentNode){
node = dojo.dom.removeNode(node);
}
if(node.nodeType != 3){ // ingore TEXT_NODE
if(dojo.evalObjPath("dojo.event.browser.clean", false)){
dojo.event.browser.clean(node);
}
if(dojo.render.html.ie){
node.outerHTML=''; //prevent ugly IE mem leak associated with Node.removeChild (ticket #1727)
}
}
}
dojo.dom.removeNode = function(/*Node*/node){
// summary:
// if node has a parent, removes node from parent and returns a
// reference to the removed child.
// To prevent IE memory leak, call destroyNode on the returned node when
// it is no longer needed.
// node:
// the node to remove from its parent.
if(node && node.parentNode){
// return a ref to the removed child
return node.parentNode.removeChild(node); //Node
}
}
dojo.dom.getAncestors = function(/*Node*/node, /*function?*/filterFunction, /*boolean?*/returnFirstHit){
// summary:
// returns all ancestors matching optional filterFunction; will return
// only the first if returnFirstHit
var ancestors = [];
var isFunction = (filterFunction && (filterFunction instanceof Function || typeof filterFunction == "function"));
while(node){
if(!isFunction || filterFunction(node)){
ancestors.push(node);
}
if(returnFirstHit && ancestors.length > 0){
return ancestors[0]; // Node
}
node = node.parentNode;
}
if(returnFirstHit){ return null; }
return ancestors; // array
}
dojo.dom.getAncestorsByTag = function(/*Node*/node, /*String*/tag, /*boolean?*/returnFirstHit){
// summary:
// returns all ancestors matching tag (as tagName), will only return
// first one if returnFirstHit
tag = tag.toLowerCase();
return dojo.dom.getAncestors(node, function(el){
return ((el.tagName)&&(el.tagName.toLowerCase() == tag));
}, returnFirstHit); // Node || array
}
dojo.dom.getFirstAncestorByTag = function(/*Node*/node, /*string*/tag){
// summary:
// Returns first ancestor of node with tag tagName
return dojo.dom.getAncestorsByTag(node, tag, true); // Node
}
dojo.dom.isDescendantOf = function(/* Node */node, /* Node */ancestor, /* boolean? */guaranteeDescendant){
// summary
// Returns boolean if node is a descendant of ancestor
// guaranteeDescendant allows us to be a "true" isDescendantOf function
if(guaranteeDescendant && node) { node = node.parentNode; }
while(node) {
if(node == ancestor){
return true; // boolean
}
node = node.parentNode;
}
return false; // boolean
}
dojo.dom.innerXML = function(/*Node*/node){
// summary:
// Implementation of MS's innerXML function.
if(node.innerXML){
return node.innerXML; // string
}else if (node.xml){
return node.xml; // string
}else if(typeof XMLSerializer != "undefined"){
return (new XMLSerializer()).serializeToString(node); // string
}
}
dojo.dom.createDocument = function(){
// summary:
// cross-browser implementation of creating an XML document object.
var doc = null;
var _document = dojo.doc();
if(!dj_undef("ActiveXObject")){
var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ];
for(var i = 0; i<prefixes.length; i++){
try{
doc = new ActiveXObject(prefixes[i]+".XMLDOM");
}catch(e){ /* squelch */ };
if(doc){ break; }
}
}else if((_document.implementation)&&
(_document.implementation.createDocument)){
doc = _document.implementation.createDocument("", "", null);
}
return doc; // DOMDocument
}
dojo.dom.createDocumentFromText = function(/*string*/str, /*string?*/mimetype){
// summary:
// attempts to create a Document object based on optional mime-type,
// using str as the contents of the document
if(!mimetype){ mimetype = "text/xml"; }
if(!dj_undef("DOMParser")){
var parser = new DOMParser();
return parser.parseFromString(str, mimetype); // DOMDocument
}else if(!dj_undef("ActiveXObject")){
var domDoc = dojo.dom.createDocument();
if(domDoc){
domDoc.async = false;
domDoc.loadXML(str);
return domDoc; // DOMDocument
}else{
dojo.debug("toXml didn't work?");
}
/*
}else if((dojo.render.html.capable)&&(dojo.render.html.safari)){
// FIXME: this doesn't appear to work!
// from: http://web-graphics.com/mtarchive/001606.php
// var xml = '<?xml version="1.0"?>'+str;
var mtype = "text/xml";
var xml = '<?xml version="1.0"?>'+str;
var url = "data:"+mtype+";charset=utf-8,"+encodeURIComponent(xml);
var req = new XMLHttpRequest();
req.open("GET", url, false);
req.overrideMimeType(mtype);
req.send(null);
return req.responseXML;
*/
}else{
var _document = dojo.doc();
if(_document.createElement){
// FIXME: this may change all tags to uppercase!
var tmp = _document.createElement("xml");
tmp.innerHTML = str;
if(_document.implementation && _document.implementation.createDocument){
var xmlDoc = _document.implementation.createDocument("foo", "", null);
for(var i = 0; i < tmp.childNodes.length; i++) {
xmlDoc.importNode(tmp.childNodes.item(i), true);
}
return xmlDoc; // DOMDocument
}
// FIXME: probably not a good idea to have to return an HTML fragment
// FIXME: the tmp.doc.firstChild is as tested from IE, so it may not
// work that way across the board
return ((tmp.document)&&
(tmp.document.firstChild ? tmp.document.firstChild : tmp)); // DOMDocument
}
}
return null;
}
dojo.dom.prependChild = function(/*Element*/node, /*Element*/parent){
// summary:
// prepends node to parent's children nodes
if(parent.firstChild) {
parent.insertBefore(node, parent.firstChild);
} else {
parent.appendChild(node);
}
return true; // boolean
}
dojo.dom.insertBefore = function(/*Node*/node, /*Node*/ref, /*boolean?*/force){
// summary:
// Try to insert node before ref
if( (force != true)&&
(node === ref || node.nextSibling === ref)){ return false; }
var parent = ref.parentNode;
parent.insertBefore(node, ref);
return true; // boolean
}
dojo.dom.insertAfter = function(/*Node*/node, /*Node*/ref, /*boolean?*/force){
// summary:
// Try to insert node after ref
var pn = ref.parentNode;
if(ref == pn.lastChild){
if((force != true)&&(node === ref)){
return false; // boolean
}
pn.appendChild(node);
}else{
return this.insertBefore(node, ref.nextSibling, force); // boolean
}
return true; // boolean
}
dojo.dom.insertAtPosition = function(/*Node*/node, /*Node*/ref, /*string*/position){
// summary:
// attempt to insert node in relation to ref based on position
if((!node)||(!ref)||(!position)){
return false; // boolean
}
switch(position.toLowerCase()){
case "before":
return dojo.dom.insertBefore(node, ref); // boolean
case "after":
return dojo.dom.insertAfter(node, ref); // boolean
case "first":
if(ref.firstChild){
return dojo.dom.insertBefore(node, ref.firstChild); // boolean
}else{
ref.appendChild(node);
return true; // boolean
}
break;
default: // aka: last
ref.appendChild(node);
return true; // boolean
}
}
dojo.dom.insertAtIndex = function(/*Node*/node, /*Element*/containingNode, /*number*/insertionIndex){
// summary:
// insert node into child nodes nodelist of containingNode at
// insertionIndex. insertionIndex should be between 0 and
// the number of the childNodes in containingNode. insertionIndex
// specifys after how many childNodes in containingNode the node
// shall be inserted. If 0 is given, node will be appended to
// containingNode.
var siblingNodes = containingNode.childNodes;
// if there aren't any kids yet, just add it to the beginning
if (!siblingNodes.length || siblingNodes.length == insertionIndex){
containingNode.appendChild(node);
return true; // boolean
}
if(insertionIndex == 0){
return dojo.dom.prependChild(node, containingNode); // boolean
}
// otherwise we need to walk the childNodes
// and find our spot
return dojo.dom.insertAfter(node, siblingNodes[insertionIndex-1]); // boolean
}
dojo.dom.textContent = function(/*Node*/node, /*string*/text){
// summary:
// implementation of the DOM Level 3 attribute; scan node for text
if (arguments.length>1) {
var _document = dojo.doc();
dojo.dom.replaceChildren(node, _document.createTextNode(text));
return text; // string
} else {
if(node.textContent != undefined){ //FF 1.5
return node.textContent; // string
}
var _result = "";
if (node == null) { return _result; }
for (var i = 0; i < node.childNodes.length; i++) {
switch (node.childNodes[i].nodeType) {
case 1: // ELEMENT_NODE
case 5: // ENTITY_REFERENCE_NODE
_result += dojo.dom.textContent(node.childNodes[i]);
break;
case 3: // TEXT_NODE
case 2: // ATTRIBUTE_NODE
case 4: // CDATA_SECTION_NODE
_result += node.childNodes[i].nodeValue;
break;
default:
break;
}
}
return _result; // string
}
}
dojo.dom.hasParent = function(/*Node*/node){
// summary:
// returns whether or not node is a child of another node.
return Boolean(node && node.parentNode && dojo.dom.isNode(node.parentNode)); // boolean
}
/**
* Examples:
*
* myFooNode = <foo />
* isTag(myFooNode, "foo"); // returns "foo"
* isTag(myFooNode, "bar"); // returns ""
* isTag(myFooNode, "FOO"); // returns ""
* isTag(myFooNode, "hey", "foo", "bar"); // returns "foo"
**/
dojo.dom.isTag = function(/* Node */node /* ... */){
// summary:
// determines if node has any of the provided tag names and returns
// the tag name that matches, empty string otherwise.
if(node && node.tagName) {
for(var i=1; i<arguments.length; i++){
if(node.tagName==String(arguments[i])){
return String(arguments[i]); // string
}
}
}
return ""; // string
}
dojo.dom.setAttributeNS = function( /*Element*/elem, /*string*/namespaceURI,
/*string*/attrName, /*string*/attrValue){
// summary:
// implementation of DOM2 setAttributeNS that works cross browser.
if(elem == null || ((elem == undefined)&&(typeof elem == "undefined"))){
dojo.raise("No element given to dojo.dom.setAttributeNS");
}
if(!((elem.setAttributeNS == undefined)&&(typeof elem.setAttributeNS == "undefined"))){ // w3c
elem.setAttributeNS(namespaceURI, attrName, attrValue);
}else{ // IE
// get a root XML document
var ownerDoc = elem.ownerDocument;
var attribute = ownerDoc.createNode(
2, // node type
attrName,
namespaceURI
);
// set value
attribute.nodeValue = attrValue;
// attach to element
elem.setAttributeNode(attribute);
}
}