When the user clicks on one of the blocks in the table ( see screenshot ) I want to find all neighbouring blocks with the same color. I am trying to do this recursively, but if I try it with more than three blocks it sometimes goes crazy and calls itself over and over until the program crashes.
As far as I can see, the objects are added to the array, but somehow my tests fails and the same object is added over and over and over.
Any insight on what the problem might be and how to solve it would be much appriciated!
Here's a screenshot
This is the function that is called when the user clicks on a block:
var $matchArray;
$('.block').click(function () {
$matchArray = [$(this)];
var $colorClass;
if ($(this).hasClass('red')) {
$colorClass = 'red';
} else if ($(this).hasClass('green')) {
$colorClass = 'green';
} else if ($(this).hasClass('blue')) {
$colorClass = 'blue';
} else {
$colorClass = 'error';
}
findAllSameColorNeighbours($(this), $colorClass);
});
And this is the recursive method:
findAllSameColorNeighbours = function ($this, $colorClass) {
$this.css('border-style', 'solid');
//LEFT
var $leftBlock = isLeftBlockSameColor($this, $colorClass);
if ($leftBlock != null) {
if (!(arrayContains($matchArray, $leftBlock))) {
$matchArray.push($leftBlock);
findAllSameColorNeighbours($leftBlock, $colorClass);
}
}
//ABOVE
//same as for LEFT
//RIGHT
//same as for LEFT
//BELOW
//same as for LEFT
}
This is how I find the neighboring cells, as far as I can see these work just fine. I have one for each direction:
isLeftBlockSameColor = function ($block, $color) {
var $this = $block;
var $tr = $this.parent().parent();
var col = $tr.children().index($this.parent().prev());
var $leftBlock = $this.parent().siblings().eq(col).children();
var $blockClassMatch = $leftBlock.hasClass($color);
if ($blockClassMatch) {
return $leftBlock;
}
else {
return null;
}
};
Here are some help methods to find out if the object is already in the array or not. I use the index of the row and cell to create a sort of latitude and longditude thing.
arrayContains = function ($array, $object) {
for (i = 0; i < Array.length; i++) {
if (compareIndex($array[i], $object)) {
say('true');
return true;
}
};
return false;
};
compareIndex = function ($obj1, $obj2) {
if ((getRowIndex($obj1)) === (getRowIndex($obj2)) {
if ((getCellIndex($obj1)) === (getCellIndex($obj2)) {
return true;
} else {
return false;
}
} else {
return false;
}
};
getCellIndex = function ($this) {
var $tr = $this.parent().parent();
var index = $tr.children().index($this.parent());
return index;
};
getRowIndex = function ($this) {
var $tr = $this.parent().parent();
var index = $tr.index();
return index;
};
There is a bug in the arrayContains function. The loop will iterates only once, because Array.length is equals to 1(As I tested with chrome browser, but I don't know why). You should use $array.length instead.
arrayContains = function ($array, $object) {
//for (i = 0; i < Array.length; i++) {
for (i = 0; i < $array.length; i++) {
if (compareIndex($array[i], $object)) {
say('true');
return true;
}
};
return false;
};
Related
I am required to stop the execution of the JavaScript function as soon as any of the table cells contains a zero. I tried my best, but nothing seems to work. I can not understand why is it not working.
function ZeroCheck(IDTable) {
var myData = [];
$('#' + IDTable).find("tr:gt(0)").each(function(i, row) {
var oRow = [];
$(row).find("td").each(function(j, cell) {
if ($(cell).text() == '0') {
alert('Please correct input. There should not be a 0 in any cell');
return false;
}
oRow.push(parseFloat($(cell).text()));
});
myData.push(oRow);
});
};
Please try with this:
function ZeroCheck(IDTable) {
let isZeroDetectted = false;
let myData = [];
$('#' + IDTable).find("tr:gt(0)").each(function(i, row) {
let oRow = [];
$(row).find("td").each(function(j, cell) {
if ($(cell).text() == '0') {
alert('Please correct input. There should not be a 0 in any cell');
isZeroDetectted = true;
return false;
}
oRow.push(parseFloat($(cell).text()));
});
if(isZeroDetectted){
return false;
}
myData.push(oRow);
});};
Corrected and Used version of mplungjan suggested piece of code. I find this code better than my one in my question. It is self-explanatory, fairly easy and gives more control over rows and columns.
function calculateAHP(IDTable){
var myData = [];
var rows = document.getElementById(IDTable).rows;
for (var i = 1; i<rows.length;i++) {
var oRow = [];
for ( var j=1; j<rows[i].cells.length; j++) {
var val = rows[i].cells[j].innerText;
if (parseFloat(val) == 0) {
alert('Please correct input. There should not be a 0 in any cell');
return false;
}
oRow.push(parseFloat(val));
}
myData.push(oRow);
}
};
I'm working on a simon game and is doing a sequence of 3 at level 2 instead of doing just 2 at level 2. I've looked all over. and I've trying output to console, but I guess I've been staring at this for too long. If someone can find the bug, please share. thanks for the help.
here's the pen
https://codepen.io/zentech/pen/XaYygR
//variables
userSeq = [];
simonSeq = [];
const NUM_OF_LEVELS = 5;
var id, color, level = 0;
var strict = false;
var error = false;
var boardSound = [
"http://www.soundjay.com/button/sounds/button-4.mp3", //green
"http://www.soundjay.com/button/sounds/button-09.mp3", //red
"http://www.soundjay.com/button/sounds/button-10.mp3", //yellow
"http://www.soundjay.com/button/sounds/button-7.mp3" //blue
];
//1- start board sequence
$(document).ready(function() {
$(".start").click(function() {
strict = false;
error = false;
level++;
simonSeq = userSeq = [];
simonSequence();
})
//user pad listener
$(".pad").click(function() {
id = $(this).attr("id");
color = $(this).attr("class").split(" ")[1];
userSequence();
});
//strict mode listener
$(".strict").click(function() {
level = 0;
level++;
simonSeq = userSeq = [];
strict = true;
simonSequence();
})
})
//user sequence
function userSequence() {
userSeq.push(id);
console.log(id+" "+color);
addClassSound(id, color);
//check user sequence
if(!checkUserSeq()) {
//if playing strict mode reset everything lol
if(strict) {
console.log("strict");
simonSeq = [];
level = 1;
}
displayError();
userSeq = [];
error = true;
console.log("start simon error")
simonSequence();
}
//checking end of sequence
else if(userSeq.length == simonSeq.length && userSeq.length < NUM_OF_LEVELS) {
level++;
userSeq = [];
error = false;
console.log("start simon")
simonSequence();
}
//checking for winners
if(userSeq.length == NUM_OF_LEVELS) {
displayWinner();
resetGame();
}
}
/* simon sequence */
function simonSequence() {
console.log("level "+level);
$(".display").text(level);
if(!error) {
getRandomNum();
}
var i = 0;
var myInterval = setInterval(function() {
id = simonSeq[i];
color = $("#"+id).attr("class");
color = color.split(" ")[1];
console.log(id+" "+color);
addClassSound(id, color);
i++;
if(i == simonSeq.length) {
clearInterval(myInterval);
}
}, 1000);
}
//generate random number
function getRandomNum() {
var random = Math.floor(Math.random() * 4);
simonSeq.push(random);
}
/* add temporary class and sound */
function addClassSound(id, color) {
$("#"+id).addClass(color+"-active");
playSound(id)
setTimeout(function(){
$("#"+id).removeClass(color+"-active");
}, 500);
}
/* checking user seq against simon's */
function checkUserSeq() {
for(var i = 0; i < userSeq.length; i++) {
if(userSeq[i] != simonSeq[i]) {
return false;
}
}
return true;
}
/* display error */
function displayError() {
console.log("error");
var counter = 0;
var myError = setInterval(function() {
$(".display").text("Err");
counter++;
if(counter == 3) {
$(".display").text(level);
clearInterval(myError);
userSeq = [];
counter = 0;
}
}, 500);
}
//display winner
function displayWinner() {
var count = 0;
var winInterval = setInterval(function() {
count++;
$(".display").text("Win");
if(count == 5) {
clearInterval(winInterval);
$(".display").text("00");
count = 0;
}
}, 500);
}
/* play board sound */
function playSound(id) {
var sound = new Audio(boardSound[id]);
sound.play();
}
/* reset game */
function resetGame() {
userSeq = [];
simonSeq = [];
level = 0;
strict = false;
$(".display").text("00");
}
PROBLEM
You have a reference vs copy problem in your initialization code.
$(document).ready(function() {
$(".start").click(function() {
strict = false;
error = false;
level++;
simonSeq = userSeq = []; //PROBLEM !!!!
simonSequence();
})
Arrays are passed by reference, not value.
simonSeq = userSeq = [];
/* Any changes to 'userSeq' will affect 'simonSeq'.
'simonSeq' is referencing 'userSeq' */
SOLUTION
Change all instances of
simonSeq = userSeq = [];
To
simonSeq = [];
userSeq = [];
EXPLINATION
Values in JavaScript can be referred to in 2 ways; by reference and by value.
When you refer to something by value, you are copying it.
var numA = 5;
var numB = numA; //COPY numA over to numB
numA = 12; // Changes to numA will not affect numB because it was copied
console.log(numA); // 12
console.log(numB); // 5
When you refer to something by reference, your are referring/referencing it, not copying it. Any changes made to the original will affect everything that is referencing it.
var original = [1,2,3];
var ref = original; //Any changes made to 'original' will affect 'ref'
original.push('APPLES');
console.log(original); // [1,2,3,'APPLES']
console.log(ref); // [1,2,3,'APPLES']
In the above code ref does not actually contain any values. ref contains the memory location of original.
ref is referencing original.
Arrays and Objects are always passed/refereed to by reference.
Everything else is passed/refereed to by value (they are copied).
I want to make so that an object with the name of mainImage would work as a background image and no other object could pass it when the user would try to use "sendBackwards" method.
So, in my thinking I need to know that
1.If the mainImage is at index 0
2.If active selected object is at index 1 when I should not allow to use sendBackwards
This is what I tried so far, but I guess you will be able to see where this is failing:
$scope.getActiveIndex = function() {
for (var i = 0; i < canvas.fabric._objects.length; i++) {
console.log("not active");
if (canvas.fabric._objects[i].status === "active") {
console.log("active");
return i;
}
}
}
$scope.sendBackwards = function() {
var currentObject = canvas.fabric.getActiveObject();
for (var i = 0; i < canvas.fabric._objects.length; i++) {
console.log($scope.getActiveIndex());
if (canvas.fabric._objects[i].name === "mainImage" && $scope.getActiveIndex() == 1) {
console.log("Can not pass the background");
return;
} else {
canvas.fabric.sendBackwards(currentObject);
}
}
canvas.fabric.renderAll();
};
Thanks.
Solved this quite easily:
$scope.sendBackwards = function() {
var currentObject = canvas.fabric.getActiveObject();
var zIndex = canvas.fabric.getObjects().indexOf(currentObject);
if (zIndex == 1) {
return false;
} else {
canvas.fabric.sendBackwards(currentObject);
}
canvas.fabric.renderAll();
};
I have given a call for the below javascript function on drop down selection.
Basically what my requirement is that ,there can be a lot of vndrCd .
But,When ever the first time vndrCd is "SFGL", alert should not open .
If "SFGL is coming second time then an alert should come . I am not able to put this condition as the call to the method is at every click . Is there a way I can achive this .
function GetOptions(var1) {
varId = var1.id;
var vndrNbrCdList = document.getElementById('TouchCellDetailForm:vendorNbrCodeList').value;
var splitVndrList = vndrNbrCdList.split(',');
if (var1.value == '0') {
varhiddBox.value = '0';
return;
}
for (var j = 0; j < splitVndrList.length; j++) {
if (splitVndrList[j].split('-')[0] == (var1.value)) {
var vndrCd = splitVndrList[j].split('-')[1];
break;
}
}
localStorage.setItem("vendorName", vndrCd);
var vendorName1 = localStorage.getItem("vendorName");
if (vendorName1 == 'SFGL') {
alert("Salesforce vendor has already been selected.Please select some other vendor");
}
}
You can use closure to hold a private variable to indicate whether 'SFGL' has been encountered or not:
function GetOptionsHelp() {
'use strict';
let called = false;
return function GetOptions(var1) {
// ... your code block
if (vendorName1 == 'SFGL') {
if (called) {
alert("Salesforce vendor has already been selected.Please select some other vendor");
} else {
called = true;
}
}
}
And you can call the function like this GetOptionsHelp()(var1)
Try doing something like this as an easy solution:
(function () {
var firstTimeFlag = true;
function GetOptions(var1) {
varId = var1.id;
var vndrNbrCdList = document.getElementById('TouchCellDetailForm:vendorNbrCodeList').value;
var splitVndrList = vndrNbrCdList.split(',');
if (var1.value == '0') {
varhiddBox.value = '0';
return;
}
for (var j = 0; j < splitVndrList.length; j++) {
if (splitVndrList[j].split('-')[0] == (var1.value)) {
var vndrCd = splitVndrList[j].split('-')[1];
break;
}
}
localStorage.setItem("vendorName", vndrCd);
var vendorName1 = localStorage.getItem("vendorName");
if (vendorName1 == 'SFGL') {
if(!firstTimeFlag) {
alert("Salesforce vendor has already been selected.Please select some other vendor");
}
firstTimeFlag = false;
}
}
}());
I have a function which gets values from elements:
function getTransactionValues() {
var o = {};
o.reservations = [];
$('#custom-headers option:selected').each(function (i, selected) {
o.reservations[i] = $(selected).val();
});
o.amount = $('input[name=amount-price]').val();
o.currency_value = $('input[name=currency-value]').val();
o.currency_name = $('.currency_appendto option:selected').html();
o.actual_amount = $('input[name=actual-amount]').val();
o.actual_remaining = $('input[name=actual-remaining]').val();
o.funds_arrival_date = $('input[name=funds-arrival]').val();
o.paid_to = $('.paidto option:selected').html();
o.checkbox = $('.multi-transaction:checked').map(function () {
return this.value
}).get();
return o;
}
Now i want to check whether amount, actual_amount and funds_arrival_date are filled. if they are i will release the disabled class from a button. i've tried
var check_review = function () {
var a = getTransactionValues();
var options = [a.amount, a.actual_amount, a.funds_arrival_date];
for(i = 0; i < options.length; i++) {
if(options[i].length > 0) {
$('a[name=review_button]').removeClass('disabled');
}
else{
//this array is empty
alert('There is a problem!');
}
}
}
$('.test').click(function() {
check_review();
});
But it doesn't seems to be working..
Remove disabled attribute using JQuery?
Can you please look at above link, I think we should use $('.inputDisabled').prop("disabled", false);
Even if a single array element will be non empty then your code will remove the class disabled from the a. To make sure that all the elements of array are non empty and then only you want to remove the class then the way is:
for(i = 0; i < options.length; i++) {
if(options[i].length > 0) {
$('a[name=review_button]').removeClass('disabled');
}
else{
$('a[name=review_button]').addClass('disabled');
}
}
Or the other way is
var check = true;
for(i = 0; i < options.length; i++) {
if(options[i].length == 0) {
check = false;
}
}
if(check ) $('a[name=review_button]').removeClass('disabled');
Try using Array.prototype.every()
if (options.every(Boolean)) {
$("a[name=review_button]").removeClass("disabled");
} else {
// do other stuff
}