/** * Grid() * by Bachir Soussi-Chiadmi www.g-u-i.net * version 2.0 */ function Grid(ctx, settings){ var _defaults = { children:'', // tell which dom elmt to target, by default $ctx.children(); cell_w: 20, cell_h: 20, cell_padding:{t:5,r:5,b:5,l:5}, // with or without padding cell width and height are the same padding:{t:5,r:5,b:5,l:5}, // padding (in cell) inside the grid where not placed element gouttiere: 5, // space between cells h and v lines: 20, columns: 20,// how much line and columns grid has grille: false, // see the grid with cell's free space infos groove: 0, // add groove attitude (randomize placement) positioning_mode:'random', // positioning_mode play with with the position on items max_distance:0, // distance max between prev item and current item. O = no limit min_distance:0, // distance min between prev item and current item. O = no limit }, $ctx = ctx, _cells = new Array(), _positions = new Array(), _grid = this; this.ctx = $ctx; this.settings = $.extend({}, _defaults, settings); // $.log('this.settings', this.settings); // set dom grille visualsation context if enabled if (this.settings.grille) { $ctx.append('
'); $('.grille', $ctx).css({ 'width': '100%', 'height': '100%', 'position': 'relative' }); } // create all empty cells for (var l = 0; l < this.settings.lines; l++) { _cells[l] = new Array(); for (var c = 0; c < this.settings.columns; c++) { _cells[l][c] = new Cell(this, c, l); }; }; function _setPrevCell(){ // _grid.prev_cell = false; switch(_grid.settings.positioning_mode){ case 'lefttop': // add items from top left to bootm rif-ght _grid.prev_cell = _cells[0][0]; break; case 'center': // add items from center of grid to both side randomly _grid.prev_cell = _cells[Math.floor(_grid.settings.lines*.5)][Math.floor(_grid.settings.columns*.5)]; break; case 'borders': // add items from center of grid to both side randomly var side = Math.floor(Math.random()*4); $.log('side = '+side); switch(side){ case 0 : _grid.prev_cell = _cells[0][Math.floor(_grid.settings.columns*Math.random())]; break; case 1 : _grid.prev_cell = _cells[_grid.settings.lines-1][Math.floor(_grid.settings.columns*Math.random())]; break; case 2 : _grid.prev_cell = _cells[Math.floor(_grid.settings.lines*Math.random())][0]; break; case 3 : _grid.prev_cell = _cells[Math.floor(_grid.settings.lines*Math.random())][_grid.settings.columns-1]; break; } break; case 'random': // add items randomly default: _grid.prev_cell = _cells[Math.floor(_grid.settings.lines*Math.random())][Math.floor(_grid.settings.columns*Math.random())]; break; } } _setPrevCell(); /** * placeTargets() */ function _placeTargets(){ // parse targets var $targets = _grid.settings.children != '' ? $(_grid.settings.children+':not(.grid-placed)', $ctx) : $ctx.children(':not(.grid-placed)'); $targets.each(function() { var $this = $(this), // get the width and height ( ! on cell unit ! how much cells it takes ? ) of item to be positioned this_cells_w = Math.ceil($this.width() / (_grid.settings.cell_w + _grid.settings.gouttiere)), this_cells_h = Math.ceil($this.height() / (_grid.settings.cell_h + _grid.settings.gouttiere)), // get free cells function of width and height of item free_cells = getFreeCells(this_cells_w, this_cells_h); if(free_cells.length > 0){ // positioning mode, to change this mode uncomment/comment lines after description switch(_grid.settings.positioning_mode){ case 'lefttop': // add items from top left to bootm rif-ght var cell_num = 0, cell = free_cells[cell_num]; break; default: // add items from center of grid to both side randomly // var cell_num = Math.floor((free_cells.length-1)*0.5 + (-2+Math.random()*4)); // cell_num = cell_num > free_cells.length-1 ? free_cells.length-1 : ( cell_num < 0 ? 0 : cell_num ); // var cell = free_cells[cell_num]; var cell_num = Math.round(Math.random() * (free_cells.length - 1)), cell = free_cells[cell_num]; break; } _grid.prev_cell = cell; var line_limit_loop = cell.row() + (this_cells_h), column_limit_loop = cell.column() + (this_cells_w); for (var l = cell.row(); l < line_limit_loop; l++) { for (var c = cell.column(); c < column_limit_loop; c++) { var temp_cell = _cells[l][c]; temp_cell.setFull(); }; }; // get tp and left coordinates // should be directly recorded in the cell object var top = cell.row() * (_grid.settings.cell_h + _grid.settings.gouttiere), left = cell.column() * (_grid.settings.cell_w + _grid.settings.gouttiere), groove = _grid.settings.groove; // set groove ;) top = !groove ? top: top + Math.round(Math.random() * groove - groove / 2); left = !groove ? left: left + Math.round(Math.random() * groove - groove / 2); // apply ccs position top += _grid.settings.cell_padding.t; left += _grid.settings.cell_padding.l; var event = jQuery.Event("grid_positioned"); event.top = top; event.left = left; $this.css({ 'top': top, 'left': left }).addClass('grid-positioned').trigger(event); }else{ $.log('GRID no free cells left'); // if no free cells exist $this.css({ 'border': '2px solid red', 'opacity': 0.2 }).trigger('grid_removed');//.remove(); } $this.addClass('grid-placed').trigger('grid_placed'); }); // end children each } /** * getFreeCells() * * @ width in cells * @ height in cells * * return array of cell objects * */ function getFreeCells(this_cells_w, this_cells_h){ var free_cells = new Array(), padding = _grid.settings.padding, max_dist = _grid.settings.max_distance, min_dist = _grid.settings.min_distance, prev_cell = _grid.prev_cell; // $.log() for (var l = 0; l < _grid.settings.lines; l++) { for (var c = 0; c < _grid.settings.columns; c++) { var cell = _cells[l][c]; /* TODO about the max_dist keep in mind that all element will not always be 1 cell elemnts !! */ // here is a big stuff of conditionning // we check a lot of things // 1 - basicly if the cell is free // 2 - if the cell is under the padding limits // 3 - if she has enought space arround her // 4 - if we got a prev cell // 4.1 - if we got a max_dist != 0 // 4.1.1 - if the cell is under the max limits // 4.2 - if we got a min_dist != 0 // 4.2.1 - if the cell is behind the min limits if (cell.isFree() // if cell is free && (cell.row() > padding.t && cell.row() < _grid.settings.lines - padding.b && cell.column() > padding.l && cell.column() < _grid.settings.columns - padding.r ) && ( (cell.getAround().right + 1) >= this_cells_w // if enought free space on the right && (cell.getAround().bottom + 1) >= this_cells_h // if enought free space on the bottom ) && ( !_grid.prev_cell // else if _grid.prev_cell undefined it's true || ( // check max_dist AND min_dist (!max_dist // if max_distance is 0 return (stop condition) true || ( cell.row() < prev_cell.row()+max_dist && cell.row() > prev_cell.row()-max_dist && 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 ) && (!min_dist // if min_distance is 0 return (stop condition) true || ( (cell.row() > prev_cell.row()+min_dist || cell.row() < prev_cell.row()-min_dist) || (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 ) ) ) ) { // true of first big stuff if var good = true; for (var i = 1; i < this_cells_w; i++) { // for each free cells needed on right var temp_cell = _cells[l][c + i]; if ((temp_cell.getAround().bottom + 1) < this_cells_h) { // if enought free space on bottom good = false; break; } }; if (good) free_cells.push(cell); } }; }; return free_cells; } /** * Cell() */ function Cell(g, c, r) { var _grid = g, _column = c, _row = r, _free = true, _free_cell_right = _grid.settings.columns - 1 - c, _free_cell_bottom = _grid.settings.lines - 1 - l; if (_grid.settings.grille) { var _table = ''; _table += ''; // _table += ''; _table += ''; _table += ''; _table += '
   
 '+_free+''+_free_cell_right+'
  ' + _free_cell_right + '
 ' + _free_cell_bottom + ' 
'; _grid.ctx.find('.grille').append('
' + _table + '
'); var _dom = $('.cell-' + l + '-' + c, _grid.ctx.find('.grille')); _dom.css({ 'position': 'absolute', 'top': _row * (_grid.settings.cell_h + _grid.settings.gouttiere) + _grid.settings.cell_padding.t, 'left': _column * (_grid.settings.cell_w + _grid.settings.gouttiere) + _grid.settings.cell_padding.l, 'width': _grid.settings.cell_w, 'height': _grid.settings.cell_h, 'backgroundColor': '#00FF00', 'opacity': 0.6 }); // $('td', _dom).css({ // 'fontSize': '8px' // }); } this.isFree = function() { return _free; }; this.getPos = function() { return { c: _column, l: _row }; }; this.column = function() { return _column; }; this.row = function() { return _row; }; this.getAround = function() { return { right: _free_cell_right, bottom: _free_cell_bottom }; }; this.setFull = function() { _free = false; _free_cell_bottom = -1; _free_cell_right = -1; if (_grid.settings.grille) { _dom.css({ 'backgroundColor': '#0000FF' }); $('.fb', _dom).html(_free_cell_bottom); $('.fr', _dom).html(_free_cell_right); } _propageFreeCells(); }; this.setFreeCellsRight = function(right) { if (_free) { // $.log('_setFreeCellRigth | _free_cell_right = ' + _free_cell_right + ' | new right = ' + right); _free_cell_right = right; if (_grid.settings.grille) $('.fr', _dom).html(_free_cell_right); _propageFreeCellsLeft(); } }; this.setFreeCellsBottom = function(bottom) { if (_free) { // $.log('_setFreeCellsBottom | _free_cell_bottom = ' + _free_cell_bottom + ' | new right = ' + bottom); _free_cell_bottom = bottom; if (_grid.settings.grille) $('.fb', _dom).html(_free_cell_bottom); _propageFreeCellsTop(); } }; function _propageFreeCells() { // $.log('_propageFreeCells'); _propageFreeCellsLeft(); _propageFreeCellsTop(); }; function _propageFreeCellsLeft() { // $.log('_propageFreeCellsLeft | _column = '+_column); if (_column > 0) { var left_cel = _cells[_row][_column - 1]; left_cel.setFreeCellsRight(_free_cell_right + 1); } }; function _propageFreeCellsTop() { // $.log('_propageFreeCellsTop | _row = '+_row); if (_row > 0) { var top_cel = _cells[_row - 1][_column]; top_cel.setFreeCellsBottom(_free_cell_bottom + 1); } }; if (typeof Cell.initialized == "undefined") { Cell.prototype.infos = function() { $.log('cell | column = ' + this.column() + ', line = ' + this.row() + ', free = ' + this.isFree() + ', free cell right = ' + this.getAround().right + ', free cell bottom = ' + this.getAround().bottom); }; Cell.initialized = true; } }; // cell() if (typeof Node.initialized == "undefined") { Grid.prototype.refresh = function() { _placeTargets(); }; Grid.prototype.resetPrevCell = function() { _setPrevCell(); // this.prev_cell = false; }; Node.initialized = true; } }; (function($) { $.fn.grid = function(settings) { return this.each(function(index) { $(this).data('grid', new Grid(this, settings)); }); } })(jQuery);