diff --git a/src/position/position.js b/src/position/position.js index 3bca8b0ba7..c0b6583d91 100644 --- a/src/position/position.js +++ b/src/position/position.js @@ -39,7 +39,52 @@ angular.module('ui.bootstrap.position', []) return offsetParent || docDomEl; }; - return { + /** + * Temporarily swap CSS properties in, and perform an operation. Based on jQuery's + * internal 'jQuery.swap' routine. + * + * @param element + * @param css object containing css property keys and values to swap in + * @callback operation to perform while the CSS properties are swapped in + */ + var swapCss = function (element, css, callback) { + var ret, prop, old = {}; + element = angular.element(element); + args = Array.prototype.slice.call(arguments, 3); + + for (prop in css) { + old[prop] = element[0].style[prop]; + element[0].style[prop] = css[prop]; + } + + ret = callback.apply(element, args); + + for (prop in css) { + element[0].style[prop] = old[prop]; + } + + return ret; + }; + + var swapDisplay = /^(none|table(?!-c[ea]).+)/; + var cssShow = { + position: 'absolute', + visibility: 'hidden', + display: 'block' + }; + + /** + * Return offsetWidth or offsetHeight of an element. + */ + var widthOrHeight = function(element, name) { + if (typeof element === 'string') { + name = element; + element = this; + } + return element[0][('offset' + name.charAt(0).toUpperCase() + name.substr(1))]; + }; + + var service = { /** * Provides read-only equivalent of jQuery's position function: * http://api.jquery.com/position/ @@ -76,4 +121,19 @@ angular.module('ui.bootstrap.position', []) }; } }; + + angular.forEach(['width', 'height'], function(name) { + service[name] = function(element, value) { + element = angular.element(element); + if (arguments.length > 1) { + return element.css(name, value); + } + if (element[0].offsetWidth === 0 && swapDisplay.test(getStyle(element[0], 'display'))) { + return swapCss(element, cssShow, widthOrHeight, name); + } + return widthOrHeight(element, name); + }; + }); + + return service; }]); diff --git a/src/position/test/position.spec.js b/src/position/test/position.spec.js new file mode 100644 index 0000000000..b6b856caeb --- /dev/null +++ b/src/position/test/position.spec.js @@ -0,0 +1,70 @@ +describe('position service', function () { + + var $position, $document, $body, elem; + beforeEach(module('ui.bootstrap.position')); + beforeEach(inject(function(_$position_, _$document_) { + $position = _$position_; + $document = _$document_; + $body = $document.find('body'); + })); + + beforeEach(function() { + elem = angular.element('

Miscellaneous content

'); + ['height', 'width'].forEach(function(name) { + ['', 'min-', 'max-'].forEach(function(prefix) { + elem.css(prefix+name, '100px'); + }); + }); + $body.append(elem); + }); + afterEach(function() { + elem.remove(); + }); + + describe('$position.width', function() { + it('will be a function', function() { + expect(typeof $position.width).toEqual('function'); + }); + + it('will return correct dimensions with `display: none`', function() { + elem.css('display', 'none'); + expect($position.width(elem)).toEqual(100); + expect(elem[0].offsetWidth).toBe(0); + }); + + it('will return correct dimensions with `visibility: hidden`', function() { + elem.css({'visibility': 'hidden', 'display': 'none'}); + expect($position.width(elem)).toEqual(100); + expect(elem[0].offsetWidth).toBe(0); + }); + + it('will return correct dimensions with `display: block`', function() { + expect($position.width(elem)).toEqual(100); + expect(elem[0].offsetWidth).toBe(100); + }); + }); + + describe('$position.height', function() { + it('will be a function', function() { + expect(typeof $position.height).toEqual('function'); + }); + + it('will return correct dimensions with `display: none`', function() { + elem.css('display', 'none'); + expect($position.height(elem)).toEqual(100); + expect(elem[0].offsetHeight).toBe(0); + }); + + it('will return correct dimensions with `visibility: hidden`', function() { + elem.css({'visibility': 'hidden', 'display': 'none'}); + expect($position.height(elem)).toEqual(100); + expect(elem[0].offsetHeight).toBe(0); + }); + + it('will return correct dimensions with `display: block`', function() { + expect($position.height(elem)).toEqual(100); + expect(elem[0].offsetHeight).toBe(100); + }); + }); + +});