SVG.Box = SVG.invent({ create: function(x, y, width, height) { if (typeof x == 'object' && !(x instanceof SVG.Element)) { // chromes getBoundingClientRect has no x and y property return SVG.Box.call(this, x.left != null ? x.left : x.x , x.top != null ? x.top : x.y, x.width, x.height) } else if (arguments.length == 4) { this.x = x this.y = y this.width = width this.height = height } // add center, right, bottom... fullBox(this) } , extend: { // Merge rect box with another, return a new instance merge: function(box) { var b = new this.constructor() // 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) } , transform: function(m) { var xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, p, bbox var pts = [ new SVG.Point(this.x, this.y), new SVG.Point(this.x2, this.y), new SVG.Point(this.x, this.y2), new SVG.Point(this.x2, this.y2) ] pts.forEach(function(p) { p = p.transform(m) xMin = Math.min(xMin,p.x) xMax = Math.max(xMax,p.x) yMin = Math.min(yMin,p.y) yMax = Math.max(yMax,p.y) }) bbox = new this.constructor() bbox.x = xMin bbox.width = xMax-xMin bbox.y = yMin bbox.height = yMax-yMin fullBox(bbox) return bbox } } }) SVG.BBox = SVG.invent({ // Initialize create: function(element) { SVG.Box.apply(this, [].slice.call(arguments)) // get values if element is given if (element instanceof SVG.Element) { var box // yes this is ugly, but Firefox can be a pain when it comes to elements that are not yet rendered try { if (!document.documentElement.contains){ // This is IE - it does not support contains() for top-level SVGs var topParent = element.node while (topParent.parentNode){ topParent = topParent.parentNode } if (topParent != document) throw new Exception('Element not in the dom') } else { // the element is NOT in the dom, throw error if(!document.documentElement.contains(element.node)) throw new Exception('Element not in the dom') } // find native bbox box = element.node.getBBox() } catch(e) { if(element instanceof SVG.Shape){ var clone = element.clone(SVG.parser.draw.instance).show() box = clone.node.getBBox() clone.remove() }else{ box = { x: element.node.clientLeft , y: element.node.clientTop , width: element.node.clientWidth , height: element.node.clientHeight } } } SVG.Box.call(this, box) } } // Define ancestor , inherit: SVG.Box // Define Parent , parent: SVG.Element // Constructor , construct: { // Get bounding box bbox: function() { return new SVG.BBox(this) } } }) SVG.BBox.prototype.constructor = SVG.BBox SVG.extend(SVG.Element, { tbox: function(){ console.warn('Use of TBox is deprecated and mapped to RBox. Use .rbox() instead.') return this.rbox(this.doc()) } }) SVG.RBox = SVG.invent({ // Initialize create: function(element) { SVG.Box.apply(this, [].slice.call(arguments)) if (element instanceof SVG.Element) { SVG.Box.call(this, element.node.getBoundingClientRect()) } } , inherit: SVG.Box // define Parent , parent: SVG.Element , extend: { addOffset: function() { // offset by window scroll position, because getBoundingClientRect changes when window is scrolled this.x += window.pageXOffset this.y += window.pageYOffset return this } } // Constructor , construct: { // Get rect box rbox: function(el) { if (el) return new SVG.RBox(this).transform(el.screenCTM().inverse()) return new SVG.RBox(this).addOffset() } } }) SVG.RBox.prototype.constructor = SVG.RBox