jquery.positionBy.js 8.4 KB

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