dimensions.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. 'use strict';
  2. angular.module('mgcrea.ngStrap.helpers.dimensions', [])
  3. .factory('dimensions', function () {
  4. var fn = {};
  5. /**
  6. * Test the element nodeName
  7. * @param element
  8. * @param name
  9. */
  10. var nodeName = fn.nodeName = function (element, name) {
  11. return element.nodeName && element.nodeName.toLowerCase() === name.toLowerCase();
  12. };
  13. /**
  14. * Returns the element computed style
  15. * @param element
  16. * @param prop
  17. * @param extra
  18. */
  19. fn.css = function (element, prop, extra) {
  20. var value;
  21. if (element.currentStyle) { // IE
  22. value = element.currentStyle[prop];
  23. } else if (window.getComputedStyle) {
  24. value = window.getComputedStyle(element)[prop];
  25. } else {
  26. value = element.style[prop];
  27. }
  28. return extra === true ? parseFloat(value) || 0 : value;
  29. };
  30. /**
  31. * Provides read-only equivalent of jQuery's offset function:
  32. * @required-by bootstrap-tooltip, bootstrap-affix
  33. * @url http://api.jquery.com/offset/
  34. * @param element
  35. */
  36. fn.offset = function (element) {
  37. var boxRect = element.getBoundingClientRect();
  38. var docElement = element.ownerDocument;
  39. return {
  40. width: boxRect.width || element.offsetWidth,
  41. height: boxRect.height || element.offsetHeight,
  42. top: boxRect.top + (window.pageYOffset || docElement.documentElement.scrollTop) - (docElement.documentElement.clientTop || 0),
  43. left: boxRect.left + (window.pageXOffset || docElement.documentElement.scrollLeft) - (docElement.documentElement.clientLeft || 0)
  44. };
  45. };
  46. /**
  47. * Provides set equivalent of jQuery's offset function:
  48. * @required-by bootstrap-tooltip
  49. * @url http://api.jquery.com/offset/
  50. * @param element
  51. * @param options
  52. * @param i
  53. */
  54. fn.setOffset = function (element, options, i) {
  55. var curPosition;
  56. var curLeft;
  57. var curCSSTop;
  58. var curTop;
  59. var curOffset;
  60. var curCSSLeft;
  61. var calculatePosition;
  62. var position = fn.css(element, 'position');
  63. var curElem = angular.element(element);
  64. var props = {};
  65. // Set position first, in-case top/left are set even on static elem
  66. if (position === 'static') {
  67. element.style.position = 'relative';
  68. }
  69. curOffset = fn.offset(element);
  70. curCSSTop = fn.css(element, 'top');
  71. curCSSLeft = fn.css(element, 'left');
  72. calculatePosition = (position === 'absolute' || position === 'fixed') &&
  73. (curCSSTop + curCSSLeft).indexOf('auto') > -1;
  74. // Need to be able to calculate position if either
  75. // top or left is auto and position is either absolute or fixed
  76. if (calculatePosition) {
  77. curPosition = fn.position(element);
  78. curTop = curPosition.top;
  79. curLeft = curPosition.left;
  80. } else {
  81. curTop = parseFloat(curCSSTop) || 0;
  82. curLeft = parseFloat(curCSSLeft) || 0;
  83. }
  84. if (angular.isFunction(options)) {
  85. options = options.call(element, i, curOffset);
  86. }
  87. if (options.top !== null) {
  88. props.top = (options.top - curOffset.top) + curTop;
  89. }
  90. if (options.left !== null) {
  91. props.left = (options.left - curOffset.left) + curLeft;
  92. }
  93. if ('using' in options) {
  94. options.using.call(curElem, props);
  95. } else {
  96. curElem.css({
  97. top: props.top + 'px',
  98. left: props.left + 'px'
  99. });
  100. }
  101. };
  102. /**
  103. * Provides read-only equivalent of jQuery's position function
  104. * @required-by bootstrap-tooltip, bootstrap-affix
  105. * @url http://api.jquery.com/offset/
  106. * @param element
  107. */
  108. fn.position = function (element) {
  109. var offsetParentRect = {top: 0, left: 0};
  110. var offsetParentEl;
  111. var offset;
  112. // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
  113. if (fn.css(element, 'position') === 'fixed') {
  114. // We assume that getBoundingClientRect is available when computed position is fixed
  115. offset = element.getBoundingClientRect();
  116. } else {
  117. // Get *real* offsetParentEl
  118. offsetParentEl = offsetParentElement(element);
  119. // Get correct offsets
  120. offset = fn.offset(element);
  121. if (!nodeName(offsetParentEl, 'html')) {
  122. offsetParentRect = fn.offset(offsetParentEl);
  123. }
  124. // Add offsetParent borders
  125. offsetParentRect.top += fn.css(offsetParentEl, 'borderTopWidth', true);
  126. offsetParentRect.left += fn.css(offsetParentEl, 'borderLeftWidth', true);
  127. }
  128. // Subtract parent offsets and element margins
  129. return {
  130. width: element.offsetWidth,
  131. height: element.offsetHeight,
  132. top: offset.top - offsetParentRect.top - fn.css(element, 'marginTop', true),
  133. left: offset.left - offsetParentRect.left - fn.css(element, 'marginLeft', true)
  134. };
  135. };
  136. /**
  137. * Returns the closest, non-statically positioned offsetParent of a given element
  138. * @required-by fn.position
  139. * @param element
  140. */
  141. function offsetParentElement (element) {
  142. var docElement = element.ownerDocument;
  143. var offsetParent = element.offsetParent || docElement;
  144. if (nodeName(offsetParent, '#document')) return docElement.documentElement;
  145. while (offsetParent && !nodeName(offsetParent, 'html') && fn.css(offsetParent, 'position') === 'static') {
  146. offsetParent = offsetParent.offsetParent;
  147. }
  148. return offsetParent || docElement.documentElement;
  149. }
  150. /**
  151. * Provides equivalent of jQuery's height function
  152. * @required-by bootstrap-affix
  153. * @url http://api.jquery.com/height/
  154. * @param element
  155. * @param outer
  156. */
  157. fn.height = function (element, outer) {
  158. var value = element.offsetHeight;
  159. if (outer) {
  160. value += fn.css(element, 'marginTop', true) + fn.css(element, 'marginBottom', true);
  161. } else {
  162. value -= fn.css(element, 'paddingTop', true) + fn.css(element, 'paddingBottom', true) + fn.css(element, 'borderTopWidth', true) + fn.css(element, 'borderBottomWidth', true);
  163. }
  164. return value;
  165. };
  166. /**
  167. * Provides equivalent of jQuery's width function
  168. * @required-by bootstrap-affix
  169. * @url http://api.jquery.com/width/
  170. * @param element
  171. * @param outer
  172. */
  173. fn.width = function (element, outer) {
  174. var value = element.offsetWidth;
  175. if (outer) {
  176. value += fn.css(element, 'marginLeft', true) + fn.css(element, 'marginRight', true);
  177. } else {
  178. value -= fn.css(element, 'paddingLeft', true) + fn.css(element, 'paddingRight', true) + fn.css(element, 'borderLeftWidth', true) + fn.css(element, 'borderRightWidth', true);
  179. }
  180. return value;
  181. };
  182. return fn;
  183. });