SVG.BBox = SVG.invent({ // Initialize create: function(element) { // get values if element is given if (element) { var box // yes this is ugly, but Firefox can be a bitch when it comes to elements that are not yet rendered try { // find native bbox box = element.node.getBBox() } catch(e) { if(element instanceof SVG.Shape){ var clone = element.clone().addTo(SVG.parser.draw) box = clone.bbox() clone.remove() }else{ box = { x: element.node.clientLeft , y: element.node.clientTop , width: element.node.clientWidth , height: element.node.clientHeight } } } // plain x and y this.x = box.x this.y = box.y // plain width and height this.width = box.width this.height = box.height } // add center, right and bottom fullBox(this) } // Define Parent , parent: SVG.Element // Constructor , construct: { // Get bounding box bbox: function() { return new SVG.BBox(this) } } }) SVG.TBox = SVG.invent({ // Initialize create: function(element) { // get values if element is given if (element) { var t = element.ctm().extract() , box = element.bbox() // width and height including transformations this.width = box.width * t.scaleX this.height = box.height * t.scaleY // x and y including transformations this.x = box.x + t.x this.y = box.y + t.y } // add center, right and bottom fullBox(this) } // Define Parent , parent: SVG.Element // Constructor , construct: { // Get transformed bounding box tbox: function() { return new SVG.TBox(this) } } }) SVG.RBox = SVG.invent({ // Initialize create: function(element) { if (element) { var e = element.doc().parent() , box = element.node.getBoundingClientRect() , zoom = 1 // get screen offset this.x = box.left this.y = box.top // subtract parent offset this.x -= e.offsetLeft this.y -= e.offsetTop while (e = e.offsetParent) { this.x -= e.offsetLeft this.y -= e.offsetTop } // calculate cumulative zoom from svg documents e = element while (e.parent && (e = e.parent())) { if (e.viewbox) { zoom *= e.viewbox().zoom this.x -= e.x() || 0 this.y -= e.y() || 0 } } // recalculate viewbox distortion this.width = box.width /= zoom this.height = box.height /= zoom } // add center, right and bottom fullBox(this) // offset by window scroll position, because getBoundingClientRect changes when window is scrolled this.x += window.pageXOffset this.y += window.pageYOffset } // define Parent , parent: SVG.Element // Constructor , construct: { // Get rect box rbox: function() { return new SVG.RBox(this) } } }) // Add universal merge method ;[SVG.BBox, SVG.TBox, SVG.RBox].forEach(function(c) { SVG.extend(c, { // Merge rect box with another, return a new instance merge: function(box) { var b = new c() // merge boxes b.x = Math.min(this.x, box.x) b.y = Math.min(this.y, box.y) b.width = Math.max(this.x + this.width, box.x + box.width) - b.x b.height = Math.max(this.y + this.height, box.y + box.height) - b.y return fullBox(b) } }) })