diff --git a/Makefile b/Makefile index 203de034..33268bfb 100644 --- a/Makefile +++ b/Makefile @@ -94,8 +94,10 @@ d3.layout.js: \ src/start.js \ src/layout/layout.js \ src/layout/chord.js \ + src/layout/force.js \ src/layout/pie.js \ src/layout/stack.js \ + src/layout/treemap.js \ src/end.js d3.geo.js: \ @@ -132,6 +134,9 @@ d3.geom.js: \ src/geom/quadtree.js \ src/end.js +tests: \ + tests/test-format.test + %.min.js: %.js Makefile @rm -f $@ $(JS_COMPILER) --js $< --js_output_file $@ @@ -141,5 +146,12 @@ d3.js d3%.js: Makefile cat $(filter %.js,$^) > $@ @chmod a-w $@ +%.test: %.js %.out d3.js + @/bin/echo -n "test: $* " + @node $< > $*.actual + @diff -U 3 $*.out $*.actual && rm -f $*.actual \ + && echo '\033[1;32mPASS\033[0m' \ + || echo test: $* '\033[1;31mFAIL\033[0m' + clean: rm -f d3*.js diff --git a/d3.js b/d3.js index ef2dbad2..921902e8 100644 --- a/d3.js +++ b/d3.js @@ -1,4 +1,4 @@ -(function(){d3 = {version: "1.4.0"}; // semver +(function(){d3 = {version: "1.5.1"}; // semver if (!Date.now) Date.now = function() { return +new Date(); }; @@ -335,30 +335,45 @@ function d3_dispatch(type) { d3.format = function(specifier) { var match = d3_format_re.exec(specifier), fill = match[1] || " ", - sign = d3_format_signs[match[3]] || d3_format_signs["-"], + sign = match[3] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9]; if (precision) precision = precision.substring(1); - if (zfill) fill = "0"; // TODO align = "="; + if (zfill) { + fill = "0"; // TODO align = "="; + if (comma) width -= Math.floor((width - 1) / 4); + } if (type == "d") precision = "0"; return function(value) { var number = +value, - negative = (number < 0) && (number = -number); + negative = (number < 0) && (number = -number) ? "\u2212" : sign; + + // Return the empty string for floats formatted as ints. if ((type == "d") && (number % 1)) return ""; + + // Convert the input value to the desired precision. if (precision) value = number.toFixed(precision); else value = "" + number; - if (comma) { - var i = value.lastIndexOf("."), - f = i >= 0 ? value.substring(i) : (i = value.length, ""), - t = []; - while (i > 0) t.push(value.substring(i -= 3, i + 3)); - value = t.reverse().join(",") + f; + + // If the fill character is 0, the sign and group is applied after the fill. + if (zfill) { + var length = value.length + negative.length; + if (length < width) value = new Array(width - length + 1).join(fill) + value; + if (comma) value = d3_format_group(value); + value = negative + value; } - var length = (value = sign(negative, value)).length; - if (length < width) value = new Array(width - length + 1).join(fill) + value; + + // Otherwise (e.g., space-filling), the sign and group is applied before. + else { + if (comma) value = d3_format_group(value); + value = negative + value; + var length = value.length; + if (length < width) value = new Array(width - length + 1).join(fill) + value; + } + return value; }; }; @@ -366,11 +381,14 @@ d3.format = function(specifier) { // [[fill]align][sign][#][0][width][,][.precision][type] var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/; -var d3_format_signs = { - "+": function(negative, value) { return (negative ? "\u2212" : "+") + value; }, - " ": function(negative, value) { return (negative ? "\u2212" : " ") + value; }, - "-": function(negative, value) { return negative ? "\u2212" + value : value; } -}; +// Apply comma grouping for thousands. +function d3_format_group(value) { + var i = value.lastIndexOf("."), + f = i >= 0 ? value.substring(i) : (i = value.length, ""), + t = []; + while (i > 0) t.push(value.substring(i -= 3, i + 3)); + return t.reverse().join(",") + f; +} /* * TERMS OF USE - EASING EQUATIONS * diff --git a/d3.layout.js b/d3.layout.js index 2dab21e3..aa6c49ea 100644 --- a/d3.layout.js +++ b/d3.layout.js @@ -150,6 +150,200 @@ d3.layout.chord = function() { return chord; }; +// A rudimentary force layout using Gauss-Seidel. +d3.layout.force = function() { + var force = {}, + event = d3.dispatch("tick"), + size = [1, 1], + alpha = .5, + distance = 30, + interval, + nodes, + links, + distances; + + function tick() { + var n = distances.length, + i, // current index + o, // current link + s, // current source + t, // current target + l, // current distance + x, // x-distance + y; // y-distance + + // gauss-seidel relaxation + for (i = 0; i < n; ++i) { + o = distances[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if (l = Math.sqrt(x * x + y * y)) { + l = alpha / (o.distance * o.distance) * (l - distance * o.distance) / l; + x *= l; + y *= l; + if (s.fixed) { + if (t.fixed) continue; + t.x -= x; + t.y -= y; + } else if (t.fixed) { + s.x += x; + s.y += y; + } else { + s.x += x; + s.y += y; + t.x -= x; + t.y -= y; + } + } + } + + // simulated annealing, basically + if ((alpha *= .99) < 1e-6) force.stop(); + + event.tick.dispatch({type: "tick"}); + } + + force.on = function(type, listener) { + event[type].add(listener); + return force; + }; + + force.nodes = function(x) { + if (!arguments.length) return nodes; + nodes = x; + return force; + }; + + force.links = function(x) { + if (!arguments.length) return links; + links = x; + return force; + }; + + force.size = function(x) { + if (!arguments.length) return size; + size = x; + return force; + }; + + force.distance = function(d) { + if (!arguments.length) return distance; + distance = d; + return force; + }; + + force.start = function() { + var i, + j, + k, + n = nodes.length, + m = links.length, + w = size[0], + h = size[1], + o; + + var paths = []; + for (i = 0; i < n; ++i) { + o = nodes[i]; + o.x = o.x || Math.random() * w; + o.y = o.y || Math.random() * h; + o.fixed = 0; + paths[i] = []; + for (j = 0; j < n; ++j) { + paths[i][j] = Infinity; + } + paths[i][i] = 0; + } + + for (i = 0; i < m; ++i) { + o = links[i]; + paths[o.source][o.target] = 1; + paths[o.target][o.source] = 1; + o.source = nodes[o.source]; + o.target = nodes[o.target]; + } + + // Floyd-Warshall + for (k = 0; k < n; ++k) { + for (i = 0; i < n; ++i) { + for (j = 0; j < n; ++j) { + paths[i][j] = Math.min(paths[i][j], paths[i][k] + paths[k][j]); + } + } + } + + distances = []; + for (i = 0; i < n; ++i) { + for (j = i + 1; j < n; ++j) { + distances.push({ + source: nodes[i], + target: nodes[j], + distance: paths[i][j] * paths[i][j] + }); + } + } + + distances.sort(function(a, b) { + return a.distance - b.distance; + }); + + if (interval) clearInterval(interval); + interval = setInterval(tick, 24); + return force; + }; + + force.resume = function() { + alpha = .1; + if (!interval) interval = setInterval(tick, 24); + return force; + }; + + force.stop = function() { + interval = clearInterval(interval); + return force; + }; + + // use `node.call(force.drag)` to make nodes draggable + force.drag = function() { + var node, element; + + this + .on("mouseover", function(d) { d.fixed = true; }) + .on("mouseout", function(d) { if (d != node) d.fixed = false; }) + .on("mousedown", mousedown); + + d3.select(window) + .on("mousemove", mousemove) + .on("mouseup", mouseup); + + function mousedown(d) { + (node = d).fixed = true; + element = this; + d3.event.preventDefault(); + } + + function mousemove() { + if (!node) return; + var m = d3.svg.mouse(element); + node.x = m[0]; + node.y = m[1]; + force.resume(); // restart annealing + } + + function mouseup() { + if (!node) return; + mousemove(); + node.fixed = false; + node = element = null; + } + + return force; + }; + + return force; +}; d3.layout.pie = function() { var value = Number, sort = null, @@ -407,4 +601,185 @@ function d3_layout_stackMaxIndex(array) { function d3_layout_stackSum(p, d) { return p + d.y; } +// Squarified Treemaps by Mark Bruls, Kees Huizing, and Jarke J. van Wijk +d3.layout.treemap = function() { + var children = d3_layout_treemapChildren, + value = d3_layout_treemapValue, + round = Math.round, + size = [1, 1]; // width, height + + // Recursively compute the node depth and value. + // Also converts the data representation into a standard tree structure. + // Also sorts child nodes by descending value to optimize squarification. + function sum(data, depth, nodes) { + var datas = children.call(treemap, data, depth), + node = {depth: depth, data: data}; + nodes.push(node); + if (datas) { + var i = -1, + n = datas.length, + c = node.children = [], + v = 0, + j = depth + 1; + while (++i < n) { + d = sum(datas[i], j, nodes); + if (d.value > 0) { // ignore NaN, negative, etc. + c.push(d); + v += d.value; + } + } + node.value = v; + } else { + node.value = value.call(treemap, data, depth); + } + if (!depth) scale(node, size[0] * size[1] / node.value); // root + return node; + } + + // Recursively compute the node area based on value & scale. + function scale(node, k) { + var children = node.children; + node.area = node.value * k; + if (children) { + var i = -1, + n = children.length; + while (++i < n) scale(children[i], k); + } + } + + // Recursively arranges the specified node's children into squarified rows. + function squarify(node) { + if (!node.children) return; + var rect = {x: node.x, y: node.y, dx: node.dx, dy: node.dy}, + row = [], + children = node.children.slice().sort(d3_layout_treemapSort), + child, + best = Infinity, // the best row score so far + score, // the current row score + u = Math.min(rect.dx, rect.dy), // initial orientation + n; + row.area = 0; + while ((n = children.length) > 0) { + row.push(child = children[n - 1]); + row.area += child.area; + if ((score = worst(row, u)) <= best) { // continue with this orientation + children.pop(); + best = score; + } else { // abort, and try a different orientation + row.area -= row.pop().area; + position(row, u, rect, false); + u = Math.min(rect.dx, rect.dy); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, u, rect, true); + row.length = row.area = 0; + } + node.children.forEach(squarify); + } + + // Computes the score for the specified row, as the worst aspect ratio. + function worst(row, u) { + var s = row.area, + r, + rmax = 0, + rmin = Infinity, + i = -1, + n = row.length; + while (++i < n) { + r = row[i].area; + if (r < rmin) rmin = r; + if (r > rmax) rmax = r; + } + s *= s; + u *= u; + return Math.max((u * rmax) / s, s / (u * rmin)); + } + + // Positions the specified row of nodes. Modifies `rect`. + function position(row, u, rect, flush) { + var i = -1, + n = row.length, + x = rect.x, + y = rect.y, + v = u ? round(row.area / u) : 0, + o; + if (u == rect.dx) { // horizontal subdivision + if (flush || v > rect.dy) v = rect.dy; // over+underflow + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dy = v; + x += o.dx = round(o.area / v); + } + o.dx += rect.x + rect.dx - x; // rounding error + rect.y += v; + rect.dy -= v; + } else { // vertical subdivision + if (flush || v > rect.dx) v = rect.dx; // over+underflow + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dx = v; + y += o.dy = round(o.area / v); + } + o.dy += rect.y + rect.dy - y; // rounding error + rect.x += v; + rect.dx -= v; + } + } + + function treemap(d) { + var nodes = [], + root = sum(d, 0, nodes); + root.x = 0; + root.y = 0; + root.dx = size[0]; + root.dy = size[1]; + squarify(root); + return nodes; + } + + treemap.children = function(x) { + if (!arguments.length) return children; + children = x; + return treemap; + }; + + treemap.value = function(x) { + if (!arguments.length) return value; + value = x; + return treemap; + }; + + treemap.size = function(x) { + if (!arguments.length) return size; + size = x; + return treemap; + }; + + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + + return treemap; +}; + +function d3_layout_treemapChildren(d) { + return d.children; +} + +function d3_layout_treemapValue(d) { + return d.value; +} + +function d3_layout_treemapSort(a, b) { + return b.area - a.area; +} })() \ No newline at end of file diff --git a/d3.layout.min.js b/d3.layout.min.js index 1dae9954..a10306db 100644 --- a/d3.layout.min.js +++ b/d3.layout.min.js @@ -1,7 +1,14 @@ -(function(){function A(a){return a.reduce(B,0)}function C(a){for(var j=1,f=0,c=a[0].y,i,e=a.length;jc){f=j;c=i}return f}function B(a,j){return a+j.y}d3.layout={};d3.layout.chord=function(){function a(){var g={},l=[],t=d3.range(b),q=[],r,n,w,o,s;c=[];i=[];r=0;for(o=-1;++oe)e=k;i.push(k)}h=0;for(b=j[0];hi){n=h;i=o}return n}function B(c,h){return c+h.y}function D(c){return c.children}function E(c){return c.value}function F(c,h){return h.area-c.area}d3.layout={};d3.layout.chord=function(){function c(){var a={},j=[],f=d3.range(g),s=[],t,k,q,r,u;i=[];o=[];t=0;for(r=-1;++rl)l=p;o.push(p)}m=0;for(g=h[0];m0){t.push(d);k+=d.value}}f.value=k}else f.value=g.call(o,e,b);b||h(f,p[0]*p[1]/f.value);return f}function h(e,b){var a=e.children;e.area=e.value*b;if(a)for(var j=-1,f=a.length;++j0;){a.push(f=j[f-1]);a.area+=f.area;f=t;for(var k=a.area,q=void 0,r=0,u=Infinity,v=-1,w=a.length;++v +r)r=q}k*=k;f*=f;if((f=Math.max(f*r/k,k/(f*u)))<=s){j.pop();s=f}else{a.area-=a.pop().area;i(a,t,b,false);t=Math.min(b.dx,b.dy);a.length=a.area=0;s=Infinity}}if(a.length){i(a,t,b,true);a.length=a.area=0}e.children.forEach(n)}}function i(e,b,a,j){var f=-1,s=e.length,t=a.x,k=a.y,q=b?m(e.area/b):0,r;if(b==a.dx){if(j||q>a.dy)q=a.dy;for(;++fa.dx)q=a.dx;for(;++f360)f-=360;else if(f<0)f+=360;if(f<60)return e+(c-e)*f/60;if(f<180)return c;if(f<240)return e+ -(c-e)*(240-f)/60;return e}var e,c;a%=360;if(a<0)a+=360;b=b<0?0:b>1?1:b;d=d<0?0:d>1?1:d;c=d<=0.5?d*(1+b):d+b-d*b;e=2*d-c;return J(Math.round(g(a+120)*255),Math.round(g(a)*255),Math.round(g(a-120)*255))}function y(a){function b(e){for(var c=[],f,h,i,k,j=0,o=a.length;jg){i[l]=2;return}else{i[l]=1;h.start.dispatch.apply(this,arguments);s=c[l]={};r.active=g;for(u in e)s[u]=e[u].apply(this,arguments)}t=p(q);for(u in e)s[u].call(this,t);if(q==1){i[l]=2;if(r.active== -g){q=r.owner;if(q==g){delete this.__transition__;f&&this.parentNode.removeChild(this)}Y=g;h.end.dispatch.apply(this,arguments);Y=0;r.owner=q}}}});return n}var d={},g=Y||++Ha,e={},c=[],f=false,h=d3.dispatch("start","end"),i=[],k=[],j=[],o,p=d3.ease("cubic-in-out");a.each(function(){(this.__transition__||(this.__transition__={})).owner=g});d.delay=function(m){var n=Infinity,l=-1;if(typeof m=="function")a.each(function(){var q=k[++l]=+m.apply(this,arguments);if(qo)o=l})}else{o=+m;a.each(function(){j[++n]=o})}return d};d.ease=function(m){p=typeof m=="string"?d3.ease(m):m;return d};d.attrTween=function(m,n){function l(r,t){var u=n.call(this,r,t,this.getAttribute(m));return function(s){this.setAttribute(m,u(s))}}function q(r,t){var u=n.call(this,r,t,this.getAttributeNS(m.space,m.local));return function(s){this.setAttributeNS(m.space, -m.local,u(s))}}e["attr."+m]=m.local?q:l;return d};d.attr=function(m,n){return d.attrTween(m,ia(n))};d.styleTween=function(m,n,l){if(arguments.length<3)l=null;e["style."+m]=function(q,r){var t=n.call(this,q,r,window.getComputedStyle(this,null).getPropertyValue(m));return function(u){this.style.setProperty(m,t(u),l)}};return d};d.style=function(m,n,l){if(arguments.length<3)l=null;return d.styleTween(m,ia(n),l)};d.select=function(m){var n;m=X(a.select(m)).ease(p);n=-1;m.delay(function(){return k[++n]}); -n=-1;m.duration(function(){return j[++n]});return m};d.selectAll=function(m){var n;m=X(a.selectAll(m)).ease(p);n=-1;m.delay(function(l,q){return k[q?n:++n]});n=-1;m.duration(function(l,q){return j[q?n:++n]});return m};d.remove=function(){f=true;return d};d.each=function(m,n){h[m].add(n);return d};d.call=ea;return d.delay(0).duration(250)}function ia(a){return typeof a=="function"?function(b,d,g){return d3.interpolate(g,String(a.call(this,b,d)))}:(a=String(a),function(b,d,g){return d3.interpolate(g, -a)})}function Ia(a,b){var d=Date.now(),g=false,e=d+b,c=F;if(isFinite(b)){for(;c;){if(c.callback==a){c.then=d;c.delay=b;g=true}else{var f=c.then+c.delay;if(fd.delay)d.flush=d.callback(a);d=d.next}a=null;for(b=F;b;)b=b.flush?a?a.next=b.next:F=b.next:(a=b).next;a||(K=clearInterval(K))}function La(a){return a.innerRadius} -function Ma(a){return a.outerRadius}function ja(a){return a.startAngle}function ka(a){return a.endAngle}function $(a,b,d,g){var e=[],c=-1,f=b.length,h=typeof d=="function",i=typeof g=="function",k;if(h&&i)for(;++c1){h=b[1];c=a[i];i++;g+="C"+(e[0]+f[0])+","+(e[1]+f[1])+","+(c[0]-h[0])+","+(c[1]-h[1])+","+c[0]+","+c[1];for(e=2;eb?1:0};d3.descending=function(a,b){return ba?1:0};d3.min=function(a,b){var d=0,g=a.length,e=a[0], -c;if(arguments.length==1)for(;++d(c=a[d]))e=c}else for(e=b(a[0]);++d(c=b(a[d])))e=c;return e};d3.max=function(a,b){var d=0,g=a.length,e=a[0],c;if(arguments.length==1)for(;++d=g.length)return f?f.call(d,h):c?h.sort(c):h;for(var k=-1,j=h.length,o=g[i++],p,m,n={};++k= -g.length)return h;var k=[],j=e[i++],o;for(o in h)k.push({key:o,values:b(h[o],i)});j&&k.sort(function(p,m){return j(p.key,m.key)});return k}var d={},g=[],e=[],c,f;d.map=function(h){return a(h,0)};d.entries=function(h){return b(a(h,0),0)};d.key=function(h){g.push(h);return d};d.sortKeys=function(h){e[g.length-1]=h;return d};d.sortValues=function(h){c=h;return d};d.rollup=function(h){f=h;return d};return d};d3.keys=function(a){var b=[],d;for(d in a)b.push(d);return b};d3.values=function(a){var b=[], -d;for(d in a)b.push(a[d]);return b};d3.entries=function(a){var b=[],d;for(d in a)b.push({key:d,value:a[d]});return b};d3.merge=function(a){return Array.prototype.concat.apply([],a)};d3.split=function(a,b){var d=[],g=[],e,c=-1,f=a.length;if(arguments.length<2)b=va;for(;++cb;)g.push(c);else for(;(c=a+d*++e)=0?i.substring(j):(j=i.length,""),p=[];j>0;)p.push(i.substring(j-=3,j+3));i=p.reverse().join(",")+o}k=(i=d(k,i)).length;if(k=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,ra={"+":function(a,b){return(a?"−":"+")+b}," ":function(a,b){return(a?"−":" ")+b},"-":function(a,b){return a?"−"+b:b}},Va=R(2),Wa=R(3),Xa={linear:function(){return xa},poly:R,quad:function(){return Va},cubic:function(){return Wa},sin:function(){return ya}, -exp:function(){return za},circle:function(){return Aa},elastic:function(a,b){var d;if(arguments.length<2)b=0.45;if(arguments.length<1){a=1;d=b/4}else d=b/(2*Math.PI)*Math.asin(1/a);return function(g){return 1+a*Math.pow(2,10*-g)*Math.sin((g-d)*2*Math.PI/b)}},back:function(a){a||(a=1.70158);return function(b){return b*b*((a+1)*b-a)}},bounce:function(){return Ba}},Ya={"in":function(a){return a},out:fa,"in-out":ga,"out-in":function(a){return ga(fa(a))}};d3.ease=function(a){var b=a.indexOf("-"),d=b>= +d)return a;b.push({listener:d,on:true});return a};a.remove=function(d){for(var g=0;g=0?a.substring(b):(b=a.length,""),g=[];b>0;)g.push(a.substring(b-=3,b+3));return g.reverse().join(",")+d}function ga(a){return function(b){return 1- +a(1-b)}}function ha(a){return function(b){return 0.5*(b<0.5?a(2*b):2-a(2-2*b))}}function xa(a){return a}function R(a){return function(b){return Math.pow(b,a)}}function ya(a){return 1-Math.cos(a*Math.PI/2)}function za(a){return a?Math.pow(2,10*(a-1))-0.0010:0}function Aa(a){return 1-Math.sqrt(1-a*a)}function Ba(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375}function J(a,b,d){return{r:a,g:b,b:d,toString:Ca}} +function Ca(){return"#"+S(this.r)+S(this.g)+S(this.b)}function S(a){return a<16?"0"+a.toString(16):a.toString(16)}function T(a,b,d){var g=0,e=0,c=0,f,h;if(f=/([a-z]+)\((.*)\)/i.exec(a)){h=f[2].split(",");switch(f[1]){case "hsl":return d(parseFloat(h[0]),parseFloat(h[1])/100,parseFloat(h[2])/100);case "rgb":return b(U(h[0]),U(h[1]),U(h[2]))}}if(d=G[a])return b(d.r,d.g,d.b);if(a!=null&&a.charAt(0)=="#"){if(a.length==4){g=a.charAt(1);g+=g;e=a.charAt(2);e+=e;c=a.charAt(3);c+=c}else if(a.length==7){g= +a.substring(1,3);e=a.substring(3,5);c=a.substring(5,7)}g=parseInt(g,16);e=parseInt(e,16);c=parseInt(c,16)}return b(g,e,c)}function Da(a,b,d){var g=Math.min(a/=255,b/=255,d/=255),e=Math.max(a,b,d),c=e-g,f=(e+g)/2;if(c){g=f<0.5?c/(e+g):c/(2-e-g);a=a==e?(b-d)/c+(b360)f-=360;else if(f<0)f+=360;if(f<60)return e+(c-e)*f/60;if(f<180)return c;if(f<240)return e+(c-e)*(240-f)/60;return e}var e,c;a%=360;if(a<0)a+=360;b=b<0?0:b>1?1:b;d=d<0?0:d>1?1:d;c=d<=0.5?d*(1+b):d+b-d*b;e=2*d-c;return J(Math.round(g(a+120)*255),Math.round(g(a)*255),Math.round(g(a-120)*255))}function y(a){function b(e){for(var c=[],f,h,i,k,j=0,o=a.length;jg){i[l]=2;return}else{i[l]=1;h.start.dispatch.apply(this,arguments);s=c[l]={};r.active=g; +for(u in e)s[u]=e[u].apply(this,arguments)}t=p(q);for(u in e)s[u].call(this,t);if(q==1){i[l]=2;if(r.active==g){q=r.owner;if(q==g){delete this.__transition__;f&&this.parentNode.removeChild(this)}Y=g;h.end.dispatch.apply(this,arguments);Y=0;r.owner=q}}}});return n}var d={},g=Y||++Ha,e={},c=[],f=false,h=d3.dispatch("start","end"),i=[],k=[],j=[],o,p=d3.ease("cubic-in-out");a.each(function(){(this.__transition__||(this.__transition__={})).owner=g});d.delay=function(m){var n=Infinity,l=-1;if(typeof m== +"function")a.each(function(){var q=k[++l]=+m.apply(this,arguments);if(qo)o=l})}else{o=+m;a.each(function(){j[++n]=o})}return d};d.ease=function(m){p=typeof m=="string"?d3.ease(m):m;return d};d.attrTween=function(m,n){function l(r,t){var u=n.call(this,r,t,this.getAttribute(m));return function(s){this.setAttribute(m, +u(s))}}function q(r,t){var u=n.call(this,r,t,this.getAttributeNS(m.space,m.local));return function(s){this.setAttributeNS(m.space,m.local,u(s))}}e["attr."+m]=m.local?q:l;return d};d.attr=function(m,n){return d.attrTween(m,ja(n))};d.styleTween=function(m,n,l){if(arguments.length<3)l=null;e["style."+m]=function(q,r){var t=n.call(this,q,r,window.getComputedStyle(this,null).getPropertyValue(m));return function(u){this.style.setProperty(m,t(u),l)}};return d};d.style=function(m,n,l){if(arguments.length< +3)l=null;return d.styleTween(m,ja(n),l)};d.select=function(m){var n;m=X(a.select(m)).ease(p);n=-1;m.delay(function(){return k[++n]});n=-1;m.duration(function(){return j[++n]});return m};d.selectAll=function(m){var n;m=X(a.selectAll(m)).ease(p);n=-1;m.delay(function(l,q){return k[q?n:++n]});n=-1;m.duration(function(l,q){return j[q?n:++n]});return m};d.remove=function(){f=true;return d};d.each=function(m,n){h[m].add(n);return d};d.call=ea;return d.delay(0).duration(250)}function ja(a){return typeof a== +"function"?function(b,d,g){return d3.interpolate(g,String(a.call(this,b,d)))}:(a=String(a),function(b,d,g){return d3.interpolate(g,a)})}function Ia(a,b){var d=Date.now(),g=false,e=d+b,c=F;if(isFinite(b)){for(;c;){if(c.callback==a){c.then=d;c.delay=b;g=true}else{var f=c.then+c.delay;if(f +d.delay)d.flush=d.callback(a);d=d.next}a=null;for(b=F;b;)b=b.flush?a?a.next=b.next:F=b.next:(a=b).next;a||(K=clearInterval(K))}function La(a){return a.innerRadius}function Ma(a){return a.outerRadius}function ka(a){return a.startAngle}function la(a){return a.endAngle}function $(a,b,d,g){var e=[],c=-1,f=b.length,h=typeof d=="function",i=typeof g=="function",k;if(h&&i)for(;++c1){h=b[1];c=a[i];i++;g+="C"+ +(e[0]+f[0])+","+(e[1]+f[1])+","+(c[0]-h[0])+","+(c[1]-h[1])+","+c[0]+","+c[1];for(e=2;eb?1:0};d3.descending=function(a,b){return ba?1:0};d3.min=function(a,b){var d=0,g=a.length,e=a[0],c;if(arguments.length==1)for(;++d(c=a[d]))e=c}else for(e=b(a[0]);++d(c=b(a[d])))e=c;return e};d3.max=function(a,b){var d=0,g=a.length,e=a[0],c;if(arguments.length==1)for(;++d=g.length)return f?f.call(d,h):c?h.sort(c): +h;for(var k=-1,j=h.length,o=g[i++],p,m,n={};++k=g.length)return h;var k=[],j=e[i++],o;for(o in h)k.push({key:o,values:b(h[o],i)});j&&k.sort(function(p,m){return j(p.key,m.key)});return k}var d={},g=[],e=[],c,f;d.map=function(h){return a(h,0)};d.entries=function(h){return b(a(h,0),0)};d.key=function(h){g.push(h);return d};d.sortKeys=function(h){e[g.length-1]=h;return d};d.sortValues=function(h){c= +h;return d};d.rollup=function(h){f=h;return d};return d};d3.keys=function(a){var b=[],d;for(d in a)b.push(d);return b};d3.values=function(a){var b=[],d;for(d in a)b.push(a[d]);return b};d3.entries=function(a){var b=[],d;for(d in a)b.push({key:d,value:a[d]});return b};d3.merge=function(a){return Array.prototype.concat.apply([],a)};d3.split=function(a,b){var d=[],g=[],e,c=-1,f=a.length;if(arguments.length<2)b=va;for(;++cb;)g.push(c);else for(;(c=a+d*++e)=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,Va=R(2),Wa=R(3),Xa={linear:function(){return xa},poly:R,quad:function(){return Va},cubic:function(){return Wa},sin:function(){return ya}, +exp:function(){return za},circle:function(){return Aa},elastic:function(a,b){var d;if(arguments.length<2)b=0.45;if(arguments.length<1){a=1;d=b/4}else d=b/(2*Math.PI)*Math.asin(1/a);return function(g){return 1+a*Math.pow(2,10*-g)*Math.sin((g-d)*2*Math.PI/b)}},back:function(a){a||(a=1.70158);return function(b){return b*b*((a+1)*b-a)}},bounce:function(){return Ba}},Ya={"in":function(a){return a},out:ga,"in-out":ha,"out-in":function(a){return ha(ga(a))}};d3.ease=function(a){var b=a.indexOf("-"),d=b>= 0?a.substring(0,b):a;b=b>=0?a.substring(b+1):"in";return Ya[b](Xa[d].apply(null,Array.prototype.slice.call(arguments,1)))};d3.event=null;d3.interpolate=function(a,b){if(typeof b=="number")return d3.interpolateNumber(+a,b);if(typeof b=="string")return b in G||/^(#|rgb\(|hsl\()/.test(b)?d3.interpolateRgb(String(a),b):d3.interpolateString(String(a),b);if(b instanceof Array)return d3.interpolateArray(a,b);return d3.interpolateObject(a,b)};d3.interpolateNumber=function(a,b){b-=a;return function(d){return a+ b*d}};d3.interpolateRound=function(a,b){b-=a;return function(d){return Math.round(a+b*d)}};d3.interpolateString=function(a,b){var d,g,e=0,c=[],f=[],h,i;for(g=0;d=aa.exec(b);++g){d.index&&c.push(b.substring(e,d.index));f.push({i:c.length,x:d[0]});c.push(null);e=aa.lastIndex}e0;j--)f.push(b(h)*j);else{for(;h>1,j=c[k];if(jf)i=k-1;else return k}return i<0?0:i}function d(f){return e[b(f)]}var g=[],e=[],c=[];d.domain=function(f){if(!arguments.length)return g; g=f.filter(function(h){return!isNaN(h)}).sort(d3.ascending);a();return d};d.range=function(f){if(!arguments.length)return e;e=f;a();return d};d.quantiles=function(){return c};return d};d3.scale.quantize=function(){function a(f){return c[Math.max(0,Math.min(e,Math.floor(g*(f-b))))]}var b=0,d=1,g=2,e=1,c=[0,1];a.domain=function(f){if(!arguments.length)return[b,d];b=f[0];d=f[1];g=c.length/(d-b);return a};a.range=function(f){if(!arguments.length)return c;c=f;g=c.length/(d-b);e=c.length-1;return a};return a}; d3.svg={};d3.svg.arc=function(){function a(){var c=b.apply(this,arguments),f=d.apply(this,arguments),h=g.apply(this,arguments)+I,i=e.apply(this,arguments)+I,k=i-h,j=k=db?c?"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+-f+"A"+f+","+f+" 0 1,1 0,"+f+"M0,"+c+"A"+c+","+c+" 0 1,1 0,"+-c+"A"+c+","+c+" 0 1,1 0,"+c+"Z":"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+-f+"A"+f+","+f+" 0 1,1 0,"+f+"Z":c?"M"+f*o+","+f*h+"A"+f+","+f+" 0 "+j+",1 "+f*p+","+ -f*i+"L"+c*p+","+c*i+"A"+c+","+c+" 0 "+j+",0 "+c*o+","+c*h+"Z":"M"+f*o+","+f*h+"A"+f+","+f+" 0 "+j+",1 "+f*p+","+f*i+"L0,0Z"}var b=La,d=Ma,g=ja,e=ka;a.innerRadius=function(c){if(!arguments.length)return b;b=v(c);return a};a.outerRadius=function(c){if(!arguments.length)return d;d=v(c);return a};a.startAngle=function(c){if(!arguments.length)return g;g=v(c);return a};a.endAngle=function(c){if(!arguments.length)return e;e=v(c);return a};a.centroid=function(){var c=(b.apply(this,arguments)+d.apply(this, -arguments))/2,f=(g.apply(this,arguments)+e.apply(this,arguments))/2+I;return[Math.cos(f)*c,Math.sin(f)*c]};return a};var I=-Math.PI/2,db=2*Math.PI-1.0E-6;d3.svg.line=function(){function a(f){return f.length<1?null:"M"+e($(this,f,b,d),c)}var b=la,d=ma,g="linear",e=P[g],c=0.7;a.x=function(f){if(!arguments.length)return b;b=f;return a};a.y=function(f){if(!arguments.length)return d;d=f;return a};a.interpolate=function(f){if(!arguments.length)return g;e=P[g=f];return a};a.tension=function(f){if(!arguments.length)return c; +f*i+"L"+c*p+","+c*i+"A"+c+","+c+" 0 "+j+",0 "+c*o+","+c*h+"Z":"M"+f*o+","+f*h+"A"+f+","+f+" 0 "+j+",1 "+f*p+","+f*i+"L0,0Z"}var b=La,d=Ma,g=ka,e=la;a.innerRadius=function(c){if(!arguments.length)return b;b=v(c);return a};a.outerRadius=function(c){if(!arguments.length)return d;d=v(c);return a};a.startAngle=function(c){if(!arguments.length)return g;g=v(c);return a};a.endAngle=function(c){if(!arguments.length)return e;e=v(c);return a};a.centroid=function(){var c=(b.apply(this,arguments)+d.apply(this, +arguments))/2,f=(g.apply(this,arguments)+e.apply(this,arguments))/2+I;return[Math.cos(f)*c,Math.sin(f)*c]};return a};var I=-Math.PI/2,db=2*Math.PI-1.0E-6;d3.svg.line=function(){function a(f){return f.length<1?null:"M"+e($(this,f,b,d),c)}var b=ma,d=na,g="linear",e=P[g],c=0.7;a.x=function(f){if(!arguments.length)return b;b=f;return a};a.y=function(f){if(!arguments.length)return d;d=f;return a};a.interpolate=function(f){if(!arguments.length)return g;e=P[g=f];return a};a.tension=function(f){if(!arguments.length)return c; c=f;return a};return a};var P={linear:H,basis:function(a){if(a.length<3)return H(a);var b=[],d=1,g=a.length,e=a[0],c=e[0],f=e[1],h=[c,c,c,(e=a[1])[0]],i=[f,f,f,e[1]];b.push(c,",",f);for(L(b,h,i);++dClustered Network - + + + - +
+ diff --git a/examples/treemap/treemap.js b/examples/treemap/treemap.js new file mode 100644 index 00000000..6cddf281 --- /dev/null +++ b/examples/treemap/treemap.js @@ -0,0 +1,26 @@ +var w = 960, + h = 500, + color = d3.scale.category20c(); + +var treemap = d3.layout.treemap() + .size([w, h]) + .children(function(d) { return typeof d.value == "object" && d3.entries(d.value); }) + .value(function(d) { return d.value; }); + +var div = d3.select("#chart").append("div") + .style("position", "relative") + .style("width", w + "px") + .style("height", h + "px"); + +d3.json("flare.json", function(json) { + div.data(d3.entries(json)).selectAll("div") + .data(treemap) + .enter().append("div") + .attr("class", "cell") + .style("background", function(d) { return d.children ? color(d.data.key) : null; }) + .style("left", function(d) { return d.x + "px"; }) + .style("top", function(d) { return d.y + "px"; }) + .style("width", function(d) { return d.dx - 1 + "px"; }) + .style("height", function(d) { return d.dy - 1 + "px"; }) + .text(function(d) { return d.children ? null : d.data.key; }); +}); diff --git a/lib/env-js/LICENSE b/lib/env-js/LICENSE new file mode 100644 index 00000000..8ec2b0bc --- /dev/null +++ b/lib/env-js/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2009 John Resig, http://jquery.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/env-js/bin/envjs b/lib/env-js/bin/envjs new file mode 100755 index 00000000..7c4bfa7c --- /dev/null +++ b/lib/env-js/bin/envjs @@ -0,0 +1,39 @@ +#!/bin/sh +# Usage: envjs file.js [file2.js ...] +########################################################################### +ENVJS_PLATFORM='node' + +# first arguments ($1) +# may be a platform or is a file and the default platorm is used +if [ -n "$1" ]; then + if [ -n "$1" ]; then ENVJS_PLATFORM="$1"; shift; fi +fi + +# Run envjs with the given platform +########################################################################### +case "$ENVJS_PLATFORM" in + + "node") + node envjs/node.js $@ + ;; + "rhino") + java -Xmx512M -jar rhino/js.jar -opt -1 envjs/rhino.js $@ + ;; + "rhino-debug") + java -cp rhino/js.jar org.mozilla.javascript.tools.debugger.Main envjs/rhino.js $@ + ;; + "spyd") + python envjs/spydermonkey.py envjs/spydermonkey.js $@ + ;; + "rubyracer") + ruby -rrubygems envjs/rubyracer.rb envjs/rubyracer.js $@ + ;; + "johnson") + ruby -rrubygems envjs/johnson.rb envjs/johnson.js $@ + ;; + *) + # platform default means $1 was actually a file + node envjs/node.js $ENVJS_PLATFORM $@ + ;; +esac + diff --git a/lib/env-js/envjs/console.js b/lib/env-js/envjs/console.js new file mode 100644 index 00000000..27cf325b --- /dev/null +++ b/lib/env-js/envjs/console.js @@ -0,0 +1,278 @@ + +/** + * @author envjs team + * @Console + */ + +var Envjs = Envjs || require('./platform/core').Envjs; + +/* + * Envjs console.1.3.pre03 + * Pure JavaScript Browser Environment + * By John Resig and the Envjs Team + * Copyright 2008-2010 John Resig, under the MIT License + */ + +//CLOSURE_START +(function(){ + + + + +/** + * @author envjs team + * borrowed 99%-ish with love from firebug-lite + */ + +//leaked globally on purpose; +try{ + console.log(); +}catch(e){ + + +function escapeHTML(value) +{ + return value; +} + +function objectToString(object) +{ + try + { + return object+""; + } + catch (exc) + { + return null; + } +} + +// ******************************************************************************************** + +function appendText(object, html) +{ + html.push(escapeHTML(objectToString(object))); +} + +function appendNull(object, html) +{ + html.push(escapeHTML(objectToString(object))); +} + +function appendString(object, html) +{ + html.push(escapeHTML(objectToString(object))); +} + +function appendInteger(object, html) +{ + html.push(escapeHTML(objectToString(object))); +} + +function appendFloat(object, html) +{ + html.push(escapeHTML(objectToString(object))); +} + +function appendFunction(object, html) +{ + var reName = /function ?(.*?)\(/; + var m = reName.exec(objectToString(object)); + var name = m ? m[1] : "function"; + html.push(escapeHTML(name)); +} + +function appendObjectFormatted(object, html) +{ + var text = objectToString(object); + var reObject = /\[object (.*?)\]/; + + var m = reObject.exec(text); + html.push( m ? m[1] : text); +} + +function appendSelector(object, html) +{ + + html.push(escapeHTML(object.nodeName.toLowerCase())); + if (object.id) { + html.push(escapeHTML(object.id)); + } + if (object.className) { + html.push(escapeHTML(object.className)); + } +} + +function appendNode(node, html) +{ + if (node.nodeType == 1) + { + html.push( node.nodeName.toLowerCase()); + + for (var i = 0; i < node.attributes.length; ++i) + { + var attr = node.attributes[i]; + if (!attr.specified) { + continue; + } + + html.push( attr.nodeName.toLowerCase(),escapeHTML(attr.nodeValue)); + } + + if (node.firstChild) + { + for (var child = node.firstChild; child; child = child.nextSibling) { + appendNode(child, html); + } + + html.push( node.nodeName.toLowerCase()); + } + } + else if (node.nodeType === 3) + { + html.push(escapeHTML(node.nodeValue)); + } +} + +function appendObject(object, html) +{ + try + { + if (object === undefined) { + appendNull("undefined", html); + } else if (object === null) { + appendNull("null", html); + } else if (typeof object == "string") { + appendString(object, html); + } else if (typeof object == "number") { + appendInteger(object, html); + } else if (typeof object == "function") { + appendFunction(object, html); + } else if (object.nodeType == 1) { + appendSelector(object, html); + } else if (typeof object == "object") { + appendObjectFormatted(object, html); + } else { + appendText(object, html); + } + } + catch (exc) + { + } +} + + +function parseFormat(format) +{ + var parts = []; + + var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/; + var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat}; + + for (var m = reg.exec(format); m; m = reg.exec(format)) + { + var type = m[8] ? m[8] : m[5]; + var appender = type in appenderMap ? appenderMap[type] : appendObject; + var precision = m[3] ? parseInt(m[3], 10) : (m[4] == "." ? -1 : 0); + + parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1)); + parts.push({appender: appender, precision: precision}); + + format = format.substr(m.index+m[0].length); + } + + parts.push(format); + + return parts; +} + + + +function logFormatted(objects, className) +{ + var html = [], + i= 0, + object; + + var format = objects[0]; + var objIndex = 0; + + if (typeof(format) != "string") + { + format = ""; + objIndex = -1; + } + + var parts = parseFormat(format); + for (i = 0; i < parts.length; ++i) + { + var part = parts[i]; + if (part && typeof(part) == "object") + { + object = objects[++objIndex]; + part.appender(object, html); + } + else { + appendText(part, html); + } + } + + for (i = objIndex+1; i < objects.length; ++i) + { + appendText(" ", html); + + object = objects[i]; + if (typeof(object) == "string") { + appendText(object, html); + } else { + appendObject(object, html); + } + } + + Envjs.log(html.join(' ')); +} + + +Console = function(module){ + var $level, + $logger, + $null = function(){}; + $logger = { + log: function(level){ + logFormatted(arguments, ""); + }, + debug: function(level){ + logFormatted(arguments, "DEBUG"); + }, + info: function(level){ + logFormatted(arguments, "INFO"); + }, + warn: function(level){ + logFormatted(arguments, "WARN"); + }, + error: function(level){ + logFormatted(arguments, "ERROR"); + }, + trace: function(){ + Envjs.trace(); + } + }; + + return $logger; +}; + +console = new Console(); +exports.console = console; + +} + + + +/** + * @author john resig & the envjs team + * @uri http://www.envjs.com/ + * @copyright 2008-2010 + * @license MIT + */ +//CLOSURE_END +}()); diff --git a/lib/env-js/envjs/css.js b/lib/env-js/envjs/css.js new file mode 100644 index 00000000..5db72eef --- /dev/null +++ b/lib/env-js/envjs/css.js @@ -0,0 +1,695 @@ + +/** + * DOM Style Level 2 + +Leaked globally + +var CSS2Properties, + CSSRule, + CSSStyleRule, + CSSImportRule, + CSSMediaRule, + CSSFontFaceRule, + CSSPageRule, + CSSRuleList, + CSSStyleSheet, + StyleSheet, + StyleSheetList; + +*/ + +var Envjs = Envjs || require('./platform/core').Envjs, + Document = require('./dom').Document, + HTMLElement = require('./html').HTMLElement; +/* + * Envjs css.1.3.pre03 + * Pure JavaScript Browser Environment + * By John Resig and the Envjs Team + * Copyright 2008-2010 John Resig, under the MIT License + */ + +//CLOSURE_START +(function(){ + + + + + +/** + * @author john resig + */ +// Helper method for extending one object with another. +function __extend__(a,b) { + for ( var i in b ) { + if(b.hasOwnProperty(i)){ + var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i); + if ( g || s ) { + if ( g ) { a.__defineGetter__(i, g); } + if ( s ) { a.__defineSetter__(i, s); } + } else { + a[i] = b[i]; + } + } + } + return a; +} + +/** + * @author john resig + */ +//from jQuery +function __setArray__( target, array ) { + // Resetting the length to 0, then using the native Array push + // is a super-fast way to populate an object with array-like properties + target.length = 0; + Array.prototype.push.apply( target, array ); +} + +/** + * @author ariel flesler + * http://flesler.blogspot.com/2008/11/fast-trim-function-for-javascript.html + * @param {Object} str + */ +function __trim__( str ){ + return (str || "").replace( /^\s+|\s+$/g, "" ); +} + + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.Document').debug('available'); +}); + +/** + * Interface DocumentStyle (introduced in DOM Level 2) + * http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/stylesheets.html#StyleSheets-StyleSheet-DocumentStyle + * + * interface DocumentStyle { + * readonly attribute StyleSheetList styleSheets; + * }; + * + */ + +__extend__(Document.prototype, { + get styleSheets() { + if (! this._styleSheets) { + this._styleSheets = new StyleSheetList(); + } + return this._styleSheets; + } +}); + +}(/*Envjs.DOM.Document*/)); +/* + * CSS2Properties - DOM Level 2 CSS + * Renamed to CSSStyleDeclaration?? + */ + +var __toCamelCase__ = function(name) { + if (name) { + return name.replace(/\-(\w)/g, function(all, letter) { + return letter.toUpperCase(); + }); + } + return name; +}; + +var __toDashed__ = function(camelCaseName) { + if (camelCaseName) { + return camelCaseName.replace(/[A-Z]/g, function(all) { + return '-' + all.toLowerCase(); + }); + } + return camelCaseName; +}; + +var __cssTextToStyles__, + __supportedStyles__; + + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.CSS.CSS2Properties').debug('available'); +}); + +exports.CSS2Properties = CSS2Properties = function(element){ + //console.log('css2properties %s', __cssproperties__++); + this.styleIndex = __supportedStyles__;//non-standard + this.type = element.tagName;//non-standard + __setArray__(this, []); + __cssTextToStyles__(this, element.cssText || ''); +}; +__extend__(CSS2Properties.prototype, { + get cssText() { + var i, css = []; + for (i = 0; i < this.length; ++i) { + css.push(this[i] + ': ' + this.getPropertyValue(this[i]) + ';'); + } + return css.join(' '); + }, + set cssText(cssText) { + __cssTextToStyles__(this, cssText); + }, + getPropertyCSSValue: function(name) { + //? + }, + getPropertyPriority: function() { + + }, + getPropertyValue: function(name) { + var index, cname = __toCamelCase__(name); + if (cname in this.styleIndex) { + return this[cname]; + } else { + index = Array.prototype.indexOf.apply(this, [name]); + if (index > -1) { + return this[index]; + } + } + return null; + }, + item: function(index) { + return this[index]; + }, + removeProperty: function(name) { + this.styleIndex[name] = null; + name = __toDashed__(name); + var index = Array.prototype.indexOf.apply(this, [name]); + if (index > -1) { + Array.prototype.splice.apply(this, [1,index]); + } + }, + setProperty: function(name, value, priority) { + var nval; + name = __toCamelCase__(name); + if (value !== undefined && name in this.styleIndex) { + // NOTE: parseFloat('300px') ==> 300 no + // NOTE: Number('300px') ==> Nan yes + nval = Number(value); + this.styleIndex[name] = isNaN(nval) ? value : nval; + name = __toDashed__(name); + if (Array.prototype.indexOf.apply(this, [name]) === -1 ){ + Array.prototype.push.apply(this,[name]); + } + } + }, + toString: function() { + return '[object CSS2Properties]'; + } +}); + + + +__cssTextToStyles__ = function(css2props, cssText) { + //console.log('__cssTextToStyles__ %s %s', css2props, cssText); + var i, style, styles = cssText.split(';'); + for (i = 0; i < styles.length; ++i) { + style = styles[i].split(':'); + if (style.length === 2) { + css2props.setProperty( + style[0].replace(' ', '', 'g'), + style[1].replace(' ', '', 'g') + ); + } + } +}; + +//Obviously these arent all supported but by commenting out various +//sections this provides a single location to configure what is +//exposed as supported. +__supportedStyles__ = { + azimuth: null, + background: null, + backgroundAttachment: null, + backgroundColor: 'rgb(0,0,0)', + backgroundImage: null, + backgroundPosition: null, + backgroundRepeat: null, + border: null, + borderBottom: null, + borderBottomColor: null, + borderBottomStyle: null, + borderBottomWidth: null, + borderCollapse: null, + borderColor: null, + borderLeft: null, + borderLeftColor: null, + borderLeftStyle: null, + borderLeftWidth: null, + borderRight: null, + borderRightColor: null, + borderRightStyle: null, + borderRightWidth: null, + borderSpacing: null, + borderStyle: null, + borderTop: null, + borderTopColor: null, + borderTopStyle: null, + borderTopWidth: null, + borderWidth: null, + bottom: null, + captionSide: null, + clear: null, + clip: null, + color: null, + content: null, + counterIncrement: null, + counterReset: null, + cssFloat: null, + cue: null, + cueAfter: null, + cueBefore: null, + cursor: null, + direction: 'ltr', + display: null, + elevation: null, + emptyCells: null, + font: null, + fontFamily: null, + fontSize: '1em', + fontSizeAdjust: null, + fontStretch: null, + fontStyle: null, + fontVariant: null, + fontWeight: null, + height: '', + left: null, + letterSpacing: null, + lineHeight: null, + listStyle: null, + listStyleImage: null, + listStylePosition: null, + listStyleType: null, + margin: null, + marginBottom: '0px', + marginLeft: '0px', + marginRight: '0px', + marginTop: '0px', + markerOffset: null, + marks: null, + maxHeight: null, + maxWidth: null, + minHeight: null, + minWidth: null, + opacity: 1, + orphans: null, + outline: null, + outlineColor: null, + outlineOffset: null, + outlineStyle: null, + outlineWidth: null, + overflow: null, + overflowX: null, + overflowY: null, + padding: null, + paddingBottom: '0px', + paddingLeft: '0px', + paddingRight: '0px', + paddingTop: '0px', + page: null, + pageBreakAfter: null, + pageBreakBefore: null, + pageBreakInside: null, + pause: null, + pauseAfter: null, + pauseBefore: null, + pitch: null, + pitchRange: null, + position: null, + quotes: null, + richness: null, + right: null, + size: null, + speak: null, + speakHeader: null, + speakNumeral: null, + speakPunctuation: null, + speechRate: null, + stress: null, + tableLayout: null, + textAlign: null, + textDecoration: null, + textIndent: null, + textShadow: null, + textTransform: null, + top: null, + unicodeBidi: null, + verticalAlign: null, + visibility: '', + voiceFamily: null, + volume: null, + whiteSpace: null, + widows: null, + width: '1px', + wordSpacing: null, + zIndex: 1 +}; + +var __displayMap__ = { + DIV : 'block', + P : 'block', + A : 'inline', + CODE : 'inline', + PRE : 'block', + SPAN : 'inline', + TABLE : 'table', + THEAD : 'table-header-group', + TBODY : 'table-row-group', + TR : 'table-row', + TH : 'table-cell', + TD : 'table-cell', + UL : 'block', + LI : 'list-item' +}; + +var __addStyleAccessor__ = function(name){ + if (name === 'width' || name === 'height') { + CSS2Properties.prototype.__defineGetter__(name, function() { + if (this.display === 'none'){ + return '0px'; + } + return this.styleIndex[name]; + }); + } else if (name === 'display') { + //display will be set to a tagName specific value if '' + CSS2Properties.prototype.__defineGetter__(name, function() { + var val = this.styleIndex[name]; + val = val ? val :__displayMap__[this.type]; + return val; + }); + } else { + CSS2Properties.prototype.__defineGetter__(name, function() { + return this.styleIndex[name]; + }); + } + CSS2Properties.prototype.__defineSetter__(name, function(value) { + this.setProperty(name, value); + }); +}; + +for (var style in __supportedStyles__) { + if (__supportedStyles__.hasOwnProperty(style)) { + __addStyleAccessor__(style); + } +} + +}(/*Envjs.CSS.CSS2Properties*/)); + + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.CSS.CSSRule').debug('available'); +}); + +/* + * CSSRule - DOM Level 2 + */ +exports.CSSRule = CSSRule = function(options) { + + var $style, + $selectorText = options.selectorText ? options.selectorText : ''; + $style = new CSS2Properties({ + cssText: options.cssText ? options.cssText : null + }); + + return __extend__(this, { + get style(){ + return $style; + }, + get selectorText(){ + return $selectorText; + }, + set selectorText(selectorText){ + $selectorText = selectorText; + }, + toString : function(){ + return "[object CSSRule]"; + } + }); +}; + +CSSRule.STYLE_RULE = 1; +CSSRule.IMPORT_RULE = 3; +CSSRule.MEDIA_RULE = 4; +CSSRule.FONT_FACE_RULE = 5; +CSSRule.PAGE_RULE = 6; +//CSSRule.NAMESPACE_RULE = 10; + + +CSSStyleRule = function() { + +}; + +CSSImportRule = function() { + +}; + +CSSMediaRule = function() { + +}; + +CSSFontFaceRule = function() { + +}; + +CSSPageRule = function() { + +}; + + +CSSRuleList = function(data) { + this.length = 0; + __setArray__(this, data); +}; + +__extend__(CSSRuleList.prototype, { + item : function(index) { + if ((index >= 0) && (index < this.length)) { + // bounds check + return this[index]; + } + return null; + }, + toString: function() { + return '[object CSSRuleList]'; + } +}); + +}(/*Envjs.CSS.CSSRuleList*/)); + + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.CSS.CSSStyleSheet').debug('available'); +}); + +/** + * StyleSheet + * http://dev.w3.org/csswg/cssom/#stylesheet + * + * interface StyleSheet { + * readonly attribute DOMString type; + * readonly attribute DOMString href; + * readonly attribute Node ownerNode; + * readonly attribute StyleSheet parentStyleSheet; + * readonly attribute DOMString title; + * [PutForwards=mediaText] readonly attribute MediaList media; + * attribute boolean disabled; + * }; + */ +StyleSheet = function() {}; + +/* + * CSSStyleSheet + * http://dev.w3.org/csswg/cssom/#cssstylesheet + * + * interface CSSStyleSheet : StyleSheet { + * readonly attribute CSSRule ownerRule; + * readonly attribute CSSRuleList cssRules; + * unsigned long insertRule(DOMString rule, unsigned long index); + * void deleteRule(unsigned long index); + * }; + */ +exports.CSSStyleSheets = CSSStyleSheet = function(options){ + var $cssRules, + $disabled = options.disabled ? options.disabled : false, + $href = options.href ? options.href : null, + $parentStyleSheet = options.parentStyleSheet ? options.parentStyleSheet : null, + $title = options.title ? options.title : "", + $type = "text/css"; + + function parseStyleSheet(text){ + //$debug("parsing css"); + //this is pretty ugly, but text is the entire text of a stylesheet + var cssRules = []; + if (!text) { + text = ''; + } + text = __trim__(text.replace(/\/\*(\r|\n|.)*\*\//g,"")); + // TODO: @import + var blocks = text.split("}"); + blocks.pop(); + var i, j, len = blocks.length; + var definition_block, properties, selectors; + for (i=0; i= 0) && (index < this.length)) { + // bounds check + return this[index]; + } + return null; + }, + toString: function() { + return '[object StyleSheetList]'; + } +}); + +}(/*Envjs.CSS.CSSStyleSheet*/)); +/** + * This extends HTMLElement to handle CSS-specific interfaces. + * + * More work / research would be needed to extend just (DOM) Element + * for xml use and additional changes for just HTMLElement. + */ + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.HTML.HTMLElement').debug('available'); +}); + + +/** + * Replace or add the getter for 'style' + * + * This could be wrapped in a closure + */ +var $css2properties = [{}]; + +__extend__(HTMLElement.prototype, { + get style(){ + if ( !this.css2uuid ) { + this.css2uuid = $css2properties.length; + $css2properties[this.css2uuid] = new CSS2Properties(this); + } + return $css2properties[this.css2uuid]; + } +}); + +/** + * Change for how 'setAttribute("style", ...)' works + * + * We are truly adding functionality to HtmlElement.setAttribute, not + * replacing it. So we need to save the old one first, call it, then + * do our stuff. If we need to do more hacks like this, HTMLElement + * (or regular Element) needs to have a hooks array or dispatch table + * for global changes. + * + * This could be wrapped in a closure if desired. + */ +var updateCss2Props = function(elem, values) { + //console.log('__updateCss2Props__ %s %s', elem, values); + if ( !elem.css2uuid ) { + elem.css2uuid = $css2properties.length; + $css2properties[elem.css2uuid] = new CSS2Properties(elem); + } + __cssTextToStyles__($css2properties[elem.css2uuid], values); +}; + +var origSetAttribute = HTMLElement.prototype.setAttribute; + +HTMLElement.prototype.setAttribute = function(name, value) { + //console.log("CSS set attribute: " + name + ", " + value); + origSetAttribute.apply(this, arguments); + if (name === "style") { + updateCss2Props(this, value); + } +}; + +var origGetAttribute = HTMLElement.prototype.getAttribute; + +HTMLElement.prototype.getAttribute = function(name) { + //console.log("CSS set attribute: " + name + ", " + value); + var style; + if (name === "style") { + style = this.style.cssText; + return style===""?null:style; + }else{ + return origGetAttribute.apply(this, arguments); + } +}; + +}(/*Envjs.HTML.HTMLElement*/)); + +/** + * @author john resig & the envjs team + * @uri http://www.envjs.com/ + * @copyright 2008-2010 + * @license MIT + */ +//CLOSURE_END +}()); diff --git a/lib/env-js/envjs/dom.js b/lib/env-js/envjs/dom.js new file mode 100644 index 00000000..1ba64d7d --- /dev/null +++ b/lib/env-js/envjs/dom.js @@ -0,0 +1,8971 @@ +/* + * Envjs dom.1.3.pre03 + * Pure JavaScript Browser Environment + * By John Resig and the Envjs Team + * Copyright 2008-2010 John Resig, under the MIT License + * + * Parts of the implementation were originally written by:\ + * and Jon van Noort (jon@webarcana.com.au) \ + * and David Joham (djoham@yahoo.com)",\ + * and Scott Severtson + * + * This file simply provides the global definitions we need to \ + * be able to correctly implement to core browser DOM interfaces." + +The following are leaked globally intentionally + +var Attr, + CDATASection, + CharacterData, + Comment, + Document, + DocumentFragment, + DocumentType, + DOMException, + DOMImplementation, + Element, + Entity, + EntityReference, + NamedNodeMap, + Namespace, + Node, + NodeList, + Notation, + ProcessingInstruction, + Text, + Range, + XMLSerializer, + DOMParser, + XPathResult, + XPathExpression; + +*/ + +var Envjs = Envjs || require('./platform/core').Envjs, + After = After || require('./platform/core').After; + +/* + * Envjs dom.1.3.pre03 + * Pure JavaScript Browser Environment + * By John Resig and the Envjs Team + * Copyright 2008-2010 John Resig, under the MIT License + */ + +//CLOSURE_START +(function(){ + + + + + +/** + * @author john resig + */ +// Helper method for extending one object with another. +function __extend__(a,b) { + for ( var i in b ) { + if(b.hasOwnProperty(i)){ + var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i); + if ( g || s ) { + if ( g ) { a.__defineGetter__(i, g); } + if ( s ) { a.__defineSetter__(i, s); } + } else { + a[i] = b[i]; + } + } + } + return a; +} + +/** + * @author john resig + */ +//from jQuery +function __setArray__( target, array ) { + // Resetting the length to 0, then using the native Array push + // is a super-fast way to populate an object with array-like properties + target.length = 0; + Array.prototype.push.apply( target, array ); +} +var __findItemIndex__, + __insertBefore__, + __replaceChild__, + __removeChild__, + __appendChild__, + __addToIndexes__, + __removeFromIndexes__, + __cloneNodes__; + +//see namednodemap for these implementations +var __addToNamedIndexes__, + __removeFromNamedIndexes__; + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.NodeList').debug('available'); +}); +/** + * @class NodeList - + * provides the abstraction of an ordered collection of nodes + * + * @param ownerDocument : Document - the ownerDocument + * @param parentNode : Node - the node that the NodeList is attached to (or null) + */ +exports.NodeList = NodeList = function(ownerDocument, parentNode) { + this.parentNode = parentNode; + this.ownerDocument = ownerDocument; + this._readonly = false; +}; +NodeList.prototype = []; +__extend__(NodeList.prototype, { + item : function(index) { + var ret = null; + if ((index >= 0) && (index < this.length)) { + // bounds check + ret = this[index]; + } + // if the index is out of bounds, default value null is returned + return ret; + }, + get xml() { + var ret = "", + j; + + // create string containing the concatenation of the string values of each child + for (j=0; j < this.length; j++) { + if(this[j] !== null){ + if(this[j].nodeType == Node.TEXT_NODE && j>0 && + this[j-1].nodeType == Node.TEXT_NODE){ + //add a single space between adjacent text nodes + ret += " "+this[j].xml; + }else{ + ret += this[j].xml; + } + } + } + return ret; + }, + toArray: function () { + return this; + }, + toString: function(){ + return "[object NodeList]"; + } +}); + + +/** + * @method __findItemIndex__ + * find the item index of the node + * @author Jon van Noort (jon@webarcana.com.au) + * @param node : Node + * @return : int + */ +__findItemIndex__ = function (nodelist, node) { + // if node is not found, default value -1 is returned + // return ret; + return nodelist.indexOf(node); +}; + +/** + * @method __insertBefore__ + * insert the specified Node into the NodeList before the specified index + * Used by Node.insertBefore(). Note: Node.insertBefore() is responsible + * for Node Pointer surgery __insertBefore__ simply modifies the internal + * data structure (Array). + * @param newChild : Node - the Node to be inserted + * @param refChildIndex : int - the array index to insert the Node before + */ +__insertBefore__ = function(nodelist, newChild, refChildIndex) { + if ((refChildIndex >= 0) && (refChildIndex <= nodelist.length)) { + // bounds check + if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { + // node is a DocumentFragment + // append the children of DocumentFragment + Array.prototype.splice.apply(nodelist, + [refChildIndex, 0].concat(newChild.childNodes.toArray())); + } + else { + // append the newChild + Array.prototype.splice.apply(nodelist,[refChildIndex, 0, newChild]); + } + } +}; + +/** + * @method __replaceChild__ + * replace the specified Node in the NodeList at the specified index + * Used by Node.replaceChild(). Note: Node.replaceChild() is responsible + * for Node Pointer surgery __replaceChild__ simply modifies the internal + * data structure (Array). + * + * @param newChild : Node - the Node to be inserted + * @param refChildIndex : int - the array index to hold the Node + */ +__replaceChild__ = function(nodelist, newChild, refChildIndex) { + var ret = null; + + // bounds check + if ((refChildIndex >= 0) && (refChildIndex < nodelist.length)) { + // preserve old child for return + ret = nodelist[refChildIndex]; + + if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { + // node is a DocumentFragment + // get array containing children prior to refChild + Array.prototype.splice.apply(nodelist, + [refChildIndex, 1].concat(newChild.childNodes.toArray())); + } + else { + // simply replace node in array (links between Nodes are + // made at higher level) + nodelist[refChildIndex] = newChild; + } + } + // return replaced node + return ret; +}; + +/** + * @method __removeChild__ + * remove the specified Node in the NodeList at the specified index + * Used by Node.removeChild(). Note: Node.removeChild() is responsible + * for Node Pointer surgery __removeChild__ simply modifies the internal + * data structure (Array). + * @param refChildIndex : int - the array index holding the Node to be removed + */ +__removeChild__ = function(nodelist, refChildIndex) { + var ret = null; + + if (refChildIndex > -1) { + // found it! + // return removed node + ret = nodelist[refChildIndex]; + // rebuild array without removed child + Array.prototype.splice.apply(nodelist,[refChildIndex, 1]); + __removeFromIndexes__(ret, nodelist.parentNode); + } + // return removed node + return ret; +}; + +/** + * @method __appendChild__ + * append the specified Node to the NodeList. Used by Node.appendChild(). + * Note: Node.appendChild() is responsible for Node Pointer surgery + * __appendChild__ simply modifies the internal data structure (Array). + * @param newChild : Node - the Node to be inserted + */ +__appendChild__ = function(nodelist, newChild) { + log.debug('Appending child %s to nodelist %s', newChild.nodeName, nodelist.length); + var i; + if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { + // newChild is a DocumentFragment + // append the children of DocumentFragment + Array.prototype.push.apply(nodelist, newChild.childNodes.toArray() ); + for(i=0;i< newChild.childNodes.length;i++){ + __addToIndexes__(newChild.childNodes[i], nodelist.parentNode); + } + } else { + // simply add node to array (links between Nodes are made at higher level) + Array.prototype.push.apply(nodelist, [newChild]); + __addToIndexes__(newChild, nodelist.parentNode); + } + +}; + +__addToIndexes__ = function(node, ancestor){ + var indexes, index, normalizedName, i, j, descendingIndex, offset, sibling, children, id, name; + if(node.nodeType == Node.ELEMENT_NODE){ + log.debug('updating node indexes for node %s ancestor %s', node.tagName, ancestor.nodeName); + //now we need to walk up all ancestors updating nodelist indexes + normalizedName = (node.tagName+'').toLowerCase(); + + //if the node doesnt have a parentNode yet then it has been imported + //into the document, but it is just now being appended. This means we + //need to merge the nodes indexes into the ancestors (which will become + //the parentNode just after this operation completes) + if(!node.parentNode){ + indexes = node._indexes_; + for(name in indexes){ + //this is the index of all descendants of the ancestor with the given tagname + if(!ancestor._indexes_.hasOwnProperty(name) && name != normalizedName ){ + + ancestor._indexes_[name] = []; + for(j=0;j %s', node.tagName, ancestor.tagName||'document', offset); + + } + } + } + //now we basically need to crawl up the ancestor chain, merging indexes + //using some smarts + while(ancestor){ + //these are all the indexes already built on the ancestor + indexes = ancestor._indexes_; + if(!(normalizedName in indexes)){ + //make sure we have an index for this particular tagname + indexes[normalizedName] = + new NodeList(node.ownerDocument, ancestor); + } + + offset = 1; + //this is the index of all descendants with the given tagname + index = indexes[normalizedName]; + children = ancestor.childNodes; + for(i=0;i %s', node.tagName, ancestor.tagName||'document', offset); + + + offset = 0; + //this is the index of all descendants with the given tagname, so simply follow + //the same procedure as above but use the '*' index + index = indexes['*']; + children = ancestor.childNodes; + for(i=0;i %s', node.tagName, offset); + + //handle input type elements and their ancestor form elements + //So far we dont bother with maintaining document order for this index + if('FORM' == ancestor.nodeName){ + switch (node.nodeName) { + case 'BUTTON': + case 'FIELDSET': + case 'INPUT': + case 'KEYGEN': + case 'OBJECT': + case 'OUTPUT': + case 'SELECT': + case 'TEXTAREA': + if(!indexes.hasOwnProperty('$elements')){ + //make sure we have an index for the form.elements + indexes.$elements = + new NodeList(node.ownerDocument, ancestor); + } + Array.prototype.push.apply(indexes.$elements, [node]); + name = node.getAttribute('name'); + if( name && !ancestor[name] ){ + //
-1){ + offset = node._indexes_[normalizedName]; + offset = offset?offset.length:0; + length = 1+offset; + //console.log('removing %s[%s] from index %s -> %s', node.tagName, i, ancestor.tagName, index.toArray()); + Array.prototype.splice.apply(index, [i,length]); + } + + index = indexes['*']; + i = Array.prototype.indexOf.apply(index, [node]); + if(i>-1){ + offset = node._indexes_['*']; + offset = offset?offset.length:0; + length = 1+offset; + //console.log('removing %s from index * -> %s', node.tagName, index.toArray()); + Array.prototype.splice.apply(index, [i,length]); + } + + //handle input type elements and their ancestor form elements + //So far we dont bother with maintaining document order for this index + if('FORM' == ancestor.nodeName){ + switch (node.nodeName) { + case 'BUTTON': + case 'FIELDSET': + case 'INPUT': + case 'KEYGEN': + case 'OBJECT': + case 'OUTPUT': + case 'SELECT': + case 'TEXTAREA': + doc = node.ownerDocument; + if(!indexes.hasOwnProperty('$elements')){ + //make sure we have an index for the form.elements + indexes.$elements = + new NodeList(node.ownerDocument, ancestor); + } + offset = Array.prototype.indexOf.apply(doc._indexes_.$elements, [node]); + if(index > -1){ + Array.prototype.splice.apply(doc._indexes_.$elements,[offset,1]); + } + name = node.getAttribute('name'); + if( name && ancestor[name] == node ){ + //1? + this.nodeName.split(':')[0]: + null; + }, + set prefix(value){ + if(value === null){ + this.nodeName = this.localName; + }else{ + this.nodeName = value+':'+this.localName; + } + }, + hasAttributes : function() { + return this.attributes.length ? + true : + false ; + }, + get textContent(){ + return __recursivelyGatherText__(this); + }, + set textContent(newText){ + log.debug('setText %s', newText); + while(this.firstChild){ + this.removeChild( this.firstChild ); + } + var text = this.ownerDocument.createTextNode(newText); + this.appendChild(text); + }, + insertBefore : function(newChild, refChild) { + log.debug('insert %s Before %s', newChild.nodeName, refChild.nodeName); + var prevNode; + + if(!newChild){ + return newChild; + } + if(!refChild){ + this.appendChild(newChild); + return this.newChild; + } + + // test for exceptions + if (__ownerDocument__(this).implementation.errorChecking) { + // throw Exception if Node is readonly + if (this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // throw Exception if newChild was not created by this Document + if (__ownerDocument__(this) != __ownerDocument__(newChild)) { + throw(new DOMException(DOMException.WRONG_DOCUMENT_ERR)); + } + + // throw Exception if the node is an ancestor + if (__isAncestor__(this, newChild)) { + throw(new DOMException(DOMException.HIERARCHY_REQUEST_ERR)); + } + } + + // if refChild is specified, insert before it + if (refChild) { + // find index of refChild + var itemIndex = __findItemIndex__(this.childNodes, refChild); + // throw Exception if there is no child node with this id + if (__ownerDocument__(this).implementation.errorChecking && (itemIndex < 0)) { + throw(new DOMException(DOMException.NOT_FOUND_ERR)); + } + + // if the newChild is already in the tree, + var newChildParent = newChild.parentNode; + if (newChildParent) { + // remove it + newChildParent.removeChild(newChild); + } + + // insert newChild into childNodes + __insertBefore__(this.childNodes, newChild, itemIndex); + + // do node pointer surgery + prevNode = refChild.previousSibling; + + // handle DocumentFragment + if (newChild.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { + if (newChild.childNodes.length > 0) { + // set the parentNode of DocumentFragment's children + for (var ind = 0; ind < newChild.childNodes.length; ind++) { + newChild.childNodes[ind].parentNode = this; + } + + // link refChild to last child of DocumentFragment + refChild.previousSibling = newChild.childNodes[newChild.childNodes.length-1]; + } + }else { + // set the parentNode of the newChild + newChild.parentNode = this; + // link refChild to newChild + refChild.previousSibling = newChild; + } + + }else { + // otherwise, append to end + prevNode = this.lastChild; + this.appendChild(newChild); + } + + if (newChild.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { + // do node pointer surgery for DocumentFragment + if (newChild.childNodes.length > 0) { + if (prevNode) { + prevNode.nextSibling = newChild.childNodes[0]; + }else { + // this is the first child in the list + this.firstChild = newChild.childNodes[0]; + } + newChild.childNodes[0].previousSibling = prevNode; + newChild.childNodes[newChild.childNodes.length-1].nextSibling = refChild; + } + }else { + // do node pointer surgery for newChild + if (prevNode) { + prevNode.nextSibling = newChild; + }else { + // this is the first child in the list + this.firstChild = newChild; + } + newChild.previousSibling = prevNode; + newChild.nextSibling = refChild; + } + + return newChild; + }, + replaceChild : function(newChild, oldChild) { + + log.debug('replaceChild %s with %s', oldChild.nodeName, newChild.nodeName); + var ret = null; + + if(!newChild || !oldChild ){ + return oldChild; + } + + // test for exceptions + if (__ownerDocument__(this).implementation.errorChecking) { + // throw Exception if Node is readonly + if (this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // throw Exception if newChild was not created by this Document + if (__ownerDocument__(this) != __ownerDocument__(newChild)) { + throw(new DOMException(DOMException.WRONG_DOCUMENT_ERR)); + } + + // throw Exception if the node is an ancestor + if (__isAncestor__(this, newChild)) { + throw(new DOMException(DOMException.HIERARCHY_REQUEST_ERR)); + } + } + + // get index of oldChild + var index = __findItemIndex__(this.childNodes, oldChild); + + // throw Exception if there is no child node with this id + if (__ownerDocument__(this).implementation.errorChecking && (index < 0)) { + throw(new DOMException(DOMException.NOT_FOUND_ERR)); + } + + // if the newChild is already in the tree, + var newChildParent = newChild.parentNode; + if (newChildParent) { + // remove it + newChildParent.removeChild(newChild); + } + + // add newChild to childNodes + ret = __replaceChild__(this.childNodes,newChild, index); + + + if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { + // do node pointer surgery for Document Fragment + if (newChild.childNodes.length > 0) { + for (var ind = 0; ind < newChild.childNodes.length; ind++) { + newChild.childNodes[ind].parentNode = this; + } + + if (oldChild.previousSibling) { + oldChild.previousSibling.nextSibling = newChild.childNodes[0]; + } else { + this.firstChild = newChild.childNodes[0]; + } + + if (oldChild.nextSibling) { + oldChild.nextSibling.previousSibling = newChild; + } else { + this.lastChild = newChild.childNodes[newChild.childNodes.length-1]; + } + + newChild.childNodes[0].previousSibling = oldChild.previousSibling; + newChild.childNodes[newChild.childNodes.length-1].nextSibling = oldChild.nextSibling; + } + } else { + // do node pointer surgery for newChild + newChild.parentNode = this; + + if (oldChild.previousSibling) { + oldChild.previousSibling.nextSibling = newChild; + }else{ + this.firstChild = newChild; + } + if (oldChild.nextSibling) { + oldChild.nextSibling.previousSibling = newChild; + }else{ + this.lastChild = newChild; + } + newChild.previousSibling = oldChild.previousSibling; + newChild.nextSibling = oldChild.nextSibling; + } + + return ret; + }, + removeChild : function(oldChild) { + log.debug('removeChild %s from %s', oldChild.nodeName, this.nodeName); + if(!oldChild){ + return null; + } + // throw Exception if NamedNodeMap is readonly + if (__ownerDocument__(this).implementation.errorChecking && + (this._readonly || oldChild._readonly)) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // get index of oldChild + var itemIndex = __findItemIndex__(this.childNodes, oldChild); + + // throw Exception if there is no child node with this id + if (__ownerDocument__(this).implementation.errorChecking && (itemIndex < 0)) { + throw(new DOMException(DOMException.NOT_FOUND_ERR)); + } + + // remove oldChild from childNodes + __removeChild__(this.childNodes, itemIndex); + + // do node pointer surgery + oldChild.parentNode = null; + + if (oldChild.previousSibling) { + oldChild.previousSibling.nextSibling = oldChild.nextSibling; + }else { + this.firstChild = oldChild.nextSibling; + } + if (oldChild.nextSibling) { + oldChild.nextSibling.previousSibling = oldChild.previousSibling; + }else { + this.lastChild = oldChild.previousSibling; + } + + oldChild.previousSibling = null; + oldChild.nextSibling = null; + + return oldChild; + }, + appendChild : function(newChild) { + log.debug('appendChild %s to %s', newChild.nodeName, this.nodeName); + if(!newChild){ + return null; + } + // test for exceptions + if (__ownerDocument__(this).implementation.errorChecking) { + // throw Exception if Node is readonly + if (this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // throw Exception if arg was not created by this Document + if (__ownerDocument__(this) != __ownerDocument__(this)) { + throw(new DOMException(DOMException.WRONG_DOCUMENT_ERR)); + } + + // throw Exception if the node is an ancestor + if (__isAncestor__(this, newChild)) { + throw(new DOMException(DOMException.HIERARCHY_REQUEST_ERR)); + } + } + + // if the newChild is already in the tree, + var newChildParent = newChild.parentNode; + if (newChildParent) { + // remove it + //console.debug('removing node %s', newChild); + newChildParent.removeChild(newChild); + } + + // add newChild to childNodes + __appendChild__(this.childNodes, newChild); + + if (newChild.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { + // do node pointer surgery for DocumentFragment + if (newChild.childNodes.length > 0) { + for (var ind = 0; ind < newChild.childNodes.length; ind++) { + newChild.childNodes[ind].parentNode = this; + } + + if (this.lastChild) { + this.lastChild.nextSibling = newChild.childNodes[0]; + newChild.childNodes[0].previousSibling = this.lastChild; + this.lastChild = newChild.childNodes[newChild.childNodes.length-1]; + } else { + this.lastChild = newChild.childNodes[newChild.childNodes.length-1]; + this.firstChild = newChild.childNodes[0]; + } + } + } else { + // do node pointer surgery for newChild + if (this.lastChild) { + this.lastChild.nextSibling = newChild; + newChild.previousSibling = this.lastChild; + this.lastChild = newChild; + } else { + this.lastChild = newChild; + this.firstChild = newChild; + } + } + newChild.parentNode = this; + return newChild; + }, + hasChildNodes : function() { + return (this.childNodes.length > 0); + }, + cloneNode: function(deep) { + log.debug('cloneNode %s', deep); + // use importNode to clone this Node + //do not throw any exceptions + try { + return __ownerDocument__(this).importNode(this, deep); + } catch (e) { + //there shouldn't be any exceptions, but if there are, return null + // may want to warn: $debug("could not clone node: "+e.code); + return null; + } + }, + normalize : function() { + log.debug('normalize'); + var i; + var inode; + var nodesToRemove = new NodeList(); + + if (this.nodeType == Node.ELEMENT_NODE || this.nodeType == Node.DOCUMENT_NODE) { + var adjacentTextNode = null; + + // loop through all childNodes + for(i = 0; i < this.childNodes.length; i++) { + inode = this.childNodes.item(i); + + if (inode.nodeType == Node.TEXT_NODE) { + // this node is a text node + if (inode.length < 1) { + // this text node is empty + // add this node to the list of nodes to be remove + __appendChild__(nodesToRemove, inode); + }else { + if (adjacentTextNode) { + // previous node was also text + adjacentTextNode.appendData(inode.data); + // merge the data in adjacent text nodes + // add this node to the list of nodes to be removed + __appendChild__(nodesToRemove, inode); + } else { + // remember this node for next cycle + adjacentTextNode = inode; + } + } + } else { + // (soon to be) previous node is not a text node + adjacentTextNode = null; + // normalize non Text childNodes + inode.normalize(); + } + } + + // remove redundant Text Nodes + for(i = 0; i < nodesToRemove.length; i++) { + inode = nodesToRemove.item(i); + inode.parentNode.removeChild(inode); + } + } + }, + isSupported : function(feature, version) { + // use Implementation.hasFeature to determine if this feature is supported + return __ownerDocument__(this).implementation.hasFeature(feature, version); + }, + getElementsByTagName : function(tagname) { + // delegate to _getElementsByTagNameRecursive + // recurse childNodes + log.debug('getElementsByTagName %s',tagname); + var normalizedName = (tagname+'').toLowerCase(); + if(!this._indexes_[normalizedName]){ + this._indexes_[normalizedName] = new NodeList(__ownerDocument__(this)); + } + return this._indexes_[normalizedName]; + }, + getElementsByTagNameNS : function(namespaceURI, localName) { + // delegate to _getElementsByTagNameNSRecursive + log.debug('getElementsByTagNameNS %s %s',namespaceURI, localName); + var nodelist = new NodeList(__ownerDocument__(this)); + for (var i = 0; i < this.childNodes.length; i++) { + __getElementsByTagNameNSRecursive__( + this.childNodes.item(i), + namespaceURI, + localName, + nodelist + ); + } + return nodelist; + }, + importNode : function(importedNode, deep) { + log.debug('importNode %s %s', importedNode.nodeName, deep); + var i, + importNode; + + //there is no need to perform namespace checks since everything has already gone through them + //in order to have gotten into the DOM in the first place. The following line + //turns namespace checking off in ._isValidNamespace + __ownerDocument__(this).importing = true; + + if (importedNode.nodeType == Node.ELEMENT_NODE) { + if (!__ownerDocument__(this).implementation.namespaceAware) { + // create a local Element (with the name of the importedNode) + importNode = __ownerDocument__(this).createElement(importedNode.tagName); + + // create attributes matching those of the importedNode + for(i = 0; i < importedNode.attributes.length; i++) { + importNode.setAttribute(importedNode.attributes.item(i).name, importedNode.attributes.item(i).value); + } + } else { + // create a local Element (with the name & namespaceURI of the importedNode) + importNode = __ownerDocument__(this).createElementNS(importedNode.namespaceURI, importedNode.nodeName); + + // create attributes matching those of the importedNode + for(i = 0; i < importedNode.attributes.length; i++) { + importNode.setAttributeNS(importedNode.attributes.item(i).namespaceURI, + importedNode.attributes.item(i).name, importedNode.attributes.item(i).value); + } + + // create namespace definitions matching those of the importedNode + for(i = 0; i < importedNode._namespaces.length; i++) { + importNode._namespaces[i] = __ownerDocument__(this).createNamespace(importedNode._namespaces.item(i).localName); + importNode._namespaces[i].value = importedNode._namespaces.item(i).value; + } + } + } else if (importedNode.nodeType == Node.ATTRIBUTE_NODE) { + if (!__ownerDocument__(this).implementation.namespaceAware) { + // create a local Attribute (with the name of the importedAttribute) + importNode = __ownerDocument__(this).createAttribute(importedNode.name); + } else { + // create a local Attribute (with the name & namespaceURI of the importedAttribute) + importNode = __ownerDocument__(this).createAttributeNS(importedNode.namespaceURI, importedNode.nodeName); + + // create namespace definitions matching those of the importedAttribute + for(i = 0; i < importedNode._namespaces.length; i++) { + importNode._namespaces[i] = __ownerDocument__(this).createNamespace(importedNode._namespaces.item(i).localName); + importNode._namespaces[i].value = importedNode._namespaces.item(i).value; + } + } + + // set the value of the local Attribute to match that of the importedAttribute + importNode.value = importedNode.value; + + } else if (importedNode.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { + // create a local DocumentFragment + importNode = __ownerDocument__(this).createDocumentFragment(); + } else if (importedNode.nodeType == Node.NAMESPACE_NODE) { + // create a local NamespaceNode (with the same name & value as the importedNode) + importNode = __ownerDocument__(this).createNamespace(importedNode.nodeName); + importNode.value = importedNode.value; + } else if (importedNode.nodeType == Node.TEXT_NODE) { + // create a local TextNode (with the same data as the importedNode) + importNode = __ownerDocument__(this).createTextNode(importedNode.data); + } else if (importedNode.nodeType == Node.CDATA_SECTION_NODE) { + // create a local CDATANode (with the same data as the importedNode) + importNode = __ownerDocument__(this).createCDATASection(importedNode.data); + } else if (importedNode.nodeType == Node.PROCESSING_INSTRUCTION_NODE) { + // create a local ProcessingInstruction (with the same target & data as the importedNode) + importNode = __ownerDocument__(this).createProcessingInstruction(importedNode.target, importedNode.data); + } else if (importedNode.nodeType == Node.COMMENT_NODE) { + // create a local Comment (with the same data as the importedNode) + importNode = __ownerDocument__(this).createComment(importedNode.data); + } else { // throw Exception if nodeType is not supported + throw(new DOMException(DOMException.NOT_SUPPORTED_ERR)); + } + + if (deep) { + // recurse childNodes + for(i = 0; i < importedNode.childNodes.length; i++) { + importNode.appendChild(__ownerDocument__(this).importNode(importedNode.childNodes.item(i), true)); + } + } + + //reset importing + __ownerDocument__(this).importing = false; + return importNode; + + }, + contains : function(node){ + log.debug("this %s contains %s ?", this.nodeName, node.nodeName); + while(node && node != this ){ + node = node.parentNode; + } + return !!node; + }, + compareDocumentPosition : function(b){ + log.debug("compareDocumentPosition of this %s to %s", this.nodeName, b.nodeName); + var i, + length, + a = this, + parent, + aparents, + bparents; + //handle a couple simpler case first + if(a === b) { + return Node.DOCUMENT_POSITION_EQUAL; + } + if(a.ownerDocument !== b.ownerDocument) { + return Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC| + Node.DOCUMENT_POSITION_FOLLOWING| + Node.DOCUMENT_POSITION_DISCONNECTED; + } + if(a.parentNode === b.parentNode){ + length = a.parentNode.childNodes.length; + for(i=0;i aparents.length){ + return Node.DOCUMENT_POSITION_FOLLOWING; + }else if(bparents.length < aparents.length){ + return Node.DOCUMENT_POSITION_PRECEDING; + }else{ + //common ancestor diverge point + if (i === 0) { + return Node.DOCUMENT_POSITION_FOLLOWING; + } else { + parent = aparents[i-1]; + } + return parent.compareDocumentPosition(bparents.pop()); + } + } + } + + return Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC| + Node.DOCUMENT_POSITION_DISCONNECTED; + + }, + toString : function() { + return '[object Node]'; + } + +}); + + +}(/*Envjs.DOM.Node*/)); + + +/** + * @method __getElementsByTagNameRecursive__ - implements getElementsByTagName() + * @param elem : Element - The element which are checking and then recursing into + * @param tagname : string - The name of the tag to match on. The special value "*" matches all tags + * @param nodeList : NodeList - The accumulating list of matching nodes + * + * @return : NodeList + */ +__getElementsByTagNameRecursive__ = function (elem, tagname, nodeList) { + + if (elem.nodeType == Node.ELEMENT_NODE || elem.nodeType == Node.DOCUMENT_NODE) { + + if(elem.nodeType !== Node.DOCUMENT_NODE && + ((elem.nodeName.toUpperCase() == tagname.toUpperCase()) || + (tagname == "*")) ){ + // add matching node to nodeList + __appendChild__(nodeList, elem); + } + + // recurse childNodes + for(var i = 0; i < elem.childNodes.length; i++) { + nodeList = __getElementsByTagNameRecursive__(elem.childNodes.item(i), tagname, nodeList); + } + } + + return nodeList; +}; + +/** + * @method __getElementsByTagNameNSRecursive__ + * implements getElementsByTagName() + * + * @param elem : Element - The element which are checking and then recursing into + * @param namespaceURI : string - the namespace URI of the required node + * @param localName : string - the local name of the required node + * @param nodeList : NodeList - The accumulating list of matching nodes + * + * @return : NodeList + */ +__getElementsByTagNameNSRecursive__ = function(elem, namespaceURI, localName, nodeList) { + if (elem.nodeType == Node.ELEMENT_NODE || elem.nodeType == Node.DOCUMENT_NODE) { + + if (((elem.namespaceURI == namespaceURI) || (namespaceURI == "*")) && + ((elem.localName == localName) || (localName == "*"))) { + // add matching node to nodeList + __appendChild__(nodeList, elem); + } + + // recurse childNodes + for(var i = 0; i < elem.childNodes.length; i++) { + nodeList = __getElementsByTagNameNSRecursive__( + elem.childNodes.item(i), namespaceURI, localName, nodeList); + } + } + + return nodeList; +}; + +/** + * @method __isAncestor__ - returns true if node is ancestor of target + * @param target : Node - The node we are using as context + * @param node : Node - The candidate ancestor node + * @return : boolean + */ +__isAncestor__ = function(target, node) { + // if this node matches, return true, + // otherwise recurse up (if there is a parentNode) + return ((target == node) || ((target.parentNode) && (__isAncestor__(target.parentNode, node)))); +}; + + + +__recursivelyGatherText__ = function(aNode) { + var accumulateText = "", + idx, + node; + for (idx=0;idx < aNode.childNodes.length;idx++){ + node = aNode.childNodes.item(idx); + if(node.nodeType == Node.TEXT_NODE){ + accumulateText += node.data; + }else{ + accumulateText += __recursivelyGatherText__(node); + } + } + return accumulateText; +}; + +/** + * function __escapeXML__ + * @param str : string - The string to be escaped + * @return : string - The escaped string + */ +var escAmpRegEx = /&(?!(amp;|lt;|gt;|quot|apos;))/g; +var escLtRegEx = //g; +var quotRegEx = /"/g; +var aposRegEx = /'/g; +__escapeXML__ = function(str) { + str = str.replace(escAmpRegEx, "&"). + replace(escLtRegEx, "<"). + replace(escGtRegEx, ">"). + replace(quotRegEx, """). + replace(aposRegEx, "'"); + + return str; +}; + +/** + * function __unescapeXML__ + * @param str : string - The string to be unescaped + * @return : string - The unescaped string + */ +var unescAmpRegEx = /&/g; +var unescLtRegEx = /</g; +var unescGtRegEx = />/g; +var unquotRegEx = /"/g; +var unaposRegEx = /'/g; +__unescapeXML__ = function(str) { + str = str.replace(unescAmpRegEx, "&"). + replace(unescLtRegEx, "<"). + replace(unescGtRegEx, ">"). + replace(unquotRegEx, "\""). + replace(unaposRegEx, "'"); + + return str; +}; + +var __findNamedItemIndex__, + __findNamedItemNSIndex__, + __hasAttribute__, + __hasAttributeNS__, + __cloneNamedNodes__; +//see nodelist for these declarations +/*var __addToNamedIndexes__, + __removeFromNamedIndexes__;*/ + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.NamedNodeMap').debug('available'); +}); +/** + * @class NamedNodeMap - + * used to represent collections of nodes that can be accessed by name + * typically a set of Element attributes + * + * @extends NodeList - + * note W3C spec says that this is not the case, but we need an item() + * method identical to NodeList's, so why not? + * @param ownerDocument : Document - the ownerDocument + * @param parentNode : Node - the node that the NamedNodeMap is attached to (or null) + */ +exports.NamedNodeMap = NamedNodeMap = function(ownerDocument, parentNode) { + NodeList.apply(this, arguments); +}; +NamedNodeMap.prototype = new NodeList(); +__extend__(NamedNodeMap.prototype, { + add: function(name) { + this[this.length] = name; + }, + getNamedItem: function(name) { + var ret = null; + log.debug('getNamedItem %s', name); + // test that Named Node exists + var itemIndex = __findNamedItemIndex__(this, name); + + if (itemIndex > -1) { + //console.log('found it!'); + ret = this[itemIndex]; + } + // if node is not found, default value null is returned + return ret; + }, + setNamedItem: function(arg) { + var doc = __ownerDocument__(this); + log.debug('setNamedItem %s', arg.name); + // test for exceptions + if (doc.implementation.errorChecking) { + // throw Exception if arg was not created by this Document + if (this.ownerDocument != arg.ownerDocument) { + throw (new DOMException(DOMException.WRONG_DOCUMENT_ERR)); + } + + // throw Exception if DOMNamedNodeMap is readonly + if (this._readonly || (this.parentNode && this.parentNode._readonly)) { + throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // throw Exception if arg is already an attribute of another Element object + if (arg.ownerElement && (arg.ownerElement != this.parentNode)) { + throw (new DOMException(DOMException.INUSE_ATTRIBUTE_ERR)); + } + } + + // console.log('setNamedItem __findNamedItemIndex__ '); + // get item index + var itemIndex = __findNamedItemIndex__(this, arg.name); + var ret = null; + + //console.log('setNamedItem __findNamedItemIndex__ %s', itemIndex); + if (itemIndex > -1) { + // found it! + ret = this[itemIndex]; + // use existing Attribute + // throw Exception if DOMAttr is readonly + if (doc.implementation.errorChecking && ret._readonly) { + throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } else { + this[itemIndex] = arg; + // over-write existing NamedNode + this[arg.name.toLowerCase()] = arg; + } + } else { + // add new NamedNode + //console.log('setNamedItem add new named node map (by index)'); + Array.prototype.push.apply(this, [arg]); + this[arg.name] = arg; + + } + + arg.ownerElement = this.parentNode; + // update ownerElement + // return old node or new node + + //add to named node indexes on the document + __addToNamedIndexes__(arg.name, arg.value, arg.ownerElement); + + return ret; + }, + removeNamedItem: function(name) { + var ret = null, doc = __ownerDocument__(this); + // test for exceptions + // throw Exception if NamedNodeMap is readonly + if (doc.implementation.errorChecking && + (this._readonly || (this.parentNode && this.parentNode._readonly))) { + throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // get item index + var itemIndex = __findNamedItemIndex__(this, name); + + // throw Exception if there is no node named name in this map + if (doc.implementation.errorChecking && (itemIndex < 0)) { + throw (new DOMException(DOMException.NOT_FOUND_ERR)); + } + + // get Node + var oldNode = this[itemIndex]; + //this[oldNode.name] = undefined; + // throw Exception if Node is readonly + if (doc.implementation.errorChecking && oldNode._readonly) { + throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + //remove from named node indexes on the document + __removeFromNamedIndexes__(name, oldNode.value, oldNode.ownerElement); + + // return removed node + return __removeChild__(this, itemIndex); + }, + getNamedItemNS: function(namespaceURI, localName) { + var ret = null; + + // test that Named Node exists + var itemIndex = __findNamedItemNSIndex__(this, namespaceURI, localName); + + if (itemIndex > -1) { + // found it! return NamedNode + ret = this[itemIndex]; + } + // if node is not found, default value null is returned + return ret; + }, + setNamedItemNS: function(arg) { + log.debug('setNamedItemNS %s %s', arg.namespaceURI, arg.localName); + // test for exceptions + if (__ownerDocument__(this).implementation.errorChecking) { + // throw Exception if NamedNodeMap is readonly + if (this._readonly || (this.parentNode && this.parentNode._readonly)) { + throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // throw Exception if arg was not created by this Document + if (__ownerDocument__(this) != __ownerDocument__(arg)) { + throw (new DOMException(DOMException.WRONG_DOCUMENT_ERR)); + } + + // throw Exception if arg is already an attribute of another Element object + if (arg.ownerElement && (arg.ownerElement != this.parentNode)) { + throw (new DOMException(DOMException.INUSE_ATTRIBUTE_ERR)); + } + } + + // get item index + var itemIndex = __findNamedItemNSIndex__(this, arg.namespaceURI, arg.localName); + var ret = null; + + if (itemIndex > -1) { + // found it! + // use existing Attribute + ret = this[itemIndex]; + // throw Exception if Attr is readonly + if (__ownerDocument__(this).implementation.errorChecking && ret._readonly) { + throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } else { + // over-write existing NamedNode + this[itemIndex] = arg; + } + } else { + // add new NamedNode + Array.prototype.push.apply(this, [arg]); + } + arg.ownerElement = this.parentNode; + + //add to named node indexes on the document + __addToNamedIndexes__( + arg.namespaceURI?arg.namespaceURI+':'+arg.localName:arg.localName, + arg.value, + arg.ownerElement + ); + + // return old node or null + return ret; + }, + removeNamedItemNS: function(namespaceURI, localName) { + var ret = null; + + // test for exceptions + // throw Exception if NamedNodeMap is readonly + if (__ownerDocument__(this).implementation.errorChecking && (this._readonly || (this.parentNode && this.parentNode._readonly))) { + throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // get item index + var itemIndex = __findNamedItemNSIndex__(this, namespaceURI, localName); + + // throw Exception if there is no matching node in this map + if (__ownerDocument__(this).implementation.errorChecking && (itemIndex < 0)) { + throw (new DOMException(DOMException.NOT_FOUND_ERR)); + } + + // get Node + var oldNode = this[itemIndex]; + + //remove from named node indexes on the document + __removeFromNamedIndexes__( + namespaceURI?namespaceURI+'::'+localName:localName, + oldNode.value, + oldNode.ownerElement + ); + + // return removed node + return __removeChild__(this, itemIndex); + }, + get xml() { + var ret = ""; + + // create string containing concatenation of all (but last) + // Attribute string values (separated by spaces) + for (var i = 0; i < this.length - 1; i++) { + ret += this[i].xml + " "; + } + + // add last Attribute to string (without trailing space) + if (this.length > 0) { + ret += this[this.length - 1].xml; + } + + return ret; + }, + toString: function() { + return "[object NamedNodeMap]"; + } + +}); + +__addToNamedIndexes__ = function(name, value, element){ + log.debug('addToNamedIndexes %s %s', name, value); + var doc = __ownerDocument__(element); + switch(name.toLowerCase()){ + case "id": + log.debug('addToNamedIndexes #id %s', value); + doc._indexes_["#"+value] = element; break; + case "name": + log.debug('addToNamedIndexes @name %s', value); + if(!(doc._indexes_['@'+value])){ + doc._indexes_["@"+value] = new NodeList(doc, null); + } + if(element.tagName.toLowerCase() === 'form'){ + if( !doc[value] ){ + // -1){ + Array.prototype.splice.apply(doc._indexes_["@"+value],[index,1]); + } + break; + } +}; + +}(/*Envjs.DOM.NamedNodeMap*/)); + + + +/** + * @method __findNamedItemIndex__ + * find the item index of the node with the specified name + * + * @param name : string - the name of the required node + * @param isnsmap : if its a NamespaceNodeMap + * @return : int + */ +__findNamedItemIndex__ = function(namednodemap, name, isnsmap) { + var ret = -1; + // loop through all nodes + for (var i = 0; i < namednodemap.length; i++) { + //console.log("namednodemap (local %s, name %s), name %s, isnsmap %s", + // namednodemap.localName, namednodemap.name, name, isnsmap) + // compare name to each node's nodeName + if (namednodemap[i].localName && name && isnsmap) { + if (namednodemap[i].localName.toLowerCase() == name.toLowerCase()) { + // found it! + ret = i; + break; + } + } else { + if (namednodemap[i].name && name) { + if (namednodemap[i].name.toLowerCase() == name.toLowerCase()) { + // found it! + ret = i; + break; + } + } + } + } + // if node is not found, default value -1 is returned + return ret; +}; + + +/** + * @method __findNamedItemNSIndex__ + * find the item index of the node with the specified + * namespaceURI and localName + * + * @param namespaceURI : string - the namespace URI of the required node + * @param localName : string - the local name of the required node + * @return : int + */ +__findNamedItemNSIndex__ = function(namednodemap, namespaceURI, localName) { + var ret = -1; + // test that localName is not null + if (localName) { + // loop through all nodes + for (var i = 0; i < namednodemap.length; i++) { + if (namednodemap[i].namespaceURI && namednodemap[i].localName) { + // compare name to each node's namespaceURI and localName + if ((namednodemap[i].namespaceURI.toLowerCase() == namespaceURI.toLowerCase()) && + (namednodemap[i].localName.toLowerCase() == localName.toLowerCase())) { + // found it! + ret = i; + break; + } + } + } + } + // if node is not found, default value -1 is returned + return ret; +}; + + +/** + * @method __hasAttribute__ + * Returns true if specified node exists + * + * @param name : string - the name of the required node + * @return : boolean + */ +__hasAttribute__ = function(namednodemap, name) { + var ret = false; + // test that Named Node exists + var itemIndex = __findNamedItemIndex__(namednodemap, name); + if (itemIndex > -1) { + // found it! + ret = true; + } + // if node is not found, default value false is returned + return ret; +}; + +/** + * @method __hasAttributeNS__ + * Returns true if specified node exists + * + * @param namespaceURI : string - the namespace URI of the required node + * @param localName : string - the local name of the required node + * @return : boolean + */ +__hasAttributeNS__ = function(namednodemap, namespaceURI, localName) { + var ret = false, + // test that Named Node exists + itemIndex = __findNamedItemNSIndex__(namednodemap, namespaceURI, localName); + if (itemIndex > -1) { + // found it! + ret = true; + } + // if node is not found, default value false is returned + return ret; +}; + +/** + * @method __cloneNamedNodes__ + * Returns a NamedNodeMap containing clones of the Nodes in this NamedNodeMap + * + * @param parentNode : Node - the new parent of the cloned NodeList + * @param isnsmap : bool - is this a NamespaceNodeMap + * @return NamedNodeMap containing clones of the Nodes in this NamedNodeMap + */ +__cloneNamedNodes__ = function(namednodemap, parentNode, isnsmap) { + var cloneNamedNodeMap = isnsmap ? + new NamespaceNodeMap(namednodemap.ownerDocument, parentNode) : + new NamedNodeMap(namednodemap.ownerDocument, parentNode); + + // create list containing clones of all children + for (var i = 0; i < namednodemap.length; i++) { + __appendChild__(cloneNamedNodeMap, namednodemap[i].cloneNode(false)); + } + + return cloneNamedNodeMap; +}; + + +/** + * @class NamespaceNodeMap - + * used to represent collections of namespace nodes that can be + * accessed by name typically a set of Element attributes + * + * @extends NamedNodeMap + * + * @param ownerDocument : Document - the ownerDocument + * @param parentNode : Node - the node that the NamespaceNodeMap is attached to (or null) + */ +NamespaceNodeMap = function(ownerDocument, parentNode) { + NamedNodeMap.apply(this, arguments); +}; +NamespaceNodeMap.prototype = new NamedNodeMap(); +__extend__(NamespaceNodeMap.prototype, { + get xml() { + var ret = "", + ns, + ind, + namespaces, + i; + // identify namespaces declared local to this Element (ie, not inherited) + for (ind = 0; ind < this.length; ind++) { + // if namespace declaration does not exist in the containing node's, parentNode's namespaces + ns = this[ind]; + namespaces = this.parentNode.parentNode._namespaces; + for(i=0;i this.data.length) || (count < 0))) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + + // delete data + if(!count || (offset + count) > this.data.length) { + this.data = this.data.substring(0, offset); + }else { + this.data = this.data.substring(0, offset). + concat(this.data.substring(offset + count)); + } + } + }, + insertData: function(offset, arg){ + // throw Exception if CharacterData is readonly + if(__ownerDocument__(this).implementation.errorChecking && this._readonly){ + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + if(this.data){ + // throw Exception if offset is negative or greater than the data length, + if (__ownerDocument__(this).implementation.errorChecking && + ((offset < 0) || (offset > this.data.length))) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + + // insert data + this.data = this.data.substring(0, offset).concat(arg, this.data.substring(offset)); + }else { + // throw Exception if offset is negative or greater than the data length, + if (__ownerDocument__(this).implementation.errorChecking && (offset !== 0)) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + + // set data + this.data = arg; + } + }, + replaceData: function(offset, count, arg){ + // throw Exception if CharacterData is readonly + if (__ownerDocument__(this).implementation.errorChecking && this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + if (this.data) { + // throw Exception if offset is negative or greater than the data length, + if (__ownerDocument__(this).implementation.errorChecking && + ((offset < 0) || (offset > this.data.length) || (count < 0))) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + + // replace data + this.data = this.data.substring(0, offset). + concat(arg, this.data.substring(offset + count)); + }else { + // set data + this.data = arg; + } + }, + substringData: function(offset, count){ + var ret = null; + if (this.data) { + // throw Exception if offset is negative or greater than the data length, + // or the count is negative + if (__ownerDocument__(this).implementation.errorChecking && + ((offset < 0) || (offset > this.data.length) || (count < 0))) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + // if count is not specified + if (!count) { + ret = this.data.substring(offset); // default to 'end of string' + }else{ + ret = this.data.substring(offset, offset + count); + } + } + return ret; + }, + toString : function(){ + return "[object CharacterData]"; + } +}); + +}(/*Envjs.DOM.CharacterData*/)); + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.Text').debug('available'); +}); +/** + * @class Text + * The Text interface represents the textual content (termed + * character data in XML) of an Element or Attr. + * If there is no markup inside an element's content, the text is + * contained in a single object implementing the Text interface that + * is the only child of the element. If there is markup, it is + * parsed into a list of elements and Text nodes that form the + * list of children of the element. + * @extends CharacterData + * @param ownerDocument The Document object associated with this node. + */ +exports.Text = Text = function(ownerDocument) { + CharacterData.apply(this, arguments); + this.nodeName = "#text"; +}; +Text.prototype = new CharacterData(); +__extend__(Text.prototype,{ + get localName(){ + return null; + }, + // Breaks this Text node into two Text nodes at the specified offset, + // keeping both in the tree as siblings. This node then only contains + // all the content up to the offset point. And a new Text node, which + // is inserted as the next sibling of this node, contains all the + // content at and after the offset point. + splitText : function(offset) { + var data, + inode; + // test for exceptions + if (__ownerDocument__(this).implementation.errorChecking) { + // throw Exception if Node is readonly + if (this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + // throw Exception if offset is negative or greater than the data length, + if ((offset < 0) || (offset > this.data.length)) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + } + if (this.parentNode) { + // get remaining string (after offset) + data = this.substringData(offset); + // create new TextNode with remaining string + inode = __ownerDocument__(this).createTextNode(data); + // attach new TextNode + if (this.nextSibling) { + this.parentNode.insertBefore(inode, this.nextSibling); + } else { + this.parentNode.appendChild(inode); + } + // remove remaining string from original TextNode + this.deleteData(offset); + } + return inode; + }, + get nodeType(){ + return Node.TEXT_NODE; + }, + get xml(){ + return __escapeXML__(""+ this.nodeValue); + }, + toString: function(){ + return "[object Text]"; + } +}); + +}(/*Envjs.DOM.Text*/)); + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.CDATASection').debug('available'); +}); +/** + * @class CDATASection + * CDATA sections are used to escape blocks of text containing + * characters that would otherwise be regarded as markup. + * The only delimiter that is recognized in a CDATA section is + * the "\]\]\>" string that ends the CDATA section + * @extends Text + * @param ownerDocument : The Document object associated with this node. + */ +exports.CDATASection = CDATASection = function(ownerDocument) { + Text.apply(this, arguments); + this.nodeName = '#cdata-section'; +}; +CDATASection.prototype = new Text(); +__extend__(CDATASection.prototype,{ + get nodeType(){ + return Node.CDATA_SECTION_NODE; + }, + get xml(){ + return ""; + }, + toString : function(){ + return "[object CDATASection]"; + } +}); + +}(/*Envjs.DOM.CDATASection*/)); + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.Comment').debug('available'); +}); +/** + * @class Comment + * This represents the content of a comment, i.e., all the + * characters between the starting '' + * @extends CharacterData + * @param ownerDocument : The Document object associated with this node. + */ +exports.Comment = Comment = function(ownerDocument) { + CharacterData.apply(this, arguments); + this.nodeName = "#comment"; +}; +Comment.prototype = new CharacterData(); +__extend__(Comment.prototype, { + get localName(){ + return null; + }, + get nodeType(){ + return Node.COMMENT_NODE; + }, + get xml(){ + return ""; + }, + toString : function(){ + return "[object Comment]"; + } +}); + +}(/*Envjs.DOM.Comment*/)); + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.DOMImplementation').debug('available'); +}); +/** + * @class DOMImplementation - + * provides a number of methods for performing operations + * that are independent of any particular instance of the + * document object model. + * + * @author Jon van Noort (jon@webarcana.com.au) + */ +exports.DOMImplementation = DOMImplementation = function() { + this.preserveWhiteSpace = true; // by default, ignore whitespace + this.namespaceAware = true; // by default, handle namespaces + this.errorChecking = false; // by default, test for exceptions +}; + +__extend__(DOMImplementation.prototype,{ + // @param feature : string - The package name of the feature to test. + // the legal only values are "XML" and "HTML" (case-insensitive). + // @param version : string - This is the version number of the package + // name to test. In Level 1, this is the string "1.0".* + // @return : boolean + hasFeature : function(feature, version) { + var ret = false; + if (feature.toLowerCase() == "xml") { + ret = (!version || (version == "1.0") || (version == "2.0")); + } + else if (feature.toLowerCase() == "html") { + ret = (!version || (version == "1.0")); + } + else if (feature.toLowerCase() == "core") { + ret = (!version || (version == "2.0")); + } + else if (feature == "http://www.w3.org/TR/SVG11/feature#BasicStructure") { + ret = (version == "1.1"); + } + return ret; + }, + createDocumentType : function(qname, publicId, systemId){ + var doctype = new DocumentType(); + doctype.nodeName = qname?qname.toUpperCase():null; + doctype.publicId = publicId?publicId:null; + doctype.systemId = systemId?systemId:null; + return doctype; + }, + createDocument : function(nsuri, qname, doctype){ + + var doc = null, documentElement; + + doc = new Document(this, null); + if(doctype){ + doc.doctype = doctype; + } + + if(nsuri && qname){ + documentElement = doc.createElementNS(nsuri, qname); + }else if(qname){ + documentElement = doc.createElement(qname); + } + if(documentElement){ + doc.appendChild(documentElement); + } + return doc; + }, + createHTMLDocument : function(title){ + var doc = new HTMLDocument(this, null, ""); + var html = doc.createElement("html"); doc.appendChild(html); + var head = doc.createElement("head"); html.appendChild(head); + var body = doc.createElement("body"); html.appendChild(body); + var t = doc.createElement("title"); head.appendChild(t); + if( title) { + t.appendChild(doc.createTextNode(title)); + } + return doc; + }, + translateErrCode : function(code) { + //convert DOMException Code to human readable error message; + var msg = ""; + + switch (code) { + case DOMException.INDEX_SIZE_ERR : // 1 + msg = "INDEX_SIZE_ERR: Index out of bounds"; + break; + + case DOMException.DOMSTRING_SIZE_ERR : // 2 + msg = "DOMSTRING_SIZE_ERR: The resulting string is too long to fit in a DOMString"; + break; + + case DOMException.HIERARCHY_REQUEST_ERR : // 3 + msg = "HIERARCHY_REQUEST_ERR: The Node can not be inserted at this location"; + break; + + case DOMException.WRONG_DOCUMENT_ERR : // 4 + msg = "WRONG_DOCUMENT_ERR: The source and the destination Documents are not the same"; + break; + + case DOMException.INVALID_CHARACTER_ERR : // 5 + msg = "INVALID_CHARACTER_ERR: The string contains an invalid character"; + break; + + case DOMException.NO_DATA_ALLOWED_ERR : // 6 + msg = "NO_DATA_ALLOWED_ERR: This Node / NodeList does not support data"; + break; + + case DOMException.NO_MODIFICATION_ALLOWED_ERR : // 7 + msg = "NO_MODIFICATION_ALLOWED_ERR: This object cannot be modified"; + break; + + case DOMException.NOT_FOUND_ERR : // 8 + msg = "NOT_FOUND_ERR: The item cannot be found"; + break; + + case DOMException.NOT_SUPPORTED_ERR : // 9 + msg = "NOT_SUPPORTED_ERR: This implementation does not support function"; + break; + + case DOMException.INUSE_ATTRIBUTE_ERR : // 10 + msg = "INUSE_ATTRIBUTE_ERR: The Attribute has already been assigned to another Element"; + break; + + // Introduced in DOM Level 2: + case DOMException.INVALID_STATE_ERR : // 11 + msg = "INVALID_STATE_ERR: The object is no longer usable"; + break; + + case DOMException.SYNTAX_ERR : // 12 + msg = "SYNTAX_ERR: Syntax error"; + break; + + case DOMException.INVALID_MODIFICATION_ERR : // 13 + msg = "INVALID_MODIFICATION_ERR: Cannot change the type of the object"; + break; + + case DOMException.NAMESPACE_ERR : // 14 + msg = "NAMESPACE_ERR: The namespace declaration is incorrect"; + break; + + case DOMException.INVALID_ACCESS_ERR : // 15 + msg = "INVALID_ACCESS_ERR: The object does not support this function"; + break; + + default : + msg = "UNKNOWN: Unknown Exception Code ("+ code +")"; + } + + return msg; + }, + toString : function(){ + return "[object DOMImplementation]"; + } +}); + +}(/*Envjs.DOM.DOMImplementation*/)); + + + + +/** + * @method DOMImplementation._isNamespaceDeclaration - Return true, if attributeName is a namespace declaration + * @author Jon van Noort (jon@webarcana.com.au) + * @param attributeName : string - the attribute name + * @return : boolean + */ +function __isNamespaceDeclaration__(attributeName) { + // test if attributeName is 'xmlns' + return (attributeName.indexOf('xmlns') > -1); +} + +/** + * @method DOMImplementation._isIdDeclaration - Return true, if attributeName is an id declaration + * @author Jon van Noort (jon@webarcana.com.au) + * @param attributeName : string - the attribute name + * @return : boolean + */ +function __isIdDeclaration__(attributeName) { + // test if attributeName is 'id' (case insensitive) + return attributeName?(attributeName.toLowerCase() == 'id'):false; +} + +/** + * @method DOMImplementation._isValidName - Return true, + * if name contains no invalid characters + * @author Jon van Noort (jon@webarcana.com.au) + * @param name : string - the candidate name + * @return : boolean + */ +var re_validName = /^[a-zA-Z_:][a-zA-Z0-9\.\-_:]*$/; +function __isValidName__(name) { + // test if name contains only valid characters + return name.match(re_validName); +} + +/** + * @method DOMImplementation._isValidString - Return true, if string does not contain any illegal chars + * All of the characters 0 through 31 and character 127 are nonprinting control characters. + * With the exception of characters 09, 10, and 13, (Ox09, Ox0A, and Ox0D) + * Note: different from _isValidName in that ValidStrings may contain spaces + * @author Jon van Noort (jon@webarcana.com.au) + * @param name : string - the candidate string + * @return : boolean + */ +var re_invalidStringChars = /\x01|\x02|\x03|\x04|\x05|\x06|\x07|\x08|\x0B|\x0C|\x0E|\x0F|\x10|\x11|\x12|\x13|\x14|\x15|\x16|\x17|\x18|\x19|\x1A|\x1B|\x1C|\x1D|\x1E|\x1F|\x7F/; +function __isValidString__(name) { + // test that string does not contains invalid characters + return (name.search(re_invalidStringChars) < 0); +} + +/** + * @method DOMImplementation._parseNSName - parse the namespace name. + * if there is no colon, the + * @author Jon van Noort (jon@webarcana.com.au) + * @param qualifiedName : string - The qualified name + * @return : NSName - [ + .prefix : string - The prefix part of the qname + .namespaceName : string - The namespaceURI part of the qname + ] + */ +function __parseNSName__(qualifiedName) { + var resultNSName = {}; + // unless the qname has a namespaceName, the prefix is the entire String + resultNSName.prefix = qualifiedName; + resultNSName.namespaceName = ""; + // split on ':' + var delimPos = qualifiedName.indexOf(':'); + if (delimPos > -1) { + // get prefix + resultNSName.prefix = qualifiedName.substring(0, delimPos); + // get namespaceName + resultNSName.namespaceName = qualifiedName.substring(delimPos +1, qualifiedName.length); + } + return resultNSName; +} + +/** + * @method DOMImplementation._parseQName - parse the qualified name + * @author Jon van Noort (jon@webarcana.com.au) + * @param qualifiedName : string - The qualified name + * @return : QName + */ +function __parseQName__(qualifiedName) { + var resultQName = {}; + // unless the qname has a prefix, the local name is the entire String + resultQName.localName = qualifiedName; + resultQName.prefix = ""; + // split on ':' + var delimPos = qualifiedName.indexOf(':'); + if (delimPos > -1) { + // get prefix + resultQName.prefix = qualifiedName.substring(0, delimPos); + // get localName + resultQName.localName = qualifiedName.substring(delimPos +1, qualifiedName.length); + } + return resultQName; +} +var __isValidNamespace__; + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.Document').debug('available'); +}); + +/** + * @class Document - The Document interface represents the entire HTML + * or XML document. Conceptually, it is the root of the document tree, + * and provides the primary access to the document's data. + * + * @extends Node + * @param implementation : DOMImplementation - the creator Implementation + */ +exports.Document = Document = function(implementation, docParentWindow) { + Node.apply(this, [this]); + + this.async = true; + // The Document Type Declaration (see DocumentType) associated with this document + this.doctype = null; + // The DOMImplementation object that handles this document. + this.implementation = implementation; + + this.nodeName = "#document"; + // initially false, set to true by parser + this.parsing = false; + this.baseURI = 'about:blank'; + + this.ownerDocument = null; + + this.importing = false; +}; + +Document.prototype = new Node(); +__extend__(Document.prototype,{ + get localName(){ + return null; + }, + get textContent(){ + return null; + }, + get all(){ + log.debug('all'); + return this.getElementsByTagName('*'); + }, + get documentElement(){ + var i, length = this.childNodes?this.childNodes.length:0; + for(i=0;i -1 ){ + valid = false; + } + + if ((valid) && (!isAttribute)) { + // if the namespaceURI is not null + if (!namespaceURI) { + valid = false; + } + } + + // if the qualifiedName has a prefix + if ((valid) && (qName.prefix === "")) { + valid = false; + } + } + + // if the qualifiedName has a prefix that is "xml" and the namespaceURI is + // different from "http://www.w3.org/XML/1998/namespace" [Namespaces]. + if ((valid) && (qName.prefix === "xml") && (namespaceURI !== "http://www.w3.org/XML/1998/namespace")) { + valid = false; + } + + return valid; +}; + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.DocumentType').debug('available'); +}); + +/** + * @author envjs team + * @param {Document} onwnerDocument + */ +exports.DocumentType = DocumentType = function(ownerDocument) { + Node.apply(this, arguments); + this.systemId = null; + this.publicId = null; +}; +DocumentType.prototype = new Node(); +__extend__({ + get name(){ + return this.nodeName; + }, + get entities(){ + return null; + }, + get internalSubsets(){ + return null; + }, + get notations(){ + return null; + }, + toString : function(){ + return "[object DocumentType]"; + } +}); + +}(/*Envjs.DOM.DocumentType*/)); + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.DocumentFragment').debug('available'); +}); +/** + * @class DocumentFragment - + * DocumentFragment is a "lightweight" or "minimal" Document object. + * @extends Node + * @param ownerDocument : The Document object associated with this node. + */ +exports.DocumentFragment = DocumentFragment =function(ownerDocument) { + Node.apply(this, arguments); + this.nodeName = "#document-fragment"; +}; +DocumentFragment.prototype = new Node(); +__extend__(DocumentFragment.prototype,{ + get nodeType(){ + return Node.DOCUMENT_FRAGMENT_NODE; + }, + get xml(){ + var xml = "", + count = this.childNodes.length; + + // create string concatenating the serialized ChildNodes + for (var i = 0; i < count; i++) { + xml += this.childNodes.item(i).xml; + } + + return xml; + }, + toString : function(){ + return "[object DocumentFragment]"; + }, + get localName(){ + return null; + } +}); + +}(/*Envjs.DOM.DocumentFragment*/)); + + + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.DOMException').debug('available'); +}); +/** + * @class DOMException - raised when an operation is impossible to perform + * @author Jon van Noort (jon@webarcana.com.au) + * @param code : int - the exception code (one of the DOMException constants) + */ +exports.DOMException = DOMException = function(code) { + this.code = code; +}; + +// DOMException constants +// Introduced in DOM Level 1: +DOMException.INDEX_SIZE_ERR = 1; +DOMException.DOMSTRING_SIZE_ERR = 2; +DOMException.HIERARCHY_REQUEST_ERR = 3; +DOMException.WRONG_DOCUMENT_ERR = 4; +DOMException.INVALID_CHARACTER_ERR = 5; +DOMException.NO_DATA_ALLOWED_ERR = 6; +DOMException.NO_MODIFICATION_ALLOWED_ERR = 7; +DOMException.NOT_FOUND_ERR = 8; +DOMException.NOT_SUPPORTED_ERR = 9; +DOMException.INUSE_ATTRIBUTE_ERR = 10; + +// Introduced in DOM Level 2: +DOMException.INVALID_STATE_ERR = 11; +DOMException.SYNTAX_ERR = 12; +DOMException.INVALID_MODIFICATION_ERR = 13; +DOMException.NAMESPACE_ERR = 14; +DOMException.INVALID_ACCESS_ERR = 15; + +//Introduced in DOM Level 3: +DOMException.VALIDATION_ERR = 16; +DOMException.TYPE_MISMATCH_ERR = 17; + +}(/*Envjs.DOM.DOMException*/)); +var __E4XParser__, + __E4XtoDomNode__; + +(function(){ + +var log = Envjs.logger(); + +Envjs.once('tick', function(){ + log = Envjs.logger('Envjs.DOM.DOMParser').debug('available'); +}); + +/** + * @author ariel flesler + * http://flesler.blogspot.com/2008/11/fast-trim-function-for-javascript.html + * @param {Object} str + */ +function trim( str ){ + return (str || "").replace( /^\s+|\s+$/g, "" ); +} + +// ========================================================================= +// +// xmlsax.js - an XML SAX parser in JavaScript. +// +// version 3.1 +// +// ========================================================================= +// +// Copyright (C) 2001 - 2002 David Joham (djoham@yahoo.com) and Scott Severtson +// +// 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. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// +// Visit the XML for