From 3cfb007d9dc0a8f32b67a4c7886bdfaa7a2c5e17 Mon Sep 17 00:00:00 2001 From: Michael Bostock Date: Thu, 3 Mar 2011 14:20:18 -0800 Subject: [PATCH 1/7] Add proper rounding to treemap layout! --- examples/treemap/flare.json | 548 +++++++++++++++++----------------- examples/treemap/layout.js | 47 +-- examples/treemap/treemap.html | 24 +- 3 files changed, 316 insertions(+), 303 deletions(-) diff --git a/examples/treemap/flare.json b/examples/treemap/flare.json index 2efb409a..ec93c038 100644 --- a/examples/treemap/flare.json +++ b/examples/treemap/flare.json @@ -1,284 +1,286 @@ { - "analytics": { - "cluster": { - "AgglomerativeCluster": 3938, - "CommunityStructure": 3812, - "HierarchicalCluster": 6714, - "MergeEdge": 743 + "flare": { + "analytics": { + "cluster": { + "AgglomerativeCluster": 3938, + "CommunityStructure": 3812, + "HierarchicalCluster": 6714, + "MergeEdge": 743 + }, + "graph": { + "BetweennessCentrality": 3534, + "LinkDistance": 5731, + "MaxFlowMinCut": 7840, + "ShortestPaths": 5914, + "SpanningTree": 3416 + }, + "optimization": { + "AspectRatioBanker": 7074 + } }, - "graph": { - "BetweennessCentrality": 3534, - "LinkDistance": 5731, - "MaxFlowMinCut": 7840, - "ShortestPaths": 5914, - "SpanningTree": 3416 - }, - "optimization": { - "AspectRatioBanker": 7074 - } - }, - "animate": { - "Easing": 17010, - "FunctionSequence": 5842, - "interpolate": { - "ArrayInterpolator": 1983, - "ColorInterpolator": 2047, - "DateInterpolator": 1375, - "Interpolator": 8746, - "MatrixInterpolator": 2202, - "NumberInterpolator": 1382, - "ObjectInterpolator": 1629, - "PointInterpolator": 1675, - "RectangleInterpolator": 2042 - }, - "ISchedulable": 1041, - "Parallel": 5176, - "Pause": 449, - "Scheduler": 5593, - "Sequence": 5534, - "Transition": 9201, - "Transitioner": 19975, - "TransitionEvent": 1116, - "Tween": 6006 - }, - "data": { - "converters": { - "Converters": 721, - "DelimitedTextConverter": 4294, - "GraphMLConverter": 9800, - "IDataConverter": 1314, - "JSONConverter": 2220 - }, - "DataField": 1759, - "DataSchema": 2165, - "DataSet": 586, - "DataSource": 3331, - "DataTable": 772, - "DataUtil": 3322 - }, - "display": { - "DirtySprite": 8833, - "LineSprite": 1732, - "RectSprite": 3623, - "TextSprite": 10066 - }, - "flex": { - "FlareVis": 4116 - }, - "physics": { - "DragForce": 1082, - "GravityForce": 1336, - "IForce": 319, - "NBodyForce": 10498, - "Particle": 2822, - "Simulation": 9983, - "Spring": 2213, - "SpringForce": 1681 - }, - "query": { - "AggregateExpression": 1616, - "And": 1027, - "Arithmetic": 3891, - "Average": 891, - "BinaryExpression": 2893, - "Comparison": 5103, - "CompositeExpression": 3677, - "Count": 781, - "DateUtil": 4141, - "Distinct": 933, - "Expression": 5130, - "ExpressionIterator": 3617, - "Fn": 3240, - "If": 2732, - "IsA": 2039, - "Literal": 1214, - "Match": 3748, - "Maximum": 843, - "methods": { - "add": 593, - "and": 330, - "average": 287, - "count": 277, - "distinct": 292, - "div": 595, - "eq": 594, - "fn": 460, - "gt": 603, - "gte": 625, - "iff": 748, - "isa": 461, - "lt": 597, - "lte": 619, - "max": 283, - "min": 283, - "mod": 591, - "mul": 603, - "neq": 599, - "not": 386, - "or": 323, - "orderby": 307, - "range": 772, - "select": 296, - "stddev": 363, - "sub": 600, - "sum": 280, - "update": 307, - "variance": 335, - "where": 299, - "xor": 354, - "_": 264 - }, - "Minimum": 843, - "Not": 1554, - "Or": 970, - "Query": 13896, - "Range": 1594, - "StringUtil": 4130, - "Sum": 791, - "Variable": 1124, - "Variance": 1876, - "Xor": 1101 - }, - "scale": { - "IScaleMap": 2105, - "LinearScale": 1316, - "LogScale": 3151, - "OrdinalScale": 3770, - "QuantileScale": 2435, - "QuantitativeScale": 4839, - "RootScale": 1756, - "Scale": 4268, - "ScaleType": 1821, - "TimeScale": 5833 - }, - "util": { - "Arrays": 8258, - "Colors": 10001, - "Dates": 8217, - "Displays": 12555, - "Filter": 2324, - "Geometry": 10993, - "heap": { - "FibonacciHeap": 9354, - "HeapNode": 1233 - }, - "IEvaluable": 335, - "IPredicate": 383, - "IValueProxy": 874, - "math": { - "DenseMatrix": 3165, - "IMatrix": 2815, - "SparseMatrix": 3366 - }, - "Maths": 17705, - "Orientation": 1486, - "palette": { - "ColorPalette": 6367, - "Palette": 1229, - "ShapePalette": 2059, - "SizePalette": 2291 - }, - "Property": 5559, - "Shapes": 19118, - "Sort": 6887, - "Stats": 6557, - "Strings": 22026 - }, - "vis": { - "axis": { - "Axes": 1302, - "Axis": 24593, - "AxisGridLine": 652, - "AxisLabel": 636, - "CartesianAxes": 6703 - }, - "controls": { - "AnchorControl": 2138, - "ClickControl": 3824, - "Control": 1353, - "ControlList": 4665, - "DragControl": 2649, - "ExpandControl": 2832, - "HoverControl": 4896, - "IControl": 763, - "PanZoomControl": 5222, - "SelectionControl": 7862, - "TooltipControl": 8435 + "animate": { + "Easing": 17010, + "FunctionSequence": 5842, + "interpolate": { + "ArrayInterpolator": 1983, + "ColorInterpolator": 2047, + "DateInterpolator": 1375, + "Interpolator": 8746, + "MatrixInterpolator": 2202, + "NumberInterpolator": 1382, + "ObjectInterpolator": 1629, + "PointInterpolator": 1675, + "RectangleInterpolator": 2042 + }, + "ISchedulable": 1041, + "Parallel": 5176, + "Pause": 449, + "Scheduler": 5593, + "Sequence": 5534, + "Transition": 9201, + "Transitioner": 19975, + "TransitionEvent": 1116, + "Tween": 6006 }, "data": { - "Data": 20544, - "DataList": 19788, - "DataSprite": 10349, - "EdgeSprite": 3301, - "NodeSprite": 19382, - "render": { - "ArrowType": 698, - "EdgeRenderer": 5569, - "IRenderer": 353, - "ShapeRenderer": 2247 + "converters": { + "Converters": 721, + "DelimitedTextConverter": 4294, + "GraphMLConverter": 9800, + "IDataConverter": 1314, + "JSONConverter": 2220 }, - "ScaleBinding": 11275, - "Tree": 7147, - "TreeBuilder": 9930 + "DataField": 1759, + "DataSchema": 2165, + "DataSet": 586, + "DataSource": 3331, + "DataTable": 772, + "DataUtil": 3322 }, - "events": { - "DataEvent": 2313, - "SelectionEvent": 1880, - "TooltipEvent": 1701, - "VisualizationEvent": 1117 + "display": { + "DirtySprite": 8833, + "LineSprite": 1732, + "RectSprite": 3623, + "TextSprite": 10066 }, - "legend": { - "Legend": 20859, - "LegendItem": 4614, - "LegendRange": 10530 + "flex": { + "FlareVis": 4116 }, - "operator": { - "distortion": { - "BifocalDistortion": 4461, - "Distortion": 6314, - "FisheyeDistortion": 3444 - }, - "encoder": { - "ColorEncoder": 3179, - "Encoder": 4060, - "PropertyEncoder": 4138, - "ShapeEncoder": 1690, - "SizeEncoder": 1830 - }, - "filter": { - "FisheyeTreeFilter": 5219, - "GraphDistanceFilter": 3165, - "VisibilityFilter": 3509 - }, - "IOperator": 1286, - "label": { - "Labeler": 9956, - "RadialLabeler": 3899, - "StackedAreaLabeler": 3202 - }, - "layout": { - "AxisLayout": 6725, - "BundledEdgeRouter": 3727, - "CircleLayout": 9317, - "CirclePackingLayout": 12003, - "DendrogramLayout": 4853, - "ForceDirectedLayout": 8411, - "IcicleTreeLayout": 4864, - "IndentedTreeLayout": 3174, - "Layout": 7881, - "NodeLinkTreeLayout": 12870, - "PieLayout": 2728, - "RadialTreeLayout": 12348, - "RandomLayout": 870, - "StackedAreaLayout": 9121, - "TreeMapLayout": 9191 - }, - "Operator": 2490, - "OperatorList": 5248, - "OperatorSequence": 4190, - "OperatorSwitch": 2581, - "SortOperator": 2023 + "physics": { + "DragForce": 1082, + "GravityForce": 1336, + "IForce": 319, + "NBodyForce": 10498, + "Particle": 2822, + "Simulation": 9983, + "Spring": 2213, + "SpringForce": 1681 }, - "Visualization": 16540 + "query": { + "AggregateExpression": 1616, + "And": 1027, + "Arithmetic": 3891, + "Average": 891, + "BinaryExpression": 2893, + "Comparison": 5103, + "CompositeExpression": 3677, + "Count": 781, + "DateUtil": 4141, + "Distinct": 933, + "Expression": 5130, + "ExpressionIterator": 3617, + "Fn": 3240, + "If": 2732, + "IsA": 2039, + "Literal": 1214, + "Match": 3748, + "Maximum": 843, + "methods": { + "add": 593, + "and": 330, + "average": 287, + "count": 277, + "distinct": 292, + "div": 595, + "eq": 594, + "fn": 460, + "gt": 603, + "gte": 625, + "iff": 748, + "isa": 461, + "lt": 597, + "lte": 619, + "max": 283, + "min": 283, + "mod": 591, + "mul": 603, + "neq": 599, + "not": 386, + "or": 323, + "orderby": 307, + "range": 772, + "select": 296, + "stddev": 363, + "sub": 600, + "sum": 280, + "update": 307, + "variance": 335, + "where": 299, + "xor": 354, + "_": 264 + }, + "Minimum": 843, + "Not": 1554, + "Or": 970, + "Query": 13896, + "Range": 1594, + "StringUtil": 4130, + "Sum": 791, + "Variable": 1124, + "Variance": 1876, + "Xor": 1101 + }, + "scale": { + "IScaleMap": 2105, + "LinearScale": 1316, + "LogScale": 3151, + "OrdinalScale": 3770, + "QuantileScale": 2435, + "QuantitativeScale": 4839, + "RootScale": 1756, + "Scale": 4268, + "ScaleType": 1821, + "TimeScale": 5833 + }, + "util": { + "Arrays": 8258, + "Colors": 10001, + "Dates": 8217, + "Displays": 12555, + "Filter": 2324, + "Geometry": 10993, + "heap": { + "FibonacciHeap": 9354, + "HeapNode": 1233 + }, + "IEvaluable": 335, + "IPredicate": 383, + "IValueProxy": 874, + "math": { + "DenseMatrix": 3165, + "IMatrix": 2815, + "SparseMatrix": 3366 + }, + "Maths": 17705, + "Orientation": 1486, + "palette": { + "ColorPalette": 6367, + "Palette": 1229, + "ShapePalette": 2059, + "SizePalette": 2291 + }, + "Property": 5559, + "Shapes": 19118, + "Sort": 6887, + "Stats": 6557, + "Strings": 22026 + }, + "vis": { + "axis": { + "Axes": 1302, + "Axis": 24593, + "AxisGridLine": 652, + "AxisLabel": 636, + "CartesianAxes": 6703 + }, + "controls": { + "AnchorControl": 2138, + "ClickControl": 3824, + "Control": 1353, + "ControlList": 4665, + "DragControl": 2649, + "ExpandControl": 2832, + "HoverControl": 4896, + "IControl": 763, + "PanZoomControl": 5222, + "SelectionControl": 7862, + "TooltipControl": 8435 + }, + "data": { + "Data": 20544, + "DataList": 19788, + "DataSprite": 10349, + "EdgeSprite": 3301, + "NodeSprite": 19382, + "render": { + "ArrowType": 698, + "EdgeRenderer": 5569, + "IRenderer": 353, + "ShapeRenderer": 2247 + }, + "ScaleBinding": 11275, + "Tree": 7147, + "TreeBuilder": 9930 + }, + "events": { + "DataEvent": 2313, + "SelectionEvent": 1880, + "TooltipEvent": 1701, + "VisualizationEvent": 1117 + }, + "legend": { + "Legend": 20859, + "LegendItem": 4614, + "LegendRange": 10530 + }, + "operator": { + "distortion": { + "BifocalDistortion": 4461, + "Distortion": 6314, + "FisheyeDistortion": 3444 + }, + "encoder": { + "ColorEncoder": 3179, + "Encoder": 4060, + "PropertyEncoder": 4138, + "ShapeEncoder": 1690, + "SizeEncoder": 1830 + }, + "filter": { + "FisheyeTreeFilter": 5219, + "GraphDistanceFilter": 3165, + "VisibilityFilter": 3509 + }, + "IOperator": 1286, + "label": { + "Labeler": 9956, + "RadialLabeler": 3899, + "StackedAreaLabeler": 3202 + }, + "layout": { + "AxisLayout": 6725, + "BundledEdgeRouter": 3727, + "CircleLayout": 9317, + "CirclePackingLayout": 12003, + "DendrogramLayout": 4853, + "ForceDirectedLayout": 8411, + "IcicleTreeLayout": 4864, + "IndentedTreeLayout": 3174, + "Layout": 7881, + "NodeLinkTreeLayout": 12870, + "PieLayout": 2728, + "RadialTreeLayout": 12348, + "RandomLayout": 870, + "StackedAreaLayout": 9121, + "TreeMapLayout": 9191 + }, + "Operator": 2490, + "OperatorList": 5248, + "OperatorSequence": 4190, + "OperatorSwitch": 2581, + "SortOperator": 2023 + }, + "Visualization": 16540 + } } } diff --git a/examples/treemap/layout.js b/examples/treemap/layout.js index fe251db7..2d6c6d40 100644 --- a/examples/treemap/layout.js +++ b/examples/treemap/layout.js @@ -2,6 +2,7 @@ function layout_treemap() { var children = layout_treemapChildren, value = layout_treemapValue, + round = Math.round, size = [1, 1]; // width, height // Recursively compute the node depth and value. @@ -43,17 +44,17 @@ function layout_treemap() { } } - // Arranges the specified children into squarified rows. - function squarify(children, node) { + // 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(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; - children = children.slice(); // copy-on-write - children.sort(function(a, b) { return b.area - a.area; }); row.area = 0; while ((n = children.length) > 0) { row.push(child = children[n - 1]); @@ -63,16 +64,17 @@ function layout_treemap() { best = score; } else { // abort, and try a different orientation row.area -= row.pop().area; - position(row, u, rect); + 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); + 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. @@ -94,45 +96,40 @@ function layout_treemap() { } // Positions the specified row of nodes. Modifies `rect`. - function position(row, u, rect) { + function position(row, u, rect, flush) { var i = -1, n = row.length, x = rect.x, y = rect.y, - v = u ? row.area / u : 0, + 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 = o.area / 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 = o.area / v; + y += o.dy = round(o.area / v); } + o.dy += rect.y + rect.dy - y; // rounding error rect.x += v; rect.dx -= v; } } - // Recursively computes the treemap layout for the node and its children. - function layout(node) { - var children = node.children; - if (children) { - squarify(children, node); - children.forEach(layout); - } - } - function treemap(d) { var nodes = [], root = sum(d, 0, nodes); @@ -140,7 +137,7 @@ function layout_treemap() { root.y = 0; root.dx = size[0]; root.dy = size[1]; - layout(root); + squarify(root); return nodes; } @@ -162,6 +159,12 @@ function layout_treemap() { return treemap; }; + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + return treemap; } @@ -172,3 +175,7 @@ function layout_treemapChildren(d) { function layout_treemapValue(d) { return d.value; } + +function layout_treemapSort(a, b) { + return b.area - a.area; +} diff --git a/examples/treemap/treemap.html b/examples/treemap/treemap.html index 9afae314..649e04eb 100644 --- a/examples/treemap/treemap.html +++ b/examples/treemap/treemap.html @@ -13,10 +13,10 @@ body { .cell { position: absolute; - background: lightsteelblue; border: solid 1px white; overflow: hidden; - text-overflow: ellipsis; + line-height: 12px; + text-indent: 2px; } @@ -25,26 +25,30 @@ body { From 36e935a8c884daf5b465ccf3dd6b64ac614e5dbb Mon Sep 17 00:00:00 2001 From: Michael Bostock Date: Thu, 3 Mar 2011 15:43:57 -0800 Subject: [PATCH 2/7] Add force & treemap to d3.layout. --- Makefile | 2 + d3.js | 2 +- d3.layout.js | 375 ++++++++++++++++++ d3.layout.min.js | 21 +- d3.min.js | 2 +- examples/force/cluster.html | 6 +- examples/force/force.html | 2 +- examples/force/force.js | 4 +- examples/treemap/treemap.css | 8 + examples/treemap/treemap.html | 50 +-- examples/treemap/treemap.js | 26 ++ src/core/core.js | 2 +- .../force/layout.js => src/layout/force.js | 11 +- .../layout.js => src/layout/treemap.js | 16 +- 14 files changed, 452 insertions(+), 75 deletions(-) create mode 100644 examples/treemap/treemap.css create mode 100644 examples/treemap/treemap.js rename examples/force/layout.js => src/layout/force.js (97%) rename examples/treemap/layout.js => src/layout/treemap.js (93%) diff --git a/Makefile b/Makefile index be6efd5a..66e91602 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: \ diff --git a/d3.js b/d3.js index ef2dbad2..bc2ca64a 100644 --- a/d3.js +++ b/d3.js @@ -1,4 +1,4 @@ -(function(){d3 = {version: "1.4.0"}; // semver +(function(){d3 = {version: "1.5.0"}; // semver if (!Date.now) Date.now = function() { return +new Date(); }; 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(;++f1){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], +function Qa(a){return a.radius}function Ra(){return 64}function Sa(){return"circle"}d3={version:"1.5.0"};if(!Date.now)Date.now=function(){return+new Date};if(!Object.create)Object.create=function(a){function b(){}b.prototype=a;return new b};var N=function(a){return Array.prototype.slice.call(a)};try{N(document.documentElement.childNodes)}catch(eb){N=ua}d3.ascending=function(a,b){return ab?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(;++cClustered Network - + + + - +
+ diff --git a/examples/treemap/treemap.js b/examples/treemap/treemap.js new file mode 100644 index 00000000..802e43ec --- /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, i) { 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/src/core/core.js b/src/core/core.js index 2359cdd0..5baff628 100644 --- a/src/core/core.js +++ b/src/core/core.js @@ -1 +1 @@ -d3 = {version: "1.4.0"}; // semver +d3 = {version: "1.5.0"}; // semver diff --git a/examples/force/layout.js b/src/layout/force.js similarity index 97% rename from examples/force/layout.js rename to src/layout/force.js index 7f39ddc0..9d20f027 100644 --- a/examples/force/layout.js +++ b/src/layout/force.js @@ -1,8 +1,8 @@ // A rudimentary force layout using Gauss-Seidel. -function layout_force() { +d3.layout.force = function() { var force = {}, event = d3.dispatch("tick"), - size = {x: 1, y: 1}, + size = [1, 1], alpha = .5, distance = 30, interval, @@ -88,8 +88,8 @@ function layout_force() { k, n = nodes.length, m = links.length, - w = size.x, - h = size.y, + w = size[0], + h = size[1], o; var paths = []; @@ -183,6 +183,7 @@ function layout_force() { function mouseup() { if (!node) return; mousemove(); + node.fixed = false; node = element = null; } @@ -190,4 +191,4 @@ function layout_force() { }; return force; -} +}; diff --git a/examples/treemap/layout.js b/src/layout/treemap.js similarity index 93% rename from examples/treemap/layout.js rename to src/layout/treemap.js index 2d6c6d40..3053c8b9 100644 --- a/examples/treemap/layout.js +++ b/src/layout/treemap.js @@ -1,7 +1,7 @@ // Squarified Treemaps by Mark Bruls, Kees Huizing, and Jarke J. van Wijk -function layout_treemap() { - var children = layout_treemapChildren, - value = layout_treemapValue, +d3.layout.treemap = function() { + var children = d3_layout_treemapChildren, + value = d3_layout_treemapValue, round = Math.round, size = [1, 1]; // width, height @@ -49,7 +49,7 @@ function layout_treemap() { if (!node.children) return; var rect = {x: node.x, y: node.y, dx: node.dx, dy: node.dy}, row = [], - children = node.children.slice().sort(layout_treemapSort), + children = node.children.slice().sort(d3_layout_treemapSort), child, best = Infinity, // the best row score so far score, // the current row score @@ -166,16 +166,16 @@ function layout_treemap() { }; return treemap; -} +}; -function layout_treemapChildren(d) { +function d3_layout_treemapChildren(d) { return d.children; } -function layout_treemapValue(d) { +function d3_layout_treemapValue(d) { return d.value; } -function layout_treemapSort(a, b) { +function d3_layout_treemapSort(a, b) { return b.area - a.area; } From 31c47f8f99b2a6a9f6f639be6423896501fe2aad Mon Sep 17 00:00:00 2001 From: Michael Bostock Date: Thu, 3 Mar 2011 16:01:18 -0800 Subject: [PATCH 3/7] Tiny simplification. --- examples/treemap/treemap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/treemap/treemap.js b/examples/treemap/treemap.js index 802e43ec..6cddf281 100644 --- a/examples/treemap/treemap.js +++ b/examples/treemap/treemap.js @@ -4,7 +4,7 @@ var w = 960, var treemap = d3.layout.treemap() .size([w, h]) - .children(function(d, i) { return typeof d.value == "object" && d3.entries(d.value); }) + .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") From 1fe1b466d9f6722e1682e4f093e376f0c9158823 Mon Sep 17 00:00:00 2001 From: Michael Bostock Date: Fri, 4 Mar 2011 11:47:19 -0800 Subject: [PATCH 4/7] Add lib/env-js. --- lib/env-js/LICENSE | 20 + lib/env-js/envjs/console.js | 278 + lib/env-js/envjs/css.js | 695 + lib/env-js/envjs/dom.js | 8971 ++++++++++++ lib/env-js/envjs/event.js | 720 + lib/env-js/envjs/html.js | 4997 +++++++ lib/env-js/envjs/johnson.js | 66 + lib/env-js/envjs/johnson.rb | 31 + lib/env-js/envjs/node.js | 3 + lib/env-js/envjs/parser.js | 14469 ++++++++++++++++++++ lib/env-js/envjs/platform/core.js | 3045 ++++ lib/env-js/envjs/platform/johnson.js | 363 + lib/env-js/envjs/platform/node.js | 343 + lib/env-js/envjs/platform/rhino.js | 461 + lib/env-js/envjs/platform/rubyracer.js | 340 + lib/env-js/envjs/platform/spydermonkey.js | 352 + lib/env-js/envjs/rhino.js | 91 + lib/env-js/envjs/rubyracer.js | 72 + lib/env-js/envjs/rubyracer.rb | 90 + lib/env-js/envjs/spydermonkey.js | 64 + lib/env-js/envjs/spydermonkey.py | 56 + lib/env-js/envjs/timer.js | 94 + lib/env-js/envjs/window.js | 864 ++ lib/env-js/envjs/xhr.js | 566 + lib/env-js/local_settings.js | 39 + 25 files changed, 37090 insertions(+) create mode 100644 lib/env-js/LICENSE create mode 100644 lib/env-js/envjs/console.js create mode 100644 lib/env-js/envjs/css.js create mode 100644 lib/env-js/envjs/dom.js create mode 100644 lib/env-js/envjs/event.js create mode 100644 lib/env-js/envjs/html.js create mode 100644 lib/env-js/envjs/johnson.js create mode 100644 lib/env-js/envjs/johnson.rb create mode 100644 lib/env-js/envjs/node.js create mode 100644 lib/env-js/envjs/parser.js create mode 100644 lib/env-js/envjs/platform/core.js create mode 100644 lib/env-js/envjs/platform/johnson.js create mode 100644 lib/env-js/envjs/platform/node.js create mode 100644 lib/env-js/envjs/platform/rhino.js create mode 100644 lib/env-js/envjs/platform/rubyracer.js create mode 100644 lib/env-js/envjs/platform/spydermonkey.js create mode 100644 lib/env-js/envjs/rhino.js create mode 100644 lib/env-js/envjs/rubyracer.js create mode 100644 lib/env-js/envjs/rubyracer.rb create mode 100644 lib/env-js/envjs/spydermonkey.js create mode 100644 lib/env-js/envjs/spydermonkey.py create mode 100644 lib/env-js/envjs/timer.js create mode 100644 lib/env-js/envjs/window.js create mode 100644 lib/env-js/envjs/xhr.js create mode 100644 lib/env-js/local_settings.js 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/envjs/console.js b/lib/env-js/envjs/console.js new file mode 100644 index 00000000..cc7a2525 --- /dev/null +++ b/lib/env-js/envjs/console.js @@ -0,0 +1,278 @@ + +/** + * @author envjs team + * @Console + */ + +var Envjs = Envjs || require('envjs/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..f5bd5fb0 --- /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('envjs/platform/core').Envjs, + Document = require('envjs/dom').Document, + HTMLElement = require('envjs/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..3e150f43 --- /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('envjs/platform/core').Envjs, + After = After || require('envjs/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