jquery.positionBy.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /*
  2. * positionBy 1.0.7 (2008-01-29)
  3. *
  4. * Copyright (c) 2006,2007 Jonathan Sharp (http://jdsharp.us)
  5. * Dual licensed under the MIT (MIT-LICENSE.txt)
  6. * and GPL (GPL-LICENSE.txt) licenses.
  7. *
  8. * http://jdsharp.us/
  9. *
  10. * Built upon jQuery 1.2.2 (http://jquery.com)
  11. * This also requires the jQuery dimensions plugin
  12. *
  13. *
  14. * @@ Modified by dflaven for handling the positioning of the iTop popup menus @@
  15. *
  16. */
  17. (function($){
  18. /**
  19. * This function centers an absolutely positioned element
  20. */
  21. /*
  22. $.fn.positionCenter = function(offsetLeft, offsetTop) {
  23. var offsetLeft = offsetLeft || 1;
  24. var offsetTop = offsetTop || 1;
  25. var ww = $(window).width();
  26. var wh = $(window).height();
  27. var sl = $(window).scrollLeft();
  28. var st = $(window).scrollTop();
  29. return this.each(function() {
  30. var $t = $(this);
  31. // If we are not visible we have to display our element (with a negative position offscreen)
  32. var left = Math.round( ( ww - $t.outerWidth() ) / 2 );
  33. if ( left < 0 ) {
  34. left = 0;
  35. } else {
  36. left *= offsetLeft;
  37. }
  38. left += sl;
  39. var top = Math.round( ( wh - $t.outerHeight() ) / 2 );
  40. if ( top < 0 ) {
  41. top = 0;
  42. } else {
  43. top *= offsetTop;
  44. }
  45. top += st;
  46. $(this).parents().each(function() {
  47. var $this = $(this);
  48. if ( $this.css('position') != 'static' ) {
  49. var o = $this.offset();
  50. left += -o.left;
  51. top += -o.top;
  52. return false;
  53. }
  54. });
  55. $t.css({left: left, top: top});
  56. });
  57. };
  58. */
  59. // Our range object is used in calculating positions
  60. var Range = function(x1, y1, x2, y2) {
  61. this.x1 = x1; this.x2 = x2;
  62. this.y1 = y1; this.y2 = y2;
  63. };
  64. Range.prototype.contains = function(range) {
  65. return (this.x1 <= range.x1 && range.x2 <= this.x2)
  66. &&
  67. (this.y1 <= range.y1 && range.y2 <= this.y2);
  68. };
  69. Range.prototype.transform = function(x, y) {
  70. return new Range(this.x1 + x, this.y1 + y, this.x2 + x, this.y2 + y);
  71. };
  72. $.fn.positionBy = function(args) {
  73. var date1 = new Date();
  74. if ( this.length == 0 ) {
  75. return this;
  76. }
  77. var args = $.extend({ // The target element to position us relative to
  78. target: null,
  79. // The target's corner, possible values 0-3
  80. targetPos: null,
  81. // The element's corner, possible values 0-3
  82. elementPos: null,
  83. // A raw x,y coordinate
  84. x: null,
  85. y: null,
  86. // Pass in an array of positions that are valid 0-15
  87. positions: null,
  88. // Add the final position class to the element (eg. positionBy0 through positionBy3, positionBy15)
  89. addClass: false,
  90. // Force our element to be at the location we specified (don't try to auto position it)
  91. force: false,
  92. // The element that we will make sure our element doesn't go outside of
  93. container: window,
  94. // Should the element be hidden after positioning?
  95. hideAfterPosition: false
  96. }, args);
  97. if ( args.x != null ) {
  98. var tLeft = args.x;
  99. var tTop = args.y;
  100. var tWidth = 0;
  101. var tHeight = 0;
  102. // Position in relation to an element
  103. } else {
  104. var $target = $( $( args.target )[0] );
  105. var tWidth = $target.outerWidth();
  106. var tHeight = $target.outerHeight();
  107. var tOffset = $target.offset();
  108. var tLeft = tOffset.left;
  109. var tTop = tOffset.top;
  110. }
  111. // Our target right, bottom coord
  112. var tRight = tLeft + tWidth;
  113. var tBottom = tTop + tHeight;
  114. return this.each(function() {
  115. var $element = $( this );
  116. // Position our element in the top left so we can grab its width without triggering scrollbars
  117. if ( !$element.is(':visible') ) {
  118. $element.css({ left: -3000,
  119. top: -3000
  120. })
  121. .show();
  122. }
  123. var eWidth = $element.outerWidth();
  124. var eHeight = $element.outerHeight();
  125. // Holds x1,y1,x2,y2 coordinates for a position in relation to our target element
  126. var position = [];
  127. // Holds a list of alternate positions to try if this one is not in the browser viewport
  128. var next = [];
  129. // Our Positions via ASCII ART
  130. /*
  131. 8 9 10 11
  132. +------------+
  133. 7 | 15 12 | 0
  134. | |
  135. 6 | 14 13 | 1
  136. +------------+
  137. 5 4 3 2
  138. */
  139. position[0] = new Range(tRight, tTop, tRight + eWidth, tTop + eHeight);
  140. next[0] = [1,7,4];
  141. position[1] = new Range(tRight, tBottom - eHeight, tRight + eWidth, tBottom);
  142. next[1] = [0,6,4];
  143. position[2] = new Range(tRight, tBottom, tRight + eWidth, tBottom + eHeight);
  144. next[2] = [1,3,10];
  145. position[3] = new Range(tRight - eWidth, tBottom, tRight, tBottom + eHeight);
  146. next[3] = [1,6,10];
  147. position[4] = new Range(tLeft, tBottom, tLeft + eWidth, tBottom + eHeight);
  148. next[4] = [1,6,9];
  149. position[5] = new Range(tLeft - eWidth, tBottom, tLeft, tBottom + eHeight);
  150. next[5] = [6,4,9];
  151. position[6] = new Range(tLeft - eWidth, tBottom - eHeight, tLeft, tBottom);
  152. next[6] = [7,1,4];
  153. position[7] = new Range(tLeft - eWidth, tTop, tLeft, tTop + eHeight);
  154. next[7] = [6,0,4];
  155. position[8] = new Range(tLeft - eWidth, tTop - eHeight, tLeft, tTop);
  156. next[8] = [7,9,4];
  157. position[9] = new Range(tLeft, tTop - eHeight, tLeft + eWidth, tTop);
  158. next[9] = [0,7,4];
  159. position[10]= new Range(tRight - eWidth, tTop - eHeight, tRight, tTop);
  160. next[10] = [0,7,3];
  161. position[11]= new Range(tRight, tTop - eHeight, tRight + eWidth, tTop);
  162. next[11] = [0,10,3];
  163. position[12]= new Range(tRight - eWidth, tTop, tRight, tTop + eHeight);
  164. next[12] = [13,7,10];
  165. position[13]= new Range(tRight - eWidth, tBottom - eHeight, tRight, tBottom);
  166. next[13] = [12,6,3];
  167. position[14]= new Range(tLeft, tBottom - eHeight, tLeft + eWidth, tBottom);
  168. next[14] = [15,1,4];
  169. position[15]= new Range(tLeft, tTop, tLeft + eWidth, tTop + eHeight);
  170. next[15] = [14,0,9];
  171. // @@ Added by dflaven
  172. position[16]= new Range(tRight - eWidth, tBottom, tRight, tBottom + eHeight);
  173. next[16] = [3,10];
  174. // @@End of modification
  175. if ( args.positions !== null ) {
  176. var pos = args.positions[0];
  177. } else if ( args.targetPos != null && args.elementPos != null ) {
  178. var pos = [];
  179. pos[0] = [];
  180. pos[0][0] = 15;
  181. pos[0][1] = 7;
  182. pos[0][2] = 8;
  183. pos[0][3] = 9;
  184. pos[1] = [];
  185. pos[1][0] = 0;
  186. pos[1][1] = 12;
  187. pos[1][2] = 10;
  188. pos[1][3] = 11;
  189. pos[2] = [];
  190. pos[2][0] = 2;
  191. pos[2][1] = 3;
  192. pos[2][2] = 13;
  193. pos[2][3] = 1;
  194. pos[3] = [];
  195. pos[3][0] = 4;
  196. pos[3][1] = 5;
  197. pos[3][2] = 6;
  198. pos[3][3] = 14;
  199. // @@ Added by dflaven
  200. pos[4] = [];
  201. pos[4][0] = 16;
  202. pos[4][1] = 16;
  203. pos[4][2] = 16;
  204. pos[4][3] = 16;
  205. // @@ End of modification
  206. var pos = pos[args.targetPos][args.elementPos];
  207. }
  208. var ePos = position[pos];
  209. var fPos = pos;
  210. if ( !args.force ) {
  211. // TODO: Do the args.container
  212. // window width & scroll offset
  213. $window = $( window );
  214. var sx = $window.scrollLeft();
  215. var sy = $window.scrollTop();
  216. // TODO: Look at innerWidth & innerHeight
  217. var container = new Range( sx, sy, sx + $window.width(), sy + $window.height() );
  218. // If we are outside of our viewport, see if we are outside vertically or horizontally and push onto the stack
  219. var stack;
  220. if ( args.positions ) {
  221. stack = args.positions;
  222. } else {
  223. stack = [pos];
  224. }
  225. var test = []; // Keeps track of our positions we already tried
  226. while ( stack.length > 0 ) {
  227. var p = stack.shift();
  228. if ( test[p] ) {
  229. continue;
  230. }
  231. test[p] = true;
  232. // If our current position is not within the viewport (eg. window)
  233. // add the next suggested position
  234. if ( !container.contains(position[p]) ) {
  235. if ( args.positions === null ) {
  236. stack = jQuery.merge( stack, next[p] );
  237. }
  238. } else {
  239. ePos = position[p];
  240. break;
  241. }
  242. }
  243. }
  244. // + TODO: Determine if we are going to use absolute left, top, bottom, right
  245. // positions relative to our target
  246. // Take into account any absolute or fixed positioning
  247. // to 'normalize' our coordinates
  248. $element.parents().each(function() {
  249. var $this = $(this);
  250. if ( $this.css('position') != 'static' ) {
  251. var abs = $this.offset();
  252. ePos = ePos.transform( -abs.left, -abs.top );
  253. return false;
  254. }
  255. });
  256. // Finally position our element
  257. var css = { left: ePos.x1, top: ePos.y1 };
  258. if ( args.hideAfterPosition ) {
  259. css['display'] = 'none';
  260. }
  261. $element.css( css );
  262. if ( args.addClass ) {
  263. $element.removeClass( 'positionBy0 positionBy1 positionBy2 positionBy3 positionBy4 positionBy5 '
  264. + 'positionBy6 positionBy7 positionBy8 positionBy9 positionBy10 positionBy11 '
  265. + 'positionBy12 positionBy13 positionBy14 positionBy15')
  266. .addClass('positionBy' + p);
  267. }
  268. });
  269. };
  270. })(jQuery);