Shrinking boxes - javascript

I am pulling my hair out, trying to solve a simple problem. I know there is some math step here that I am missing and I feel like I am right on the edge of the solution but I just can't get there.
What I am trying to do is write an algorithm to fill an element with squares, and once the element is filled, resize the squares, create a new row and keep filling. Once both new rows are filled, append another row. Basically a line feed type algorithm?
So what I have so far works on the first row, but fails after that as it keeps resizing the unnecessarily.
$(function(){ //DOM Ready
//get columns of even squares
var columns = parseInt($(".grid").width()/$(".grid").height());
var rows = 1;
function checkSize(ico,index){
var grid,gridWidth,gridHeight
var icon,iconWidth,iconHeight
//get our grid info
grid = $(".grid");
gridWidth = grid.width();
gridHeight= grid.height();
//get our icon info
icon = $(ico);
iconWidth = icon.outerWidth();
iconHeight = icon.outerHeight();
//will go over columns?
if(index >= columns){
//do we need another row?
if(Math.floor(gridHeight/iconHeight) === rows){
//add row
rows++;
//reset columns
columns = gridWidth / Math.floor(gridHeight / rows);
//resize boxes
size();
};
}
};
function size(){
var grid,icon,newHeight;
grid = $(".grid");
newHeight = (grid.height()/rows)
$.each(grid.children(".icon"),function(index,value){
icon = $(value);
icon.css("height",newHeight+'px');
icon.css("width",newHeight+'px');
});
}
//fill
var counter = 0;
function fillGrid(){
var icon,grid,r,g,b,a
icon = $('<div class="icon"></div>');
//random color
r = Math.floor(Math.random()*256);
g = Math.floor(Math.random()*256);
b = Math.floor(Math.random()*256);
a = 1;
icon.css('background-color','rgba('+r+','+g+','+b+','+a+')')
grid = $(".grid");
grid.append(icon);
size(icon);
checkSize(icon,counter);
counter++;
};
var timeoutID;
function start(){
if(timeoutID){
clearInterval(timeoutID);
timeoutID = undefined;
}else{
timeoutID = window.setInterval(fillGrid, 1000);
}
};
//START THE TIMER
$('#start').on('click',start);
});
See the working example here-
http://jsbin.com/ovewib/7/edit
Any help would be greatly appreciated!

Here's the problem:
//will go over columns?
if (index >= columns) {
This is counting wrong when there is more than one row. Try this instead:
//will go over columns?
if (index >= Math.floor(columns)*rows) {
http://jsfiddle.net/mblase75/zXeHG/

Related

Can I Use Google Apps Script to Get the List Preset From One Paragraph and Apply it to Another in Google Slides

I'm trying to write some code in apps script to pull text from a text box in google slides and split it by paragraphs into new text boxes and I'm hitting a snag when I get to preserving lists. I haven't been successful in finding a simple way to get the listPreset from the paragraphs in the original text box to the paragraphs in their own text boxes.
I was able to use ListStyle() methods getGlyph() and isInList() successfully but unless I make a dictionary of which listPreset each glyph is in, I don't seem to able to retrieve the listPreset with any of the listed methods which I would need in order to fill into applyListPreset().
I'd love a getListPreset() function so I could just nab that from the original list. Is there something like that I'm missing?
Here is an example "before" condition.
Here is an example of the expected "after" condition.
Here is an example of what the "after" condition is when I am unable to duplicate the listStyle from the original text box into the new text boxes.
In the following example I copy the text from the original textbox and gather various attributes from it. I attempt to input the listStyle() information into the applyListPreset() method even though I know that's wrong. I just don't know how else to get the List Preset from the original text box.
FYI I've already had a bit of help with this already so it's not all my work.
function myFunction() { // get slides in the presentation
var slides = SlidesApp.getActivePresentation().getSlides();
var numberOfLogs = 5;
for (let slide of slides) { // get the objects on each slide
splitParagraphs(slide);
}
}
function splitParagraphs(slide){
slideShapes = slide.getShapes();
for(let shape of slideShapes){
var shapetype = shape.getShapeType();
if (shapetype == "TEXT_BOX"){ // checks to see if the object is a text box
createSplitShapes(shape, slide);
shape.remove();
}
}
}
function createSplitShapes(shape, slide){
var paragraphs = shape.getText().getParagraphs();
var oldHeight = shape.getHeight();
var width = shape.getWidth();
var newShapeHeight = oldHeight / paragraphs.length;
for (let [index, paragraph] of paragraphs.entries() ){ // make a textbox for each paragraph distributed vertically over the original textbox
createParagraphShapes(shape, index, paragraph, newShapeHeight, width, slide);
}
}
function createParagraphShapes(shape, index, paragraph, shapeheight, width, slide){
var text = paragraph.getRange();
var list = text.getListStyle().isInList();
var style = text.getListStyle();
var glyph = text.getListStyle().getGlyph();
var rawText = text.asString();
var textStyle = text.getTextStyle();
var fontsize = textStyle.getFontSize();
var fontfamily = textStyle.getFontFamily();
var fontweight = textStyle.getFontWeight();
var paragraphStyle = text.getParagraphStyle();
var alignment = paragraphStyle.getParagraphAlignment();
var lineSpacing = paragraphStyle.getLineSpacing();
var indent = paragraphStyle.getIndentStart();
var shapetop = shapeheight * index + shape.getTop();
if ( ! isBlank(rawText) ) {
var t = slide.insertTextBox(rawText);
t.setLeft(shape.getLeft());
t.setTop(shapetop);
t.setWidth(width);
var newTextStyle = t.getText().getTextStyle();
newTextStyle.setFontSize(fontsize);
newTextStyle.setFontFamilyAndWeight(fontfamily, fontweight);
var newParagraphStyle = t.getText().getParagraphStyle();
newParagraphStyle.setParagraphAlignment(alignment);
newParagraphStyle.setLineSpacing(lineSpacing);
newParagraphStyle.setIndentStart(indent);
if (list = true) {
t.getText().getListStyle().applyListPreset(style);
}
}
}
function paragraphLogs(paragraph, numberOfLogs){
if(numberOfLogs > 0){
console.log("\tWhat's in paragraphs?: ");
for (var k = 0; k < paragraph.length; k++){
console.log("\t\t" + ( paragraphs[k].getRange().asString() ) );
}
}
}
function isBlank(str){
return (!str || str.trim().length === 0);
}

Why is my variable not incrementing?

I'm trying to build a very basic slider. I managed to create something but I am stuck when I try to change from one slide to another. I was thinking my code would work but it does not and I don't understand why.
Here's a link to a codepen of it
Here's the relevant part of my problem:
I have defined a slider like so:
var carrousel = document.getElementById('carrouselcontainer');
var slide1 = document.createElement('div');
slide1.className = 'slide1';
var slide2 = document.createElement('div');
slide2.className = 'slide2';
var slide3 = document.createElement('div');
slide3.className = 'slide3';
var slider = [slide1, slide2, slide3]
carrousel.appendChild(slider[0]);
And this does work. Now I am trying to add a function that will increment or decrement the slider variable when clicked.
var backButton = document.getElementById('backButton');
var forwardButton = document.getElementById('forwardButton')
function forward() {
slider++
}
forwardButton.addEventListener('click', forward)
But when I click on the forwardButton element, nothing happens. I know that the eventListener is working since I tried a window.alert() message on it and it works when I click it. What is it that I am doing wrong here?
Incrementing an array will not work but only generate error.
forward() should remove the actual child of the carrousel and
add a new child that has as value the next element of the slider array:
Introduce a currentSlide number variable to indicate the current index displayed and increment it in forward().
var currentSlide = 0;
...
carrousel.appendChild(slider[currentSlide]);
...
function forward() {
carrousel.removeChild(slider[currentSlide++]);
if (currentSlide >= slider.length){
currentSlide = 0;
}
carrousel.appendChild(slider[currentSlide]);
}
You need some kind of index which you use to get something out of your array:
var forwardButton = document.getElementById('forwardButton');
var result = document.getElementById('carrouselcontainer');
var sliderIndex = 0;
var slider1 = document.createElement("div");
slider1.innerHTML = "item 1";
var slider2 = document.createElement("div");
slider2.innerHTML = "item 2";
var slider3 = document.createElement("div");
slider3.innerHTML = "item 3";
var slider = [slider1, slider2, slider3];
result.appendChild(slider[sliderIndex]);
function forward() {
result.removeChild(slider[sliderIndex]);
sliderIndex = (sliderIndex + 1) % slider.length;
result.appendChild(slider[sliderIndex]);
}
forwardButton.addEventListener('click', forward);
<button id="forwardButton">Increment</button>
<div id="carrouselcontainer"></div>
You have to replace the element only incrementing the element does not work:
I added a counter "silderPos" to your script, so you can increment it by one on every click.
If it larger then 2 it will go back to 0 by a modulo action.
I made the carrouselElement global, so that your click script can access the element directly.
With this changes i can now go throu all of your element.
Please find the modified source code here:
var carrouselElement;
var sliderPos = 0;
function carrousel()
{
carrouselElement = document.getElementById('carrouselcontainer');
var slide1 = document.createElement('div');
slide1.className = 'slide1';
var slide2 = document.createElement('div');
slide2.className = 'slide2';
var slide3 = document.createElement('div');
slide3.className = 'slide3';
var slider = [slide1, slide2, slide3]
carrouselElement.appendChild(slider[0]);
var backButton = document.getElementById('backButton');
var forwardButton = document.getElementById('forwardButton')
function forward() {
carrouselElement.lastChild.remove();
sliderPos++;
sliderPos = sliderPos % 3;
carrouselElement.appendChild(slider[sliderPos]);
}
/*backButton.addEventListener('click', back)*/
forwardButton.addEventListener('click', forward)
}
window.addEventListener('load', carrousel);

Change javascript variable using html buttons

So I'm a beginning programmer and I'm trying to make four in a row using javascript and html.
The game works just fine, but I'm using a prompt to make the players choose a column. This isn't very user friendly, so I would like to use html buttons for this (one for every column).
However, I can't get it to work. I think the code is being executed before the buttonclick.
the button should give the var column the right value every turn.
place will place a 1 or 2 (resembling the player) into a 2d array
checkField will check if there are 4 in a row
This is what I have now:
in the body :
<button onclick="clickButton(1)" style="position:absolute; left:300px; top:10px">A</button>
And in the script:
var turn = 1;
var wichPlayer = 0;
var noFourInARow = true;
while(noFourInARow){
if(turn%2 == 1){
wichPlayer = 1;
}
else{
wichPlayer = 2;
}
function clickButton(k){
var column = k;
array = place(row,wichplayer,column);
turn++;
noFourInARow = checkField(row, wichPlayer, player1, player2);
}
}
When you click button your code is not re-evaluated. SO you run only portion inside button function. Why not move all actions into button?
function action() {
var turn = 1;
var wichPlayer = 0;
var noFourInARow = true;
while(noFourInARow){
if(turn%2 == 1){
wichPlayer = 1;
}
else{
wichPlayer = 2;
}
}
function clickButton(k){
var column = k;
array = place(row,wichplayer,column);
turn++;
noFourInARow = checkField(row, wichPlayer, player1, player2);
action();
}
This is just an example to change point of view. I'm not sure what you are doing with your code so...

Handle cells with rowspan when hiding table rows

I have a table containing cells with rowspan attributes, I would like to:
Whenever a tr is hidden, the table will rearrange itself correctly
Whenever a tr is shown again, it will be restored to original state
So if you have a table like this clicking on X shouldn't destroy the layout.
and click a come back button, should restore the original layout.
(try removing all rows from bottom-up, and than restoring them from right-to-left, this is a desired flow)
I had some semi-solutions, but all seem too complicated, and i'm sure there is a nice way to handle this.
OK I really spent a hell of a long time over this question, so here goes...
For those of you who just want to see the working solution, click here
Update: I've changed the visual columns calculation method to iterate over the table and create a 2-dimensional array, to see the old method which used the jQuery offset() method, click here. The code is shorter, but more time costly.
The problem exists because when we hide a row, whilst we want all the cells to be hidden, we want the pseudo-cells — that is, the cells that appear to be in the following rows due to the cells rowspan attribute — to persist. To get around this, whenever we come across a hidden cell with a rowspan, we try to move it down the the next visible row (decrementing it's rowspan value as we go). With either our original cell or it's clone, we then iterate down the table once more for every row that would contain a pseudo-cell, and if the row is hidden we decrement the rowspan again. (To understand why, look at the working example, and note that when the blue row is hidden, red cell 9's rowspan must be reduced from 2 to 1, else it would push green 9 right).
With that in mind, we must apply the following function whenever rows are shown/hidden:
function calculate_rowspans() {
// Remove all temporary cells
$(".tmp").remove();
// We don't care about the last row
// If it's hidden, it's cells can't go anywhere else
$("tr").not(":last").each(function() {
var $tr = $(this);
// Iterate over all non-tmp cells with a rowspan
$("td[rowspan]:not(.tmp)", $tr).each(function() {
$td = $(this);
var $rows_down = $tr;
var new_rowspan = 1;
// If the cell is visible then we don't need to create a copy
if($td.is(":visible")) {
// Traverse down the table given the rowspan
for(var i = 0; i < $td.data("rowspan") - 1; i ++) {
$rows_down = $rows_down.next();
// If our cell's row is visible then it can have a rowspan
if($rows_down.is(":visible")) {
new_rowspan ++;
}
}
// Set our rowspan value
$td.attr("rowspan", new_rowspan);
}
else {
// We'll normally create a copy, unless all of the rows
// that the cell would cover are hidden
var $copy = false;
// Iterate down over all rows the cell would normally cover
for(var i = 0; i < $td.data("rowspan") - 1; i ++) {
$rows_down = $rows_down.next();
// We only consider visible rows
if($rows_down.is(":visible")) {
// If first visible row, create a copy
if(!$copy) {
$copy = $td.clone(true).addClass("tmp");
// You could do this 1000 better ways, using classes e.g
$copy.css({
"background-color": $td.parent().css("background-color")
});
// Insert the copy where the original would normally be
// by positioning it relative to it's columns data value
var $before = $("td", $rows_down).filter(function() {
return $(this).data("column") > $copy.data("column");
});
if($before.length) $before.eq(0).before($copy);
else $(".delete-cell", $rows_down).before($copy);
}
// For all other visible rows, increment the rowspan
else new_rowspan ++;
}
}
// If we made a copy then set the rowspan value
if(copy) copy.attr("rowspan", new_rowspan);
}
});
});
}
The next, really difficult part of the question is calculating at which index to place the copies of the cells within the row. Note in the example, blue cell 2 has an actual index within its row of 0, i.e. it's the first actual cell within the row, however we can see that visually it lies in column 2 (0-indexed).
I took the approach of calculating this only once, as soon as the document is loaded. I then store this value as a data attribute of the cell, so that I can position a copy of it in the right place (I've had many Eureka moments on this one, and made many pages of notes!). To do this calculation, I ended up constructing a 2-dimensional Array matrix which keeps track of all of the used-visual columns. At the same time, I store the cells original rowspan value, as this will change with hiding/showing rows:
function get_cell_data() {
var matrix = [];
$("tr").each(function(i) {
var $cells_in_row = $("td", this);
// If doesn't exist, create array for row
if(!matrix[i]) matrix[i] = [];
$cells_in_row.each(function(j) {
// CALCULATE VISUAL COLUMN
// Store progress in matrix
var column = next_column(matrix[i]);
// Store it in data to use later
$(this).data("column", column);
// Consume this space
matrix[i][column] = "x";
// If the cell has a rowspan, consume space across
// Other rows by iterating down
if($(this).attr("rowspan")) {
// Store rowspan in data, so it's not lost
var rowspan = parseInt($(this).attr("rowspan"));
$(this).data("rowspan", rowspan);
for(var x = 1; x < rowspan; x++) {
// If this row doesn't yet exist, create it
if(!matrix[i+x]) matrix[i+x] = [];
matrix[i+x][column] = "x";
}
}
});
});
// Calculate the next empty column in our array
// Note that our array will be sparse at times, and
// so we need to fill the first empty index or push to end
function next_column(ar) {
for(var next = 0; next < ar.length; next ++) {
if(!ar[next]) return next;
}
return next;
}
}
Then simply apply this on page load:
$(document).ready(function() {
get_cell_data();
});
(Note: whilst the code here is longer than my jQuery .offset() alternative, it's probably quicker to calculate. Please correct me if I'm wrong).
Working solution - http://codepen.io/jmarroyave/pen/eLkst
This is basically the same solution that i presented before, i just changed how to get the column index to remove the restriction of the jquery.position, and did some refactor to the code.
function layoutInitialize(tableId){
var layout = String();
var maxCols, maxRows, pos, i, rowspan, idx, xy;
maxCols = $(tableId + ' tr').first().children().length;
maxRows = $(tableId + ' tr').length;
// Initialize the layout matrix
for(i = 0; i < (maxCols * maxRows); i++){
layout += '?';
}
// Initialize cell data
$(tableId + ' td').each(function() {
$(this).addClass($(this).parent().attr('color_class'));
rowspan = 1;
if($(this).attr('rowspan')){
rowspan = $(this).attr("rowspan");
$(this).data("rowspan", rowspan);
}
// Look for the next position available
idx = layout.indexOf('?');
pos = {x:idx % maxCols, y:Math.floor(idx / maxCols)};
// store the column index in the cell for future reposition
$(this).data('column', pos.x);
for(i = 0; i < rowspan; i++){
// Mark this position as not available
xy = (maxCols * pos.y) + pos.x
layout = layout.substr(0, xy + (i * maxCols)) + 'X' + layout.substr(xy + (i * maxCols) + 1);
}
});
}
Solution: with jquery.position() - http://codepen.io/jmarroyave/pen/rftdy
This is an alternative solution, it assumes that the first row contains all the information about the number of the table columns and the position of each on.
This aproach has the restriction that the inizialitation code must be call when the table is visible, because it depends on the visible position of the columns.
If this is not an issue, hope it works for you
Initialization
// Initialize cell data
$('td').each(function() {
$(this).addClass($(this).parent().attr('color_class'));
$(this).data('posx', $(this).position().left);
if($(this).attr('rowspan')){
$(this).data("rowspan", $(this).attr("rowspan"));
}
});
UPDATE
According to this post ensuring the visibility of the table can be manage with
$('table').show();
// Initialize cell data
$('td').each(function() {
$(this).addClass($(this).parent().attr('color_class'));
$(this).data('posx', $(this).position().left);
if($(this).attr('rowspan')){
$(this).data("rowspan", $(this).attr("rowspan"));
}
});
$('table').hide();
As Ian said, the main issue to solve in this problem is to calculate the position of the cells when merging the hidden with the visible rows.
I tried to figure it out how the browser implements that funcionality and how to work with that. Then looking the DOM i searched for something like columnVisiblePosition and i found the position attributes and took that way
function getColumnVisiblePostion($firstRow, $cell){
var tdsFirstRow = $firstRow.children();
for(var i = 0; i < tdsFirstRow.length; i++){
if($(tdsFirstRow[i]).data('posx') == $cell.data('posx')){
return i;
}
}
}
The js code
$(document).ready(function () {
add_delete_buttons();
$(window).on("tr_gone", function (e, tr) {
add_come_back_button(tr);
});
// Initialize cell data
$('td').each(function() {
$(this).addClass($(this).parent().attr('color_class'));
$(this).data('posx', $(this).position().left);
if($(this).attr('rowspan')){
$(this).data("rowspan", $(this).attr("rowspan"));
}
});
});
function calculate_max_rowspans() {
// Remove all temporary cells
$(".tmp").remove();
// Get all rows
var trs = $('tr'), tds, tdsTarget,
$tr, $trTarget, $td, $trFirst,
cellPos, cellTargetPos, i;
// Get the first row, this is the layout reference
$trFirst = $('tr').first();
// Iterate through all rows
for(var rowIdx = 0; rowIdx < trs.length; rowIdx++){
$tr = $(trs[rowIdx]);
$trTarget = $(trs[rowIdx+1]);
tds = $tr.children();
// For each cell in row
for(cellIdx = 0; cellIdx < tds.length; cellIdx++){
$td = $(tds[cellIdx]);
// Find which one has a rowspan
if($td.data('rowspan')){
var rowspan = Number($td.data('rowspan'));
// Evaluate how the rowspan should be display in the current state
// verify if the cell with rowspan has some hidden rows
for(i = rowIdx; i < (rowIdx + Number($td.data('rowspan'))); i++){
if(!$(trs[i]).is(':visible')){
rowspan--;
}
}
$td.attr('rowspan', rowspan);
// if the cell doesn't have rows hidden within, evaluate the next cell
if(rowspan == $td.data('rowspan')) continue;
// If this row is hidden copy the values to the next row
if(!$tr.is(':visible') && rowspan > 0) {
$clone = $td.clone();
// right now, the script doesn't care about copying data,
// but here is the place to implement it
$clone.data('rowspan', $td.data('rowspan') - 1);
$clone.data('posx', $td.data('posx'));
$clone.attr('rowspan', rowspan);
$clone.addClass('tmp');
// Insert the temp node in the correct position
// Get the current cell position
cellPos = getColumnVisiblePostion($trFirst, $td);
// if is the last just append it
if(cellPos == $trFirst.children().length - 1){
$trTarget.append($clone);
}
// Otherwise, insert it before its closer sibling
else {
tdsTarget = $trTarget.children();
for(i = 0; i < tdsTarget.length; i++){
cellTargetPos = getColumnVisiblePostion($trFirst, $(tdsTarget[i]));
if(cellPos < cellTargetPos){
$(tdsTarget[i]).before($clone);
break;
}
}
}
}
}
}
// remove tmp nodes from the previous row
if(rowIdx > 0){
$tr = $(trs[rowIdx-1]);
if(!$tr.is(':visible')){
$tr.children(".tmp").remove();
}
}
}
}
// this function calculates the position of a column
// based on the visible position
function getColumnVisiblePostion($firstRow, $cell){
var tdsFirstRow = $firstRow.children();
for(var i = 0; i < tdsFirstRow.length; i++){
if($(tdsFirstRow[i]).data('posx') == $cell.data('posx')){
return i;
}
}
}
function add_delete_buttons() {
var $all_rows = $("tr");
$all_rows.each(function () {
// TR to remove
var $tr = $(this);
var delete_btn = $("<button>").text("x");
delete_btn.on("click", function () {
$tr.hide();
calculate_max_rowspans();
$(window).trigger("tr_gone", $tr);
});
var delete_cell = $("<td>");
delete_cell.append(delete_btn);
$(this).append(delete_cell);
});
}
function add_come_back_button(tr) {
var $tr = $(tr);
var come_back_btn = $("<button>").text("come back " + $tr.attr("color_class"));
come_back_btn.css({"background": $(tr).css("background")});
come_back_btn.on("click", function () {
$tr.show();
come_back_btn.remove();
calculate_max_rowspans();
});
$("table").before(come_back_btn);
}
if you have any questions or comments let me know.
I'm assuming you want the the rows to shift upward when you hide the row but you do not want the cells to shift left.
Here is what I got http://codepen.io/anon/pen/prDcK
I added two css rules:
#come_back_container{height: 30px;}
td[rowspan='0']{background-color: white;}
Here is the html I used:
<div id="come_back_container"></div>
<table id="dynamic_table" cellpadding=7></table>
<table id="dynamic_table2" cellpadding=7>
<tr style="background-color: red">
<td rowspan="5">a</td>
<td rowspan="1">b</td>
<td rowspan="5">c</td>
<td rowspan="1">d</td>
<td rowspan="2">e</td>
</tr>
<tr style="background-color: grey">
<td rowspan="0">f</td>
<td rowspan="1">g</td>
<td rowspan="0">h</td>
<td rowspan="1">i</td>
<td rowspan="0">j</td>
</tr>
<tr style="background-color: blue">
<td rowspan="0">k</td>
<td rowspan="1">l</td>
<td rowspan="0">m</td>
<td rowspan="1">n</td>
<td rowspan="1">o</td>
</tr>
<tr style="background-color: yellow">
<td rowspan="0">p</td>
<td rowspan="1">q</td>
<td rowspan="0">r</td>
<td rowspan="1">s</td>
<td rowspan="2">t</td>
</tr>
<tr style="background-color: green">
<td rowspan="0">u</td>
<td rowspan="1">v</td>
<td rowspan="0">w</td>
<td rowspan="1">x</td>
<td rowspan="0">y</td>
</tr>
</table>
The first rule is just to keep the top edge of the table in the same place. The second rule is to make the cells appear blank by blending in with the background, so change accordingly.
Finally here is the js:
$(function () {
//firstTable()
var myTb2 = new dynamicTable();
myTb2.createFromElement( $("#dynamic_table2") );
myTb2.drawTable()
$(window).on("tr_hide", function (e,data){
var tbl = data.ctx,
rowIndex = data.idx;
tbl.hideRow.call(tbl, rowIndex);
})
$(window).on("tr_show", function (e,data){
var tbl = data.ctx,
rowIndex = data.idx;
tbl.showRow.call(tbl, rowIndex);
})
})
function dynamicTableItem(){
this.height = null;
this.content = null;
}
function dynamicTableRow(){
this.color = null;
this.items = []
this.show = true
this.setNumColumns = function(numCols){
for(var i=0;i<numCols;i++){
var item = new dynamicTableItem();
item.height = 0;
this.items.push(item)
}
}
this.addItem = function(index, height, content){
var item = new dynamicTableItem();
item.height = height;
item.content = content;
if(index>=this.items.length){ console.error("index out of range",index); }
this.items[index] = item;
}
}
function dynamicTable(){
this.element = null;
this.numCols = null;
this.rows = []
this.addRow = function(color){
var row = new dynamicTableRow();
row.color = color;
row.setNumColumns(this.numCols)
var length = this.rows.push( row )
return this.rows[length-1]
}
this.drawTable = function(){
this.element.empty()
var cols = [],
rowElements = [];
for(var i=0;i<this.numCols;i++){
cols.push( [] )
}
for(var r=0; r<this.rows.length; r++){
var row = this.rows[r]
if(row.show){
var $tr = $("<tr>"),
delete_cell = $("<td>"),
delete_btn = $("<button>").text("x")
var data = {ctx: this, idx: r};
delete_btn.on("click", data, function(e){
$(window).trigger("tr_hide", e.data);
})
delete_cell.addClass("deleteCell");
$tr.css( {"background": row.color} );
delete_cell.append(delete_btn);
$tr.append(delete_cell);
this.element.append($tr);
rowElements.push( $tr );
for(var i=0; i<row.items.length; i++){
cols[i].push( row.items[i] );
}
}
}
for(var c=0; c<cols.length; c++){
var cellsFilled = 0;
for(var r=0; r<cols[c].length; r++){
var item = cols[c][r]
var size = item.height;
if(r>=cellsFilled){
cellsFilled += (size>0 ? size : 1);
var el = $("<td>").attr("rowspan",size);
el.append(item.content);
rowElements[r].children().last().before(el);
}
}
}
}
this.hideRow = function(rowIndex){
var row = this.rows[rowIndex]
row.show = false;
var come_back_btn = $("<button>").text("come back");
come_back_btn.css( {"background": row.color} );
var data = {ctx:this, idx:rowIndex};
come_back_btn.on("click", data, function(e){
$(window).trigger("tr_show", e.data);
$(this).remove();
});
$("#come_back_container").append(come_back_btn);
this.drawTable();
}
this.showRow = function(rowIndex){
this.rows[rowIndex].show = true;
this.drawTable();
}
this.createFromElement = function(tbl){
this.element = tbl;
var tblBody = tbl.children().filter("tbody")
var rows = tblBody.children().filter("tr")
this.numCols = rows.length
for(var r=0;r<rows.length;r++){
var row = this.addRow( $(rows[r]).css("background-color") );
var items = $(rows[r]).children().filter("td");
for(var i=0;i<items.length;i++){
var item = $(items[i]);
var height = parseInt(item.attr("rowspan"));
var contents = item.contents();
row.addItem(i,height,contents);
}
}
//console.log(this);
}
}
function firstTable(){
var myTable = new dynamicTable();
myTable.element = $("#dynamic_table");
myTable.numCols = 5
var red = myTable.addRow("red");
red.addItem(0,5);
red.addItem(1,1);
red.addItem(2,5);
red.addItem(3,1);
red.addItem(4,2);
var white = myTable.addRow("grey");
//white.addItem(0,0);
white.addItem(1,1);
//white.addItem(2,0);
white.addItem(3,1);
//white.addItem(4,0);
var blue = myTable.addRow("blue");
//blue.addItem(0,3); //try uncommenting this and removing red
blue.addItem(1,1);
//blue.addItem(2,0);
blue.addItem(3,1);
blue.addItem(4,1);
var yellow = myTable.addRow("yellow");
//yellow.addItem(0,0);
yellow.addItem(1,1);
//yellow.addItem(2,0);
yellow.addItem(3,1);
yellow.addItem(4,2);
var green = myTable.addRow("green");
//green.addItem(0,0);
green.addItem(1,1);
//green.addItem(2,0);
green.addItem(3,1);
//green.addItem(4,0);
myTable.drawTable();
}
I tried to use clear variable and method names but if you have any quests just ask.
PS- I know there is no easy way to add content to the cells right now but you only asked for disappearing rows.

jQuery: too much recursion But I need recursion for minesweeper

I am relatively new to javascript. I am trying to code my web version of minesweeper. Here is a recursive function I needed, and it looks to work fine until browser gives this "too much recursion" error. The problem is that i need that recursion. Is there any other way to code minesweeper? Here is the demo: http://altynachar.com/minesweeper/
I can post my php code if needed.
function recursive(id){
var id = id;
//Determine what kind of cell this is: Clean, Bomb or Adjasent to bomb
if($("#"+id).hasClass("adj")== true)
var under = "adj";
if($("#"+id).hasClass("bomb")==true)
var under = "bomb";
if($("#"+id).hasClass("clean")==true)
var under = "clean";
//open up the cell
$("#"+id).hide();
$("#under_"+id).show();
//if it is bomb, open up whole grid and button for restart
if(under == 'bomb')
{
$(".cover").hide();
$(".under").show();
$("body").append("<br /><input type='button' value='restart' onClick='javascript:window.location.reload();' />");
} else {
//if it is clean cell
if(under == "clean")
{
//get all the adjasent cell ids
var split = id.split('-');
var row = parseInt(split[0]);
var col = parseInt(split[1]);
var adjasent = new Array();
adjasent[0] = (row-1)+"-"+ (col+1);
adjasent[1] = row +"-"+(col+1);
adjasent[2] = (row+1)+"-"+(col+1);
adjasent[3] = (row+1)+"-"+col;
adjasent[4] = (row+1)+"-"+(col-1);
adjasent[5] = row+"-"+(col-1);
adjasent[6] = (row -1)+"-"+(col-1);
adjasent[7] = (row -1)+"-"+col;
//loop through adjasent cells
for(var i=0; i<adjasent.length; i++)
{
var split2 = adjasent[i].split('-');
var row2 = parseInt(split2[0]);
var col2 = parseInt(split2[1]);
//check if cell is existent
if(row2 > 0 && row2 < 17)
{
if(col2 > 0 && col2 < 17)
{
//perform recursion
var adj = adjasent[i];
recursive(adj);
}
}
}
}
}
}
My guess is that if you have 2 clean cells next to each other your code will get in an infinite recursion.
Each iteration recurses to all adjacent cells. So say cell A and B are next to each other, and both are clean. A will call recurse to B, which will then recurse to A, which recurses to B, and so on.
You can either try to clean up your recursion so that it doesn't look at cells that were already seen, or remove the recursion. You can accomplish the same thing by adding any unseen clean cells to a queue, and just keep popping off the end of the queue until it's empty. That might make it easier to avoid checking the same cell twice too.
Also, please don't build up strings just to split them into separate data later. Instead of:
adjasent[0] = (row-1)+"-"+ (col+1);
/* ... */
var split2 = adjasent[i].split('-');
var row2 = parseInt(split2[0]);
var col2 = parseInt(split2[1]);
just do
adjacent[0] = { row: row-1, col: col+1 };
/* ... */
var row2 = adjacent[0].row
var col2 = adjacent[0].col
Your recursion is essentially a depth-first search. The problem is that you do not account for visited cells. In other words, say you have 2 cells, A & B:
A B
When you click on A, it searches for adjacent cells, and comes up with a list containing B. You recurse, and then look for neighbors of B, which is a list containing A, and then you recurse again. This cycle never ends.
You need to mark each cell you visit and return if it has already been visited:
function recursive(id){
var id = id;
if( $("#"+id).hasClass('visited') ) {
return;
}
$("#"+id).addClass('visited');
...
}
Then you need to remove 'visited' from everything after recursion is complete:
$('div').removeClass('visited');
Keep a running array of cell id's to check, and delete these values from the array as you check them.
var stack = ["first_id_to_check"];
function check(id){
var id = id;
//Determine what kind of cell this is: Clean, Bomb or Adjasent to bomb
if($("#"+id).hasClass("adj")== true)
var under = "adj";
if($("#"+id).hasClass("bomb")==true)
var under = "bomb";
if($("#"+id).hasClass("clean")==true)
var under = "clean";
//open up the cell
$("#"+id).hide();
$("#under_"+id).show();
//if it is bomb, open up whole grid and button for restart
if(under == 'bomb')
{
$(".cover").hide();
$(".under").show();
$("body").append("<br /><input type='button' value='restart' onClick='javascript:window.location.reload();' />");
} else {
//if it is clean cell
if(under == "clean")
{
//get all the adjasent cell ids
var split = id.split('-');
var row = parseInt(split[0]);
var col = parseInt(split[1]);
var adjasent = new Array();
adjasent[0] = (row-1)+"-"+ (col+1);
adjasent[1] = row +"-"+(col+1);
adjasent[2] = (row+1)+"-"+(col+1);
adjasent[3] = (row+1)+"-"+col;
adjasent[4] = (row+1)+"-"+(col-1);
adjasent[5] = row+"-"+(col-1);
adjasent[6] = (row -1)+"-"+(col-1);
adjasent[7] = (row -1)+"-"+col;
//loop through adjasent cells
for(var i=0; i<adjasent.length; i++)
{
var split2 = adjasent[i].split('-');
var row2 = parseInt(split2[0]);
var col2 = parseInt(split2[1]);
//check if cell is existent
if(row2 > 0 && row2 < 17)
{
if(col2 > 0 && col2 < 17)
{
stack.push(adjasent[i]);
}
}
}
}
}
}
while(stack[0]!==undefined) {
check(stack[0]);
stack.splice(0,1);
}

Categories