jquery.grid.min.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /**
  2. * Grid()
  3. * by Bachir Soussi-Chiadmi www.g-u-i.net
  4. * version 2.0
  5. */
  6. function Grid(ctx, settings){
  7. var _defaults = {
  8. children:'', // tell which dom elmt to target, by default $ctx.children();
  9. cell_w: 20, cell_h: 20,
  10. cell_padding:{t:5,r:5,b:5,l:5}, // with or without padding cell width and height are the same
  11. padding:{t:5,r:5,b:5,l:5}, // padding (in cell) inside the grid where not placed element
  12. gouttiere: 5, // space between cells h and v
  13. lines: 20, columns: 20,// how much line and columns grid has
  14. grille: false, // see the grid with cell's free space infos
  15. groove: 0, // add groove attitude (randomize placement)
  16. positioning_mode:'random', // positioning_mode play with with the position on items
  17. max_distance:0, // distance max between prev item and current item. O = no limit
  18. min_distance:0, // distance min between prev item and current item. O = no limit
  19. },
  20. $ctx = ctx,
  21. _cells = new Array(),
  22. _positions = new Array(),
  23. _grid = this;
  24. this.ctx = $ctx;
  25. this.settings = $.extend({}, _defaults, settings);
  26. // $.log('this.settings', this.settings);
  27. // set dom grille visualsation context if enabled
  28. if (this.settings.grille) {
  29. $ctx.append('<div class="grille"><div>');
  30. $('.grille', $ctx).css({
  31. 'width': '100%',
  32. 'height': '100%',
  33. 'position': 'relative'
  34. });
  35. }
  36. // create all empty cells
  37. for (var l = 0; l < this.settings.lines; l++) {
  38. _cells[l] = new Array();
  39. for (var c = 0; c < this.settings.columns; c++) {
  40. _cells[l][c] = new Cell(this, c, l);
  41. };
  42. };
  43. function _setPrevCell(){
  44. // _grid.prev_cell = false;
  45. switch(_grid.settings.positioning_mode){
  46. case 'lefttop': // add items from top left to bootm rif-ght
  47. _grid.prev_cell = _cells[0][0];
  48. break;
  49. case 'center': // add items from center of grid to both side randomly
  50. _grid.prev_cell = _cells[Math.floor(_grid.settings.lines*.5)][Math.floor(_grid.settings.columns*.5)];
  51. break;
  52. case 'borders': // add items from center of grid to both side randomly
  53. var side = Math.floor(Math.random()*4);
  54. $.log('side = '+side);
  55. switch(side){
  56. case 0 :
  57. _grid.prev_cell = _cells[0][Math.floor(_grid.settings.columns*Math.random())];
  58. break;
  59. case 1 :
  60. _grid.prev_cell = _cells[_grid.settings.lines-1][Math.floor(_grid.settings.columns*Math.random())];
  61. break;
  62. case 2 :
  63. _grid.prev_cell = _cells[Math.floor(_grid.settings.lines*Math.random())][0];
  64. break;
  65. case 3 :
  66. _grid.prev_cell = _cells[Math.floor(_grid.settings.lines*Math.random())][_grid.settings.columns-1];
  67. break;
  68. }
  69. break;
  70. case 'random': // add items randomly
  71. default:
  72. _grid.prev_cell = _cells[Math.floor(_grid.settings.lines*Math.random())][Math.floor(_grid.settings.columns*Math.random())];
  73. break;
  74. }
  75. }
  76. _setPrevCell();
  77. /**
  78. * placeTargets()
  79. */
  80. function _placeTargets(){
  81. // parse targets
  82. var $targets = _grid.settings.children != '' ? $(_grid.settings.children+':not(.grid-placed)', $ctx) : $ctx.children(':not(.grid-placed)');
  83. $targets.each(function() {
  84. var $this = $(this),
  85. // get the width and height ( ! on cell unit ! how much cells it takes ? ) of item to be positioned
  86. this_cells_w = Math.ceil($this.width() / (_grid.settings.cell_w + _grid.settings.gouttiere)),
  87. this_cells_h = Math.ceil($this.height() / (_grid.settings.cell_h + _grid.settings.gouttiere)),
  88. // get free cells function of width and height of item
  89. free_cells = getFreeCells(this_cells_w, this_cells_h);
  90. if(free_cells.length > 0){
  91. // positioning mode, to change this mode uncomment/comment lines after description
  92. switch(_grid.settings.positioning_mode){
  93. case 'lefttop': // add items from top left to bootm rif-ght
  94. var cell_num = 0,
  95. cell = free_cells[cell_num];
  96. break;
  97. default: // add items from center of grid to both side randomly
  98. // var cell_num = Math.floor((free_cells.length-1)*0.5 + (-2+Math.random()*4));
  99. // cell_num = cell_num > free_cells.length-1 ? free_cells.length-1 : ( cell_num < 0 ? 0 : cell_num );
  100. // var cell = free_cells[cell_num];
  101. var cell_num = Math.round(Math.random() * (free_cells.length - 1)),
  102. cell = free_cells[cell_num];
  103. break;
  104. }
  105. _grid.prev_cell = cell;
  106. var line_limit_loop = cell.row() + (this_cells_h),
  107. column_limit_loop = cell.column() + (this_cells_w);
  108. for (var l = cell.row(); l < line_limit_loop; l++) {
  109. for (var c = cell.column(); c < column_limit_loop; c++) {
  110. var temp_cell = _cells[l][c];
  111. temp_cell.setFull();
  112. };
  113. };
  114. // get tp and left coordinates
  115. // should be directly recorded in the cell object
  116. var top = cell.row() * (_grid.settings.cell_h + _grid.settings.gouttiere),
  117. left = cell.column() * (_grid.settings.cell_w + _grid.settings.gouttiere),
  118. groove = _grid.settings.groove;
  119. // set groove ;)
  120. top = !groove ? top: top + Math.round(Math.random() * groove - groove / 2);
  121. left = !groove ? left: left + Math.round(Math.random() * groove - groove / 2);
  122. // apply ccs position
  123. top += _grid.settings.cell_padding.t;
  124. left += _grid.settings.cell_padding.l;
  125. var event = jQuery.Event("grid_positioned");
  126. event.top = top;
  127. event.left = left;
  128. $this.css({
  129. 'top': top,
  130. 'left': left
  131. }).addClass('grid-positioned').trigger(event);
  132. }else{
  133. $.log('GRID no free cells left');
  134. // if no free cells exist
  135. $this.css({
  136. 'border': '2px solid red',
  137. 'opacity': 0.2
  138. }).trigger('grid_removed');//.remove();
  139. }
  140. $this.addClass('grid-placed').trigger('grid_placed');
  141. }); // end children each
  142. }
  143. /**
  144. * getFreeCells()
  145. *
  146. * @ width in cells
  147. * @ height in cells
  148. *
  149. * return array of cell objects
  150. *
  151. */
  152. function getFreeCells(this_cells_w, this_cells_h){
  153. var free_cells = new Array(),
  154. padding = _grid.settings.padding,
  155. max_dist = _grid.settings.max_distance,
  156. min_dist = _grid.settings.min_distance,
  157. prev_cell = _grid.prev_cell;
  158. // $.log()
  159. for (var l = 0; l < _grid.settings.lines; l++) {
  160. for (var c = 0; c < _grid.settings.columns; c++) {
  161. var cell = _cells[l][c];
  162. /*
  163. TODO about the max_dist keep in mind that all element will not always be 1 cell elemnts !!
  164. */
  165. // here is a big stuff of conditionning
  166. // we check a lot of things
  167. // 1 - basicly if the cell is free
  168. // 2 - if the cell is under the padding limits
  169. // 3 - if she has enought space arround her
  170. // 4 - if we got a prev cell
  171. // 4.1 - if we got a max_dist != 0
  172. // 4.1.1 - if the cell is under the max limits
  173. // 4.2 - if we got a min_dist != 0
  174. // 4.2.1 - if the cell is behind the min limits
  175. if (cell.isFree() // if cell is free
  176. && (cell.row() > padding.t && cell.row() < _grid.settings.lines - padding.b
  177. && cell.column() > padding.l && cell.column() < _grid.settings.columns - padding.r
  178. )
  179. && (
  180. (cell.getAround().right + 1) >= this_cells_w // if enought free space on the right
  181. && (cell.getAround().bottom + 1) >= this_cells_h // if enought free space on the bottom
  182. )
  183. && ( !_grid.prev_cell // else if _grid.prev_cell undefined it's true
  184. || ( // check max_dist AND min_dist
  185. (!max_dist // if max_distance is 0 return (stop condition) true
  186. || ( cell.row() < prev_cell.row()+max_dist && cell.row() > prev_cell.row()-max_dist
  187. && cell.column() < prev_cell.column()+max_dist && cell.column() > prev_cell.column()-max_dist )// if we got max_distance and prev_cell we check the distance
  188. )
  189. &&
  190. (!min_dist // if min_distance is 0 return (stop condition) true
  191. || ( (cell.row() > prev_cell.row()+min_dist || cell.row() < prev_cell.row()-min_dist)
  192. || (cell.column() > prev_cell.column()+min_dist || cell.column() < prev_cell.column()-min_dist) )// if we got min_distance and prev_cell we check the distance
  193. )
  194. )
  195. )
  196. ) { // true of first big stuff if
  197. var good = true;
  198. for (var i = 1; i < this_cells_w; i++) { // for each free cells needed on right
  199. var temp_cell = _cells[l][c + i];
  200. if ((temp_cell.getAround().bottom + 1) < this_cells_h) { // if enought free space on bottom
  201. good = false;
  202. break;
  203. }
  204. };
  205. if (good) free_cells.push(cell);
  206. }
  207. };
  208. };
  209. return free_cells;
  210. }
  211. /**
  212. * Cell()
  213. */
  214. function Cell(g, c, r) {
  215. var _grid = g,
  216. _column = c,
  217. _row = r,
  218. _free = true,
  219. _free_cell_right = _grid.settings.columns - 1 - c,
  220. _free_cell_bottom = _grid.settings.lines - 1 - l;
  221. if (_grid.settings.grille) {
  222. var _table = '<table>';
  223. _table += '<tr><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr>';
  224. // _table += '<tr><td>&nbsp;</td><td class="state">'+_free+'</td><td class="fr">'+_free_cell_right+'</td></tr>';
  225. _table += '<tr><td>&nbsp;</td><td>&nbsp;</td><td class="fr">' + _free_cell_right + '</td></tr>';
  226. _table += '<tr><td>&nbsp;</td><td class="fb">' + _free_cell_bottom + '</td><td>&nbsp;</td></tr>';
  227. _table += '</table>';
  228. _grid.ctx.find('.grille').append('<div class="cell-' + l + '-' + c + '">' + _table + '<div>');
  229. var _dom = $('.cell-' + l + '-' + c, _grid.ctx.find('.grille'));
  230. _dom.css({
  231. 'position': 'absolute',
  232. 'top': _row * (_grid.settings.cell_h + _grid.settings.gouttiere) + _grid.settings.cell_padding.t,
  233. 'left': _column * (_grid.settings.cell_w + _grid.settings.gouttiere) + _grid.settings.cell_padding.l,
  234. 'width': _grid.settings.cell_w,
  235. 'height': _grid.settings.cell_h,
  236. 'backgroundColor': '#00FF00',
  237. 'opacity': 0.6
  238. });
  239. // $('td', _dom).css({
  240. // 'fontSize': '8px'
  241. // });
  242. }
  243. this.isFree = function() { return _free; };
  244. this.getPos = function() { return { c: _column, l: _row }; };
  245. this.column = function() { return _column; };
  246. this.row = function() { return _row; };
  247. this.getAround = function() { return { right: _free_cell_right, bottom: _free_cell_bottom }; };
  248. this.setFull = function() {
  249. _free = false;
  250. _free_cell_bottom = -1;
  251. _free_cell_right = -1;
  252. if (_grid.settings.grille) {
  253. _dom.css({
  254. 'backgroundColor': '#0000FF'
  255. });
  256. $('.fb', _dom).html(_free_cell_bottom);
  257. $('.fr', _dom).html(_free_cell_right);
  258. }
  259. _propageFreeCells();
  260. };
  261. this.setFreeCellsRight = function(right) {
  262. if (_free) {
  263. // $.log('_setFreeCellRigth | _free_cell_right = ' + _free_cell_right + ' | new right = ' + right);
  264. _free_cell_right = right;
  265. if (_grid.settings.grille) $('.fr', _dom).html(_free_cell_right);
  266. _propageFreeCellsLeft();
  267. }
  268. };
  269. this.setFreeCellsBottom = function(bottom) {
  270. if (_free) {
  271. // $.log('_setFreeCellsBottom | _free_cell_bottom = ' + _free_cell_bottom + ' | new right = ' + bottom);
  272. _free_cell_bottom = bottom;
  273. if (_grid.settings.grille) $('.fb', _dom).html(_free_cell_bottom);
  274. _propageFreeCellsTop();
  275. }
  276. };
  277. function _propageFreeCells() {
  278. // $.log('_propageFreeCells');
  279. _propageFreeCellsLeft();
  280. _propageFreeCellsTop();
  281. };
  282. function _propageFreeCellsLeft() {
  283. // $.log('_propageFreeCellsLeft | _column = '+_column);
  284. if (_column > 0) {
  285. var left_cel = _cells[_row][_column - 1];
  286. left_cel.setFreeCellsRight(_free_cell_right + 1);
  287. }
  288. };
  289. function _propageFreeCellsTop() {
  290. // $.log('_propageFreeCellsTop | _row = '+_row);
  291. if (_row > 0) {
  292. var top_cel = _cells[_row - 1][_column];
  293. top_cel.setFreeCellsBottom(_free_cell_bottom + 1);
  294. }
  295. };
  296. if (typeof Cell.initialized == "undefined") {
  297. Cell.prototype.infos = function() {
  298. $.log('cell | column = ' + this.column() + ', line = ' + this.row() + ', free = ' + this.isFree() + ', free cell right = ' + this.getAround().right + ', free cell bottom = ' + this.getAround().bottom);
  299. };
  300. Cell.initialized = true;
  301. }
  302. }; // cell()
  303. if (typeof Node.initialized == "undefined") {
  304. Grid.prototype.refresh = function() {
  305. _placeTargets();
  306. };
  307. Grid.prototype.resetPrevCell = function() {
  308. _setPrevCell();
  309. // this.prev_cell = false;
  310. };
  311. Node.initialized = true;
  312. }
  313. };
  314. (function($) {
  315. $.fn.grid = function(settings) {
  316. return this.each(function(index) {
  317. $(this).data('grid', new Grid(this, settings));
  318. });
  319. }
  320. })(jQuery);