I`m trying to remove event listener from a tile that has been filled correctly on a sudoku game. But it is not working. After i fill the correct number on slot, i wish i wouldnt be able to click it again, but happens as the GIF show, it is erasing the grayed slots, meaning that removeEventListener is not working correctly. Any Help?
function qsa(selector) {
return document.querySelectorAll(selector);
}
function removeListenTiles() {
let tiles = qsa("tile");
for (let i = 0; i < tiles.length; i++) {
if (tiles[i].innerHTML.indexOf("table") == -1) {
if (tiles[i].textContent != "") {
tiles[i].removeEventListener("click", () => handleTile(tile[i), true);
}
}
}
}
function handleTile(tile) {
if (!disableSelect) {
if (tile.classList.contains("selected")) {
removeAllGrayedSelected();
updateMove();
removeSelected(tile);
selectedTile = null;
selectedNum = null;
} else {
removeAllGrayedSelected();
if (tile.innerHTML.indexOf("table") != -1) {
for (let j = 0; j < 81; j++) {
if (qsa(".tile")[j] !== tile) removeSelected(qsa(".tile")[j]);
}
tile.classList.add("selected");
selectedTile = tile;
updateSurround(tile);
updateMove();
}
}
}
}
function addListenTile(tile) {
tile.addEventListener("click", () => handleTile(tile), true);
}
Tried the currying method,and added tile.id to handlers array. Solved the problem.
const handlers = [];
const addListenTile = (tile) => {
tile.addEventListener("click", handlers[tile.id] = handleTile(tile), true);
};
const removeListenTile = (tile) => {
tile.removeEventListener("click", handlers[tile,id], true);
}
const handleTile = function (tile) {
return function onhandlerTile(event) {
console.log("OK1")
if (!disableSelect) {
if (tile.classList.contains("selected")) {
console.log("OK2");
removeAllGrayedSelected();
updateMove();
removeSelected(tile);
selectedTile = null;
selectedNum = null;
} else {
console.log("OK3");
removeAllGrayedSelected();
if (tile.innerHTML.indexOf("table") != -1) {
for (let j = 0; j < 81; j++) {
if (qsa(".tile")[j] !== tile) removeSelected(qsa(".tile")[j]);
}
tile.classList.add("selected");
selectedTile = tile;
updateSurround(tile);
updateMove();
}
}
}
};
};
I am trying to code an evolutionary neural network for the game snake. I already coded the neural network part and now I'd like to output the game of the best individual of every generation. For that I'm using the drawing library p5.js (https://p5js.org/).
In my code I am running a loop in where a new generation based on the last generation is created. Each individual of the generation will have to play the game and that is how they are rated. Now I want the best individual to be outputted after I let every individual play once.
Between each outputted turn of the game of the best individual I want the code to wait 500 milliseconds. How do I achieve that?
Here is the code I've already tried but here it only outputed the board after the last turn of each generation:
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
}
}
Another version but the waiting didn't work here:
let i = 0;
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
i++;
}, 1);
The code you provided should do what you asked for, I could only clean up some parts for you. Explain a bit better what is the problem you are facing.
// The function should be defined only once.
function drawBoard(board) { }
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
// Don't wait on first iteration
await new Promise(resolve => setTimeout(resolve, 500 * (turn ? 0 : 1 )));
drawBoard(bestGameTurns[turn]);
}
}
}
Original idea (discarded)
You can create a short function like that:
function pause(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Then in any async function you can call it like that:
async function () {}
// something happening
await pause(500);
// continue
}
The other idea
Now, the code in your question is not complete so this is kind of blind coding but.
So, first of all setInterval will be running the whole function every 1 millisecond (actually 4 ms, as this is the minimum in JS). Which means it will run those loops. I decided to focus on the part that was marked by you.
Instead of loop and trying to pause it, I ask that maybe rewriting the loop into function called each half second until condition is met would do?
Also, I move drawBoard outside
setInterval(async () => {
// ^^^^^^^^ <-- this should probably go
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
function tick(turn = 0) {
let board = bestGameTurns[turn];
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
drawBoard(board);
// here is "setTimeouted" loop
if (turn < bestGameTurns.length) {
setTimeout(tick, 500, turn + 1);
}
}
tick();
}, 1);
Thanks to everyone, your suggestions brought me to an idea. I found out that the problem was lying somewhere else. Because javascript only runs on the one thread (I think thats how its called), after we run over one generation, we have to stop that function to let another draw function, which runs every frame, draw the board. After it is drawn, the main function can continue. This is how it looks:
let isDrawn = false;
let currentBoard;
async function setup() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn], turn);
}
}
}
function step(board, turn) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
setTimeout(() => {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}, 1);
const nrOfCols = 10;
const nrOfRows = 10;
const fieldWidth = 20;
const nodeNrs = [24, 8, 8, 4];
const populationSize = 200;
const mutationRate = 0.01;
let population;
let game;
let isDrawn = false;
let currentBoard;
async function setup() {
createCanvas(500, 500);
population = new PopulationManager(populationSize);
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn]);
}
}
}
function step(board) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
function draw() {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}
function drawBoard(board) {
board.forEach((col, colNr) => {
col.forEach((field, rowNr) => {
fill(field.isSnake ? "green" : field.isFruit ? "red" : "black");
stroke(255);
rect(colNr*fieldWidth, rowNr*fieldWidth, fieldWidth, fieldWidth);
});
});
}
function play(game) {
setInterval(() => {
if (!game.lost) {
game.nextTurn();
drawBoard(game.board);
} else {
clearInterval(1);
}
}, 500);
}
class PopulationManager {
constructor(populationSize) {
this.population = createPopulation();
function createPopulation() {
let population = [];
for (let i = 0; i < populationSize; i++) {
let chromosomes = createRandomChromosomes();
let i = new Individual(chromosomes);
population.push(i);
}
return population;
function createRandomChromosomes() {
let arr = [];
let nrOfChromosomes = calcNrOfChromosomes();
for (let i = 0; i < nrOfChromosomes; i++)
arr.push(Math.random()*2-1);
return arr;
function calcNrOfChromosomes() {
let nr = 0;
for (let i = 0; i < nodeNrs.length - 1; i++)
nr += (nodeNrs[i] + 1)*nodeNrs[i + 1];
return nr;
}
}
};
}
createNewGeneration() {
let that = this;
getFitnessOfPop();
this.calcAvgFitness();
this.findBestIndividual();
let parents = selection();
breed(parents);
function getFitnessOfPop() {
that.population.forEach(iv => {
iv.fitness = iv.playGame();
});
that.population.sort((a, b) => a.fitness - b.fitness);
}
function selection() {
let totalFitness = that.population.map(iv => iv.fitness/* + 1 */).reduce((a,b) => a + b);
let allParents = [];
for (let i = 0; i < that.population.length/2; i++) {
allParents.push(selectRandomParents());
}
return allParents;
function selectRandomParents() {
let p1, p2;
do {
p1 = selectRandomParent();
p2 = selectRandomParent();
} while (p1 == p2);
return [p1, p2];
function selectRandomParent() {
let rdm = Math.random()*totalFitness;
return that.population.find((iv, i) => {
let sum = that.population.filter((iv2, i2) => i2 <= i).map(iv => iv.fitness /* + 1 */).reduce((a,b) => a + b);
return rdm <= sum;
});
}
}
}
function breed(allParents) {
that.population = [];
allParents.forEach(ps => {
let childChromosomes = crossOver(ps[0].chromosome, ps[1].chromosome);
childChromosomes = [mutate(childChromosomes[0]), mutate(childChromosomes[1])];
let child1 = new Individual(childChromosomes[0]);
let child2 = new Individual(childChromosomes[1]);
that.population.push(child1);
that.population.push(child2);
});
function crossOver(parent1Chromosome, parent2Chromosome) {
let crossingPoint = Math.round(Math.random()*parent1Chromosome.length);
let divided1 = divideChromosome(parent1Chromosome);
let divided2 = divideChromosome(parent2Chromosome);
let child1Chromosome = divided1[0].concat(divided2[1]);
let child2Chromosome = divided2[0].concat(divided1[1]);
return [child1Chromosome, child2Chromosome];
function divideChromosome(chromosome) {
let part1 = chromosome.filter((g, i) => i <= crossingPoint);
let part2 = chromosome.filter((g, i) => i > crossingPoint);
return [part1, part2];
}
}
function mutate(chromosome) {
chromosome = chromosome.map(g => {
if (Math.random() < mutationRate)
return Math.random()*2-1;
return g;
});
return chromosome;
}
}
}
calcAvgFitness() {
this.avgFitness = this.population.map(iv => iv.fitness).reduce((a, b) => a + b) / this.population.length;
}
findBestIndividual() {
let bestFitness = -1, bestIndividual;
this.population.forEach(i => {
if (i.fitness > bestFitness) {
bestFitness = i.fitness;
bestIndividual = i;
}
});
this.bestIndividual = bestIndividual;
}
}
class Individual {
constructor(chromosome) {
this.chromosome = chromosome;
this.fitness = 0;
this.game = createGame();
function createGame() {
let weights = convertChromosomeToWeights();
let game = new Game(weights);
return game;
function convertChromosomeToWeights() {
let weights = [];
for (let i = 0; i < nodeNrs.length - 1; i++) {
let lArr = [];
for (let j = 0; j < nodeNrs[i] + 1; j++) {
let nArr = [];
lArr.push(nArr);
}
weights.push(lArr);
}
chromosome.forEach((gene, geneIdx) => {
let lIdx = -1, minIdx, maxIdx = 0;
for (let i = 0; i < nodeNrs.length - 1; i++) {
let nr = 0;
for (let j = 0; j <= i; j++)
nr += (nodeNrs[j] + 1)*nodeNrs[j + 1];
if (geneIdx < nr) {
lIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let nIdx = -1;
for (let i = 0; i < nodeNrs[lIdx] + 1; i++) {
let nr = minIdx + nodeNrs[lIdx + 1];;
if (geneIdx < nr) {
nIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let wIdx = -1;
for (let i = 0; i < nodeNrs[lIdx + 1]; i++) {
let nr = minIdx + 1;
if (geneIdx < nr) {
wIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
weights[lIdx][nIdx][wIdx] = gene;
});
return weights;
}
}
}
playGame() {
while (!this.game.lost) {
this.game.nextTurn();
}
return this.game.score;
}
}
class Game {
constructor(weights) {
let that = this;
this.chromosome = flattenArray(weights);
this.nn = new NeuralNetwork(weights);
this.turnNr = 0;
this.score = 0;
this.lost = false;
this.board = createBoard();
this.snake = new Snake();
setupSnake();
this.createFruit();
this.turns = [JSON.parse(JSON.stringify(this.board))];
function createBoard() {
let board = [];
for (let colNr = 0; colNr < nrOfCols; colNr++) {
board[colNr] = [];
for (let rowNr = 0; rowNr < nrOfRows; rowNr++) {
let field = new Field(colNr, rowNr);
board[colNr][rowNr] = field;
}
}
return board;
}
function setupSnake() {
for (let i = 0; i < 4; i++)
that.addToTail([floor(nrOfCols/2) - i, floor(nrOfRows/2)]);
that.length = that.snake.body.length;
}
function flattenArray(arr) {
let flattened = [];
flatten(arr);
return flattened;
function flatten(arr) {
arr.forEach(e => {
if (Array.isArray(e))
flatten(e);
else
flattened.push(e);
});
}
}
}
addToTail(pos) {
this.snake.body.push(pos);
this.board[pos[0]][pos[1]].setSnake(true);
}
nextTurn() {
let that = this;
let direction = findDirection();
this.moveSnake(direction);
this.turns.push(JSON.parse(JSON.stringify(this.board)));
this.turnNr++;
checkEat();
function findDirection() {
let inputValues = [];
for (let i = 0; i < 8; i++) {
let distances = that.snake.look(i, that.board);
inputValues.push(distances.distToFruit);
inputValues.push(distances.distToWall);
inputValues.push(distances.distToBody);
}
let output = that.nn.getOutput(inputValues);
let probability = -1;
let direction = -1;
output.forEach((v, vIdx) => {
if (v > probability) {
probability = v;
direction = vIdx;
}
});
return direction;
}
function checkEat() {
let head = that.snake.body[0];
let headField = that.board[head[0]][head[1]];
if (headField.isFruit) {
that.snake.eat();
that.score++;
headField.setFruit(false);
that.createFruit();
}
}
}
createFruit() {
let field;
do {
let colNr = floor(random()*nrOfCols);
let rowNr = floor(random()*nrOfRows);
field = this.board[colNr][rowNr];
} while(field.isSnake);
field.setFruit(true);
}
moveSnake(newDirection) {
let that = this;
let oldBody = JSON.parse(JSON.stringify(that.snake.body));
moveTail();
makeSnakeLonger();
moveHead();
function moveTail() {
for (let i = oldBody.length - 1; i > 0; i--)
that.snake.body[i] = oldBody[i - 1];
}
function moveHead() {
let newHeadPosition = findNewHeadPos();
if (
newHeadPosition[0] >= nrOfCols || newHeadPosition[0] < 0 ||
newHeadPosition[1] >= nrOfRows || newHeadPosition[1] < 0
) {
that.lose();
return;
}
let newHeadField = that.board[newHeadPosition[0]][newHeadPosition[1]];
if (newHeadField.isSnake) {
that.lose();
return;
}
addNewHeadPos(newHeadPosition);
}
function findNewHeadPos() {
if (newDirection == 0) { //up
return [oldBody[0][0], oldBody[0][1] - 1];
} else if (newDirection == 1) { //right
return [oldBody[0][0] + 1, oldBody[0][1]];
} else if (newDirection == 2) { //down
return [oldBody[0][0], oldBody[0][1] + 1];
} else if (newDirection == 3) { //left
return [oldBody[0][0] - 1, oldBody[0][1]];
}
}
function makeSnakeLonger() {
if (that.snake.length > that.snake.body.length) {
that.addToTail(oldBody[oldBody.length - 1]);
} else {
removeFromTail(oldBody[oldBody.length - 1]);
}
}
function removeFromTail(pos) {
that.board[pos[0]][pos[1]].setSnake(false);
}
function addNewHeadPos(pos) {
that.snake.body[0] = pos;
that.board[pos[0]][pos[1]].setSnake(true);
}
}
lose() {
this.lost = true;
}
}
class Field {
constructor(col, row) {
this.col = col;
this.row = row;
this.isFruit = false;
this.isSnake = false;
}
setFruit(bool) {
this.isFruit = bool;
}
setSnake(bool) {
this.isSnake = bool;
}
}
class Snake {
constructor() {
this.body = [];
this.length = 4;
}
eat() {
this.length++;
}
look(direction, board) {
let distances = {distToFruit: 0, distToWall: 0, distToBody: 0};
let xDiff = getXDiff(direction), yDiff = getYDiff(direction);
let pos = [this.body[0][0] + xDiff, this.body[0][1] + yDiff];
let dist = 1;
while (pos[0] > 0 && pos[0] < nrOfRows && pos[1] > 0 && pos[1] < nrOfCols) {
if (board[pos[0]][pos[1]].isFruit && distances.distToFruit == 0) distances.distToFruit = dist;
if (board[pos[0]][pos[1]].isSnake && distances.distToBody == 0) distances.distToBody = dist;
pos[0] += xDiff, pos[1] += yDiff;
dist++;
}
distances.distToWall = dist;
return distances;
function getXDiff(direction) {
if (direction == 5 || direction == 6 || direction == 7) return -1;
else if (direction == 1 || direction == 2 || direction == 3) return 1;
return 0;
}
function getYDiff(direction) {
if (direction == 7 || direction == 0 || direction == 1) return -1;
else if (direction == 3 || direction == 4 || direction == 5) return 1;
return 0;
}
}
}
class NeuralNetwork {
constructor(weights) {
this.layers = createLayers();
this.layers = addWeights(this.layers, weights);
function createLayers() {
let layers = [];
let nrOfNodesGlobal;
nodeNrs.forEach((nrOfNodes, lNr) => {
nrOfNodesGlobal = nrOfNodes;
layers[lNr] = [];
for (let i = 0; i < nrOfNodes; i++) {
let node = createNode(lNr);
layers[lNr][i] = node;
}
if (lNr != nodeNrs.length - 1)
layers[lNr].push(new Bias());
});
return layers;
function createNode(lNr) {
if (lNr == 0) return new InputLayerNode();
else if (lNr == nrOfNodesGlobal - 1) return new OutputLayerNode();
else return new HiddenLayerNode();
}
}
function addWeights(layers, weights) {
for (let lNr = 0; lNr < layers.length - 1; lNr++) {
let l = layers[lNr];
l.forEach((n1, nNr) => {
for (let n2Nr = 0; n2Nr < layers[lNr+1].length - 1; n2Nr++) { //not including bias of next layer
let n2 = layers[lNr+1][n2Nr];
let weight = weights[lNr][nNr][n2Nr];
let w = new Weight(n1, n2, weight);
n1.addWeight(w);
}
});
}
return layers;
}
}
getOutput(inputValues) {
let output = [];
this.layers[0].forEach((inputNeuron, nNr) => {
if (nNr != this.layers[0].length - 1)
inputNeuron.addToInput(inputValues[nNr]);
});
this.layers.forEach((l, lNr) => {
calcOutputs(l);
if (lNr != this.layers.length - 1) {
l.forEach(n => {
n.feedForward();
});
} else {
output = l.map(n => n.output);
}
});
return output;
function calcOutputs(layer) {
layer.forEach(n => n.output = n.activationFunction(n.summedInput, layer.map(N => N.summedInput)));
}
}
log() {
console.log(this.weights, this.nodes);
}
}
class Node {
constructor() {
this.weights = [];
this.summedInput = 0;
}
addWeight(w) {
this.weights.push(w);
}
addToInput(input) {
if (input == NaN)
console.log("A");
this.summedInput += input;
}
feedForward() {
this.weights.forEach((w, wNr) => {
let input = w.weight*this.output;
w.to.addToInput(input);
});
}
}
class Bias extends Node {
constructor() {
super();
this.output = 1;
}
activationFunction(x, allXs) {
return x;
}
}
class InputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return x;
}
}
class HiddenLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return leakyReLU(x);
}
}
class OutputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return softmax(x, allXs);
}
}
class Weight {
constructor(from, to, weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
setWeight(newWeight) {
this.weight = weight;
}
}
function leakyReLU(x) {
if (x >= 0) return x;
else return 0.01*x;
}
function softmax(x, allXs) {
return Math.exp(x) / allXs.map(X => Math.exp(X)).reduce((a, b) => a+b);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
It's not working that well but a few improvements should make it better...
If you have any suggestions for improvements of the code, please let me know!
I tried to fix it into steps as I said in comments,I hope I have no mistake :
let i = 0;
async function step(bestGameTurns, turn)
{
if (turn == bestGameTurns.length)
return;
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(() => step(bestGameTurns, turn+1), 500));
await p;
}
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
step(bestGameTurns, 0);
i++;
}, 1);
I am trying to get an implementation of Dijkstra's algorithm in JavaScript to run properly.
I found a walkthrough Here and do not understand the return line
return `Path is ${path} and time is ${times[endNode]}
I have collected the code from the link below to make it easier.
class Graph {
constructor() {
this.nodes = [];
this.adjacencyList = [];
}
addNode(node) {
this.nodes.push(node);
this.adjacencyList[node] = [];
}
addEdge(node1, node2, weight) {
this.adjacencyList[node1].push({node:node2, weight: weight});
this.adjacencyList[node2].push({node:node1, weight: weight});
}
findPathWithDijkstra(startNode, endNode) {
let times = [];
let backtrace = [];
let pq = new PriorityQueue();
times[startNode] = 0;
this.nodes.forEach(node => {
if (node !== startNode) {
times[node] = Infinity
}
});
pq.enqueue([startNode, 0]);
while (!pq.isEmpty()) {
let shortestStep = pq.dequeue();
let currentNode = shortestStep[0];
this.adjacencyList[currentNode].forEach(neighbor => {
let time = times[currentNode] + neighbor.weight;
if (time < times[neighbor.node]) {
times[neighbor.node] = time;
backtrace[neighbor.node] = currentNode;
pq.enqueue([neighbor.node, time]);
}
});
}
let path = [endNode];
let lastStep = endNode;
while(lastStep !== startNode) {
path.unshift(backtrace[lastStep])
lastStep = backtrace[lastStep]
}
return `Path is ${path} and time is ${times[endNode] }`
}
}
class PriorityQueue {
constructor() {
this.collection = [];
}
enqueue(element){
if (this.isEmpty()){
this.collection.push(element);
} else {
let added = false;
for (let i = 1; i <= this.collection.length; i++){
if (element[1] < this.collection[i-1][1]){
this.collection.splice(i-1, 0, element);
added = true;
break;
}
}
if (!added){
this.collection.push(element);
}
}
};
dequeue() {
let value = this.collection.shift();
return value;
};
isEmpty() {
return (this.collection.length === 0)
};
}
let testGraph01 = new Graph();
let testGraph02 = new Graph();
let testGraph03 = new Graph();
//creating graphs
testGraph01.addNode("A");
testGraph01.addNode("B");
testGraph01.addNode("C");
testGraph01.addNode("D");
testGraph01.addEdge("A","B",10);
testGraph01.addEdge("C","D",5);
testGraph01.addEdge("D","B",5);
console.log(testGraph01.findPathWithDijkstra("A","D"));
I am looking for an explanation of the templated return. How does it work in javascript i guess?
In the fiddle is a "class" I have written to manage navigation over the data model and a test which shows that multiple instances (starting from second) of this "class" are referencing something wrong.
https://jsfiddle.net/btvmnaxc/
(outputs to console)
Expected output would be
[{"name":"xx"}]
[{"name":"yy"}]
But after setting Elements via setElements, in other methods Elements is empty, strangely only after creating the second instance. I could think that setElements overwrites the reference, but why other methods keep this old reference instead of getting a new one from the var.
Could somebody explain this behavior?
P.S. I probably can think on a solution, as packing vars in a property which is an object.
function Pagination() {
var props = {Elements:[], ...}
}
P.S.S
function Pagination() {
var that = this;
var Elements = [0,1];
var Frame = [];
var FrameNumber = 0;
var EntitiesPerFrame = 25;
var FrameChangedCB = [];
this.subscribeFrameChange = function(cb) {
if (typeof cb === "function") {
FrameChangedCB.push(cb);
} else {
throw new Error("Not a function");
}
}
this.setEntitiesPerFrame = function(entities_per_frame) {
entities_per_frame = parseInt(entities_per_frame);
if (entities_per_frame > 0) {
EntitiesPerFrame = entities_per_frame;
while (!this.canDisplayFrame(FrameNumber) && FrameNumber > 0) {
FrameNumber--;
}
calculateFrame();
}
}
frameChanged = function() {
FrameChangedCB.forEach(function(cb) {
cb();
});
}
this.setElements = function(elements) {
if (Array.isArray(elements)) {
Elements = elements;
calculateFrame();
console.log("qq");
} else {
throw new Error("Can only work with arrays");
}
}
this.getStart = function() {
return FrameNumber * EntitiesPerFrame;
}
this.getEnd = function() {
var end = (FrameNumber + 1) * EntitiesPerFrame;
return end > Elements.length ? Elements.length : end;
}
this.getEntitiesPerFrame = function() {
return EntitiesPerFrame;
}
calculateFrame = function() {
var start = that.getStart();
var end = that.getEnd();
if (that.canDisplayFrame(FrameNumber)) {
Frame = Elements.slice(
start,
end
);
frameChanged();
} else {
throw new Error("Boundaries");
}
}
this.canDisplayFrame = function(nr) {
nr = parseInt(nr);
var can = false;
var start = nr * EntitiesPerFrame
var end = (nr + 1) * EntitiesPerFrame;
if (start <= Elements.length && nr >= 0) {
can = true;
}
return can;
}
this.getFrame = function() {
return Frame;
}
this.next = function() {
return this.goto(FrameNumber + 1);
}
this.prev = function() {
return this.goto(FrameNumber - 1);
}
this.goto = function(frame_nr) {
var changed = false;
if (that.canDisplayFrame(frame_nr)) {
FrameNumber = parseInt(frame_nr);
calculateFrame();
changed = true;
}
return changed;
}
this.getLength = function() {
return Elements.length;
}
}
var b = new Pagination();
var a = new Pagination();
a.setElements([{name: 'xx'}]);
b.setElements([{name: 'yy'}]);
console.log(JSON.stringify(a.getFrame()));
console.log(JSON.stringify(b.getFrame()));
This is happening because you are abusing implicit globals.
Your Pagination function contains two places where a function is assigned to an identifier without using var:
calculateFrame = function() {
var start = that.getStart();
var end = that.getEnd();
if (that.canDisplayFrame(FrameNumber)) {
Frame = Elements.slice(
start,
end
);
frameChanged();
} else {
throw new Error("Boundaries");
}
}
This will assign this function to a global variable named calculateFrame and any call to calculateFrame() will be calling whichever of those was assigned last (and therefore be using whatever scope it has access to).
To fix this, use var:
var calculateFrame = function() {
var start = that.getStart();
var end = that.getEnd();
if (that.canDisplayFrame(FrameNumber)) {
Frame = Elements.slice(
start,
end
);
frameChanged();
} else {
throw new Error("Boundaries");
}
}
Or better yet, use a named function declaration:
function calculateFrame() {
var start = that.getStart();
var end = that.getEnd();
if (that.canDisplayFrame(FrameNumber)) {
Frame = Elements.slice(
start,
end
);
frameChanged();
} else {
throw new Error("Boundaries");
}
}
After fixing the two places where you have this issue, the snippet outputs the expected result.
function Pagination() {
var that = this;
var Elements = [0, 1];
var Frame = [];
var FrameNumber = 0;
var EntitiesPerFrame = 25;
var FrameChangedCB = [];
this.subscribeFrameChange = function(cb) {
if (typeof cb === "function") {
FrameChangedCB.push(cb);
} else {
throw new Error("Not a function");
}
}
this.setEntitiesPerFrame = function(entities_per_frame) {
entities_per_frame = parseInt(entities_per_frame);
if (entities_per_frame > 0) {
EntitiesPerFrame = entities_per_frame;
while (!this.canDisplayFrame(FrameNumber) && FrameNumber > 0) {
FrameNumber--;
}
calculateFrame();
}
}
function frameChanged() {
FrameChangedCB.forEach(function(cb) {
cb();
});
}
this.setElements = function(elements) {
if (Array.isArray(elements)) {
Elements = elements;
calculateFrame();
console.log("qq");
} else {
throw new Error("Can only work with arrays");
}
}
this.getStart = function() {
return FrameNumber * EntitiesPerFrame;
}
this.getEnd = function() {
var end = (FrameNumber + 1) * EntitiesPerFrame;
return end > Elements.length ? Elements.length : end;
}
this.getEntitiesPerFrame = function() {
return EntitiesPerFrame;
}
function calculateFrame() {
var start = that.getStart();
var end = that.getEnd();
if (that.canDisplayFrame(FrameNumber)) {
Frame = Elements.slice(
start,
end
);
frameChanged();
} else {
throw new Error("Boundaries");
}
}
this.canDisplayFrame = function(nr) {
nr = parseInt(nr);
var can = false;
var start = nr * EntitiesPerFrame
var end = (nr + 1) * EntitiesPerFrame;
if (start <= Elements.length && nr >= 0) {
can = true;
}
return can;
}
this.getFrame = function() {
return Frame;
}
this.next = function() {
return this.goto(FrameNumber + 1);
}
this.prev = function() {
return this.goto(FrameNumber - 1);
}
this.goto = function(frame_nr) {
var changed = false;
if (that.canDisplayFrame(frame_nr)) {
FrameNumber = parseInt(frame_nr);
calculateFrame();
changed = true;
}
return changed;
}
this.getLength = function() {
return Elements.length;
}
}
var b = new Pagination();
var a = new Pagination();
a.setElements([{
name: 'xx'
}]);
b.setElements([{
name: 'yy'
}]);
console.log(a.getFrame());
console.log(b.getFrame());
I have an ASP.NET page, written in VB.NET, that I'm trying to use javascript on. The script takes the value from one listbox and inserts it into another list box. I'm using a master page, which I'm pretty sure is the issue.
Here's the javascript:
function OT_transferLeft() { moveSelectedOptions(this.right, this.left, this.autoSort, this.staticOptionRegex); this.update(); }
function OT_transferRight() { moveSelectedOptions(this.left, this.right, this.autoSort, this.staticOptionRegex); this.update(); }
function OT_transferAllLeft() { moveAllOptions(this.right, this.left, this.autoSort, this.staticOptionRegex); this.update(); }
function OT_transferAllRight() { moveAllOptions(this.left, this.right, this.autoSort, this.staticOptionRegex); this.update(); }
function OT_saveRemovedLeftOptions(f) { this.removedLeftField = f; }
function OT_saveRemovedRightOptions(f) { this.removedRightField = f; }
function OT_saveAddedLeftOptions(f) { this.addedLeftField = f; }
function OT_saveAddedRightOptions(f) { this.addedRightField = f; }
function OT_saveNewLeftOptions(f) { this.newLeftField = f; }
function OT_saveNewRightOptions(f) { this.newRightField = f; }
function OT_update() {
var removedLeft = new Object();
var removedRight = new Object();
var addedLeft = new Object();
var addedRight = new Object();
var newLeft = new Object();
var newRight = new Object();
for (var i = 0; i < this.left.options.length; i++) {
var o = this.left.options[i];
newLeft[o.value] = 1;
if (typeof (this.originalLeftValues[o.value]) == "undefined") {
addedLeft[o.value] = 1;
removedRight[o.value] = 1;
}
}
for (var i = 0; i < this.right.options.length; i++) {
var o = this.right.options[i];
newRight[o.value] = 1;
if (typeof (this.originalRightValues[o.value]) == "undefined") {
addedRight[o.value] = 1;
removedLeft[o.value] = 1;
}
}
if (this.removedLeftField != null) { this.removedLeftField.value = OT_join(removedLeft, this.delimiter); }
if (this.removedRightField != null) { this.removedRightField.value = OT_join(removedRight, this.delimiter); }
if (this.addedLeftField != null) { this.addedLeftField.value = OT_join(addedLeft, this.delimiter); }
if (this.addedRightField != null) { this.addedRightField.value = OT_join(addedRight, this.delimiter); }
if (this.newLeftField != null) { this.newLeftField.value = OT_join(newLeft, this.delimiter); }
if (this.newRightField != null) { this.newRightField.value = OT_join(newRight, this.delimiter); }
}
function OT_join(o, delimiter) {
var val; var str = "";
for (val in o) {
if (str.length > 0) { str = str + delimiter; }
str = str + val;
}
return str;
}
function OT_setDelimiter(val) { this.delimiter = val; }
function OT_setAutoSort(val) { this.autoSort = val; }
function OT_setStaticOptionRegex(val) { this.staticOptionRegex = val; }
function OT_init(theform) {
this.form = theform;
if (!theform[this.left]) { alert("OptionTransfer init(): Left select list does not exist in form!"); return false; }
if (!theform[this.right]) { alert("OptionTransfer init(): Right select list does not exist in form!"); return false; }
this.left = theform[this.left];
this.right = theform[this.right];
for (var i = 0; i < this.left.options.length; i++) {
this.originalLeftValues[this.left.options[i].value] = 1;
}
for (var i = 0; i < this.right.options.length; i++) {
this.originalRightValues[this.right.options[i].value] = 1;
}
if (this.removedLeftField != null) { this.removedLeftField = theform[this.removedLeftField]; }
if (this.removedRightField != null) { this.removedRightField = theform[this.removedRightField]; }
if (this.addedLeftField != null) { this.addedLeftField = theform[this.addedLeftField]; }
if (this.addedRightField != null) { this.addedRightField = theform[this.addedRightField]; }
if (this.newLeftField != null) { this.newLeftField = theform[this.newLeftField]; }
if (this.newRightField != null) { this.newRightField = theform[this.newRightField]; }
this.update();
}
// -------------------------------------------------------------------
// OptionTransfer()
// This is the object interface.
// -------------------------------------------------------------------
function OptionTransfer(l, r) {
this.form = null;
this.left = l;
this.right = r;
this.autoSort = true;
this.delimiter = ",";
this.staticOptionRegex = "";
this.originalLeftValues = new Object();
this.originalRightValues = new Object();
this.removedLeftField = null;
this.removedRightField = null;
this.addedLeftField = null;
this.addedRightField = null;
this.newLeftField = null;
this.newRightField = null;
this.transferLeft = OT_transferLeft;
this.transferRight = OT_transferRight;
this.transferAllLeft = OT_transferAllLeft;
this.transferAllRight = OT_transferAllRight;
this.saveRemovedLeftOptions = OT_saveRemovedLeftOptions;
this.saveRemovedRightOptions = OT_saveRemovedRightOptions;
this.saveAddedLeftOptions = OT_saveAddedLeftOptions;
this.saveAddedRightOptions = OT_saveAddedRightOptions;
this.saveNewLeftOptions = OT_saveNewLeftOptions;
this.saveNewRightOptions = OT_saveNewRightOptions;
this.setDelimiter = OT_setDelimiter;
this.setAutoSort = OT_setAutoSort;
this.setStaticOptionRegex = OT_setStaticOptionRegex;
this.init = OT_init;
this.update = OT_update;
}
var lb1 = document.getElementById("<%=lbSiteType.ClientID%>");
var lb2 = document.getElementById("<%=lbSelectedSiteType.ClientID%>");
var opt = new OptionTransfer(lb1, lb2);
alert(opt);
opt.setAutoSort(true);
opt.setDelimiter(",");
opt.setStaticOptionRegex("^(Bill|Bob|Matt)$");
opt.saveRemovedLeftOptions("removedLeft");
opt.saveRemovedRightOptions("removedRight");
opt.saveAddedLeftOptions("addedLeft");
opt.saveAddedRightOptions("addedRight");
opt.saveNewLeftOptions("newLeft");
opt.saveNewRightOptions("newRight");
and here's the code from the control:
<asp:Button ID="btnMoveAll" Text=" >> " CssClass="button7" CausesValidation="false"
ONCLICK="opt.transferRight()"
runat="server" /><br />
In short, it doesn't work. I keep getting an 'opt' is not a member of the page. Can someone explain how I can properly call this code?
To invoke client Methods(javascript) use OnClientClick
<asp:Button ID="btnMoveAll" Text="" CssClass="button7" CausesValidation="false"
OnClientClick="opt.transferRight()"
runat="server" />