blob: 2fe223a69b35a044a0e29283fb46e5fefe0a9ab4 [file] [log] [blame]
/*
Copyright (c) 2004-2005, The Dojo Foundation
All Rights Reserved.
Licensed under the Academic Free License version 2.1 or above OR the
modified BSD license. For more information on Dojo licensing, see:
http://dojotoolkit.org/community/licensing.shtml
*/
dojo.provide("dojo.dom");
dojo.require("dojo.lang");
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 = {
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 = dojo.lang.isDomNode = function(wh){
if(typeof Element == "object") {
try {
return wh instanceof Element;
} catch(E) {}
} else {
// best-guess
return wh && !isNaN(wh.nodeType);
}
}
dojo.lang.whatAmI.custom["node"] = dojo.dom.isNode;
dojo.dom.getTagName = function(node){
var tagName = node.tagName;
if(tagName.substr(0,5).toLowerCase()!="dojo:"){
if(tagName.substr(0,4).toLowerCase()=="dojo"){
// FIXME: this assuumes tag names are always lower case
return "dojo:" + tagName.substring(4).toLowerCase();
}
// allow lower-casing
var djt = node.getAttribute("dojoType")||node.getAttribute("dojotype");
if(djt){
return "dojo:"+djt.toLowerCase();
}
if((node.getAttributeNS)&&(node.getAttributeNS(this.dojoml,"type"))){
return "dojo:" + node.getAttributeNS(this.dojoml,"type").toLowerCase();
}
try{
// FIXME: IE really really doesn't like this, so we squelch
// errors for it
djt = node.getAttribute("dojo:type");
}catch(e){ /* FIXME: log? */ }
if(djt){
return "dojo:"+djt.toLowerCase();
}
if((!dj_global["djConfig"])||(!djConfig["ignoreClassNames"])){
// FIXME: should we make this optionally enabled via djConfig?
var classes = node.className||node.getAttribute("class");
// FIXME: following line, without check for existence of classes.indexOf
// breaks firefox 1.5's svg widgets
if((classes)&&(classes.indexOf)&&(classes.indexOf("dojo-") != -1)){
var aclasses = classes.split(" ");
for(var x=0; x<aclasses.length; x++){
if((aclasses[x].length>5)&&(aclasses[x].indexOf("dojo-")>=0)){
return "dojo:"+aclasses[x].substr(5).toLowerCase();
}
}
}
}
}
return tagName.toLowerCase();
}
dojo.dom.getUniqueId = function(){
do {
var id = "dj_unique_" + (++arguments.callee._idIncrement);
}while(document.getElementById(id));
return id;
}
dojo.dom.getUniqueId._idIncrement = 0;
dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(parentNode, 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;
}
dojo.dom.lastElement = dojo.dom.getLastChildElement = function(parentNode, 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;
}
dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(node, 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;
}
dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(node, 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;
}
// 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(srcNode, destNode, trim){
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;
}
dojo.dom.copyChildren = function(srcNode, destNode, trim){
var clonedNode = srcNode.cloneNode(true);
return this.moveChildren(clonedNode, destNode, trim);
}
dojo.dom.removeChildren = function(node){
var count = node.childNodes.length;
while(node.hasChildNodes()){ node.removeChild(node.firstChild); }
return count;
}
dojo.dom.replaceChildren = function(node, newChild){
// FIXME: what if newChild is an array-like object?
dojo.dom.removeChildren(node);
node.appendChild(newChild);
}
dojo.dom.removeNode = function(node){
if(node && node.parentNode){
// return a ref to the removed child
return node.parentNode.removeChild(node);
}
}
dojo.dom.getAncestors = function(node, filterFunction, returnFirstHit) {
var ancestors = [];
var isFunction = dojo.lang.isFunction(filterFunction);
while(node) {
if (!isFunction || filterFunction(node)) {
ancestors.push(node);
}
if (returnFirstHit && ancestors.length > 0) { return ancestors[0]; }
node = node.parentNode;
}
if (returnFirstHit) { return null; }
return ancestors;
}
dojo.dom.getAncestorsByTag = function(node, tag, returnFirstHit) {
tag = tag.toLowerCase();
return dojo.dom.getAncestors(node, function(el){
return ((el.tagName)&&(el.tagName.toLowerCase() == tag));
}, returnFirstHit);
}
dojo.dom.getFirstAncestorByTag = function(node, tag) {
return dojo.dom.getAncestorsByTag(node, tag, true);
}
dojo.dom.isDescendantOf = function(node, ancestor, guaranteeDescendant){
// guaranteeDescendant allows us to be a "true" isDescendantOf function
if(guaranteeDescendant && node) { node = node.parentNode; }
while(node) {
if(node == ancestor){ return true; }
node = node.parentNode;
}
return false;
}
dojo.dom.innerXML = function(node){
if(node.innerXML){
return node.innerXML;
}else if(typeof XMLSerializer != "undefined"){
return (new XMLSerializer()).serializeToString(node);
}
}
dojo.dom.createDocumentFromText = function(str, mimetype){
if(!mimetype) { mimetype = "text/xml"; }
if(typeof DOMParser != "undefined") {
var parser = new DOMParser();
return parser.parseFromString(str, mimetype);
}else if(typeof ActiveXObject != "undefined"){
var domDoc = new ActiveXObject("Microsoft.XMLDOM");
if(domDoc) {
domDoc.async = false;
domDoc.loadXML(str);
return domDoc;
}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 request = new XMLHttpRequest();
request.open("GET", url, false);
request.overrideMimeType(mtype);
request.send(null);
return request.responseXML;
*/
}else 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;
}
// 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;
}
return null;
}
dojo.dom.prependChild = function(node, parent) {
if(parent.firstChild) {
parent.insertBefore(node, parent.firstChild);
} else {
parent.appendChild(node);
}
return true;
}
dojo.dom.insertBefore = function(node, ref, force){
if (force != true &&
(node === ref || node.nextSibling === ref)){ return false; }
var parent = ref.parentNode;
parent.insertBefore(node, ref);
return true;
}
dojo.dom.insertAfter = function(node, ref, force){
var pn = ref.parentNode;
if(ref == pn.lastChild){
if((force != true)&&(node === ref)){
return false;
}
pn.appendChild(node);
}else{
return this.insertBefore(node, ref.nextSibling, force);
}
return true;
}
dojo.dom.insertAtPosition = function(node, ref, position){
if((!node)||(!ref)||(!position)){ return false; }
switch(position.toLowerCase()){
case "before":
return dojo.dom.insertBefore(node, ref);
case "after":
return dojo.dom.insertAfter(node, ref);
case "first":
if(ref.firstChild){
return dojo.dom.insertBefore(node, ref.firstChild);
}else{
ref.appendChild(node);
return true;
}
break;
default: // aka: last
ref.appendChild(node);
return true;
}
}
dojo.dom.insertAtIndex = function(node, containingNode, insertionIndex){
var siblingNodes = containingNode.childNodes;
// if there aren't any kids yet, just add it to the beginning
if (!siblingNodes.length){
containingNode.appendChild(node);
return true;
}
// otherwise we need to walk the childNodes
// and find our spot
var after = null;
for(var i=0; i<siblingNodes.length; i++){
var sibling_index = siblingNodes.item(i)["getAttribute"] ? parseInt(siblingNodes.item(i).getAttribute("dojoinsertionindex")) : -1;
if (sibling_index < insertionIndex){
after = siblingNodes.item(i);
}
}
if (after){
// add it after the node in {after}
return dojo.dom.insertAfter(node, after);
}else{
// add it to the start
return dojo.dom.insertBefore(node, siblingNodes.item(0));
}
}
/**
* implementation of the DOM Level 3 attribute.
*
* @param node The node to scan for text
* @param text Optional, set the text to this value.
*/
dojo.dom.textContent = function(node, text){
if (text) {
dojo.dom.replaceChildren(node, document.createTextNode(text));
return text;
} else {
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;
}
}
dojo.dom.collectionToArray = function(collection){
dojo.deprecated("dojo.dom.collectionToArray", "use dojo.lang.toArray instead");
return dojo.lang.toArray(collection);
}
dojo.dom.hasParent = function(node) {
if(!node || !node.parentNode || (node.parentNode && !node.parentNode.tagName)) {
return false;
}
return true;
}
/**
* Determines if node has any of the provided tag names and
* returns the tag name that matches, empty string otherwise.
*
* 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 /* ... */) {
if(node && node.tagName) {
var arr = dojo.lang.toArray(arguments, 1);
return arr[ dojo.lang.find(node.tagName, arr) ] || "";
}
return "";
}