JavaScript Need help writing shouldQuarantine prototype - javascript

Need this to determine if any of the passengers are isHealthy =false then quarantine the wagon. I may have an issue on the join prototype as well. The isHealthy is only triggered if they eat and have no food. So it is possible for them to eat, and then have no food but not trigger isHealthy.
I am very new to this please be patient.
const Traveler = function (travelerName) {
this.name = travelerName;
this.food = 1;
this.isHealthy = true;
};
Traveler.prototype.hunt = function () {
this.food += 2;
console.log(this.food);
};
Traveler.prototype.eat = function () {
this.food -= 1;
if (this.food === 1) {
} else {
this.food === 0;
this.isHealthy = false;
}
console.log(this.food);
};
console.log(new Traveler("John"));
function Wagon(capacity) {
this.capacity = capacity;
this.passengers = [];
}
console.log(new Wagon(4));
Wagon.prototype.getAvailableSeatCount = function () {
let seatingCapacity = this.capacity - this.passengers.length;
console.log(seatingCapacity);
return seatingCapacity;
};
Wagon.prototype.join = function (traveler) {
console.log(this.capacity);
let currentCapacity = this.capacity;
if (currentCapacity <= this.passengers.length) {
this.currentCapacity = 0;
} else if (this.getAvailableSeatCount != 0) {
this.passengers.push(traveler);
}
console.log(this.passengers);
};
Wagon.prototype.shouldQuarantine = function () {
for (let i = 0; i < this.passengers.length; i++) {
if (this.passengers[i].isHealthy) {
return false;
}
}
};
Wagon.prototype.totalFood = function () {
let totalFood = "";
this.passengers.forEach(this.food);
console.log(this.food);
};

In you eat method of the Traveler class, first check if there is any food. If there is, then subtract one and check if the food is now empty. If it is then set isHealthy to false.
Traveler.prototype.eat = function () {
if (this.food > 0) {
this.food -= 1;
if (this.food === 0) {
this.isHealthy = false;
}
}
};
Subsequently you should also modify your hunt method to make your traveler healthy again after hunting.
Traveler.prototype.hunt = function () {
this.food += 2;
this.isHealthy = true;
};
In the shouldQuarantine method of the Wagon class, instead of checking if all passengers are healthy, check if anyone of them is not healthy and return true if that is the case.
If everyone is healthy, the loop will finish. Return false after the loop.
Wagon.prototype.shouldQuarantine = function () {
for (let i = 0; i < this.passengers.length; i++) {
if (!this.passengers[i].isHealthy) {
return true;
}
}
return false;
};
Alternatively you could use the some method on the this.passengers array to check if any of the passenger isn't healthy.
Wagon.prototype.shouldQuarantine = function () {
return this.passengers.some(
passenger => passenger.isHealthy === false
);
};
The join method can be simplyfied. You only need the result from this.getAvailableSeatCount() to see if there is any room. Add the traveler if the result is not 0.
Wagon.prototype.join = function (traveler) {
const availableSeats = this.getAvailableSeatCount();
if (availableSeats !== 0) {
this.passengers.push(traveler);
}
};
I also noticed that the totalFood method doesn't work as expected, but I'll let this one up to you. Hint: totalFood should be a number. Loop over every passenger and add the amount of food to the totalFood value.

Related

Why are my functions executing out of order at the end of this Connect Four game? It works in some browsers

I'm having two timing issues here, both involving the process in this game once the winning move has been made: https://codepen.io/acchang/pen/XWePpWB
Ideally, I should (1) pick the winning space (2) see the winning space filled (3) have the alert proclaim the winner.
What I see and do not like is:
*checkForWinners() runs
winDeclared() runs and the alert "winner" pop up first
Then after the alert is cleared, drawboard() runs, adding the winning piece to the gameboard.
This does not happen as badly in Firefox. The piece is added at the same time the alert pops up.
Then, in winDeclared(), I also change the display in the top right to also indicate the winner. But swapTurns() seems to execute before winDeclared().
Is that because winDeclared() is two functions deep into checkForWinners()? Is there a way to delay it?
Thanks!
let gameboard = [
[1,2,3,4,5,6,7],
[8,9,10,11,12,13,14],
[15,16,17,18,19,20,21],
[22,23,24,25,26,27,28],
[29,30,31,32,33,34,35],
[36,37,38,39,40,41,42]
];
let playerOne
let playerTwo
let indexPick
let availableSpots
let gameType
let playerOneTurn = true
document.getElementsByName("announcements")[0].innerHTML = "Current Player: " + whosPlaying() + " "
let itsAOnePlayerGame = true
let isThereAWinner = false
let mainDiv = document.createElement("div");
mainDiv.setAttribute('class', 'mainDiv')
document.body.append(mainDiv);
let selectorHolder = document.createElement("div")
selectorHolder.setAttribute('class', 'selectorHolder')
selectorHolder.setAttribute('id', 'selectorHolder')
mainDiv.append(selectorHolder)
let selectorTable = document.createElement("table")
selectorTable.setAttribute('class', 'selectorTable')
selectorTable.setAttribute('id', 'selectorTable')
selectorHolder.append(selectorTable)
function drawSelector() {
let selectorRow = document.createElement("tr")
selectorRow.setAttribute('class', 'selectorRow')
selectorTable.append(selectorRow)
for (i=0; i<7; i++){
let selectorCell = document.createElement("td")
selectorCell.setAttribute('class', 'selectorCell')
let innerSelectorCell = document.createElement("div")
innerSelectorCell.setAttribute('class', 'innerSelectorCell')
innerSelectorCell.setAttribute('id', [i])
selectorCell.append(innerSelectorCell)
innerSelectorCell.addEventListener("mouseover", function(event) {
if (playerOneTurn == true) {
innerSelectorCell.classList.add('yellowBG')}
else {innerSelectorCell.classList.add('redBG')
}
})
innerSelectorCell.addEventListener("mouseout", function(event) {
if (playerOneTurn == true) {
innerSelectorCell.classList.remove('yellowBG')}
else {innerSelectorCell.classList.remove('redBG')
}
})
innerSelectorCell.onclick = function(){
if (isThereAWinner == true){return}
else {
indexPick = parseInt(this.id)
console.log(indexPick)
claimSpot()
}
}
selectorRow.append(selectorCell)
}
};
drawSelector()
// Draw Main Gameboard
let mainTable = document.createElement("table");
mainTable.setAttribute('class', 'mainTable')
mainDiv.append(mainTable)
function drawBoard() {
for (i=0; i<gameboard.length; i++){
let row = document.createElement("tr")
mainTable.append(row)
for (j=0; j<gameboard[i].length; j++){
let outerCell = document.createElement('td')
outerCell.setAttribute('class', 'outerCell')
row.append(outerCell)
let innerCell = document.createElement('div')
innerCell.setAttribute('class', 'innerCell')
innerCell.classList.add(gameboard[i][j])
innerCell.setAttribute('innerHTML', gameboard[i][j])
outerCell.append(innerCell)
}
}
};
drawBoard()
function validateRadio() {
let ele = document.getElementsByName('gameType');
for(i = 0; i < ele.length; i++) {
if(ele[i].checked){
gameType = (ele[i].value)
beginGame()
}
}
};
function beginGame() {
if (gameType == "1PEasy"){
itsAOnePlayerGame = true
resetBoard()
onePlayerPickSides()
play1PGame()
}
else if (gameType == "1PHard"){
itsAOnePlayerGame = true
resetBoard()
onePlayerPickSides()
play1PGame()
}
else if (gameType == "2P"){
itsAOnePlayerGame = false
resetBoard()
twoPlayerPickSides()
play2PGame()
}
};
function resetBoard() {
playerOneTurn = true
isThereAWinner = false
gameboard = [
[1,2,3,4,5,6,7],
[8,9,10,11,12,13,14],
[15,16,17,18,19,20,21],
[22,23,24,25,26,27,28],
[29,30,31,32,33,34,35],
[36,37,38,39,40,41,42]
];
}
function swapTurns() {
selectorTable.innerHTML = ""
drawSelector()
playerOneTurn = !playerOneTurn
document.getElementsByName("announcements")[0].innerHTML = "Current Player: " + whosPlaying() + " "
};
// GAMEPLAY
function playerSelects2P() {
findAvailableSpots()
// put an eventListener here?
columnPick = prompt(whosPlaying() + ', choose which column 1-7')
if (availableSpots.includes(parseInt(columnPick)))
{console.log(columnPick)}
else {
alert("not available")
playerSelects2P()}
};
function playerSelects1P() {
if (whosPlaying() == playerTwo) {
findAvailableSpots()
columnPick = availableSpots[Math.floor(Math.random() * availableSpots.length)]
return
}
else {playerSelects2P()}
};
function whosPlaying() {
if (playerOneTurn) {
return "Yellow"
} else {
return "Red"
}
};
// starts from the bottom row and claims spot when there it is a number (unoccupied)
function claimSpot(){
findAvailableSpots()
if (availableSpots.includes(indexPick+1)) {
let i;
for (i = 5; i > -1; i--)
{if (Number.isInteger(gameboard[i][indexPick])) {
gameboard[i].splice((indexPick), 1, whosPlaying())
mainTable.innerHTML = ""
drawBoard()
checkForWinners()
// do I need to put some sort of delay here for it not to go to swap turns right away?
swapTurns()
return
}
}
}
else {
console.log(availableSpots)
alert("Forbidden")
}
};
// if there is a string in row[0], that column is no longer available.
// the cells are numbered from 1 to 7, not per index so you need to add one to indexPick to identify
function findAvailableSpots() {
availableSpots = gameboard[0].filter(x => Number.isInteger(x) == true)
};
function checkForWinners() {
horizontalCheck()
verticalCheck()
downrightCheck()
uprightCheck()
}
// WIN CHECKERS
// a forloop evaluates a section of the matrix, moving through it and seeing if the 3 ahead match.
// it stops before going out of bounds
function findFour(w,x,y,z) {
// Checks first cell against current player and all cells match that player
return ((w == whosPlaying()) && (w === x) && (w === y) && (w === z));
};
function winDeclared() {
isThereAWinner = true
alert("winner")
document.getElementsByName("announcements")[0].innerHTML = whosPlaying() + " wins! "
// this does not show, it snaps to swap places
};
function uprightCheck() {
for (r=5; r>2; r--) {
for (c=0; c<4; c++){
if (findFour(gameboard[r][c], gameboard[r-1][c+1], gameboard[r-2][c+2], gameboard[r-3][c+3])) {
winDeclared()
return
}
}
}
};
function downrightCheck() {
for (r=0; r<3; r++) {
for (c=0; c<4; c++){
if (findFour(gameboard[r][c], gameboard[r+1][c+1], gameboard[r+2][c+2], gameboard[r+3][c+3])) {
winDeclared()
return
}
}
}
};
function verticalCheck() {
for (r=5; r>2; r--) {
for (c=0; c<7; c++){
if (findFour(gameboard[r][c], gameboard[r-1][c], gameboard[r-2][c], gameboard[r-3][c])) {
winDeclared()
return
}
}
}
};
function horizontalCheck() {
for (r=0; r<6; r++) {
for (c=0; c<4; c++){
if (findFour(gameboard[r][c], gameboard[r][c+1], gameboard[r][c+2], gameboard[r][c+3])) {
winDeclared()
return
}
}
}
};
When you manipulate the DOM, the operation itself is syncrhonous but the browser decides when the user will actually see the changes. Sometimes, the broswer will not have time to redraw before the prompt appears. To get around this, you can wrap the alert in a setTimeout() to delay the alert.
setTimeout(
function() {
alert("winner")
}, 10)

Click event listener is being repeatedly triggered without any clicks?

I am developing a "Battleship" game with two grids made up of divs and am currently attempting to add a click event listener to all of the divs.
The issue that I am having is that the event listener is being repeatedly triggered (until every single div has been clicked) when I refresh my page and I can't understand why...
Here's the event listener in question:
let aiGridCells = document.querySelectorAll(".ai-grid__game-cell");
aiGridCells.forEach(cell => {
cell.addEventListener("click", humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});
Where humanPlayer is an object that has been generated by a factory function:
const humanPlayer = playerFactory('human');
import gameboardFactory from './gameboardFactory';
const playerFactory = (name) => {
const playerBoard = gameboardFactory();
const humanAttack = (cell, player) => { // Where player is opponent
if (player.playerBoard.gameBoard[cell].id !== 'miss') {
player.playerBoard.receiveAttack(cell);
};
};
const aiAttack = (player) => { // Where player is opponent
const availableCells = [];
for (let i = 0; i < player.playerBoard.gameBoard.length; i++) {
if (player.playerBoard.gameBoard[i].id === null) {
availableCells.push(i);
};
};
const attackCell = Math.floor(Math.random() * availableCells.length);
player.playerBoard.receiveAttack(attackCell);
};
return {
name,
playerBoard,
humanAttack,
aiAttack
}
};
export default playerFactory;
and gameboardFactory is:
import shipFactory from './shipFactory';
const gameboardFactory = () => {
const gameBoard = [];
const shipYard = [];
const init = () => {
for (let i = 0; i<100; i++) {
gameBoard.push({id: null})
};
};
const checkValidCoordinates = (direction, start, end) => {
if (direction === 'horizontal') {
if ((start <= 9) && (end <= 9)) {
return true;
} else {
let newStart = (start/10).toString(10);
let newEnd = (end/10).toString(10);
if ((newStart.charAt(0)) === (newEnd.charAt(0))) {
return true;
};
};
} else {
if ((start <= 9) && (end <= 9)) {
return false
} else if (start <= 9) {
let newStart = start.toString(10);
let newEnd = end.toString(10);
if ((newStart.charAt(0)) === (newEnd.charAt(1))) {
return true;
};
} else {
let newStart = start.toString(10);
let newEnd = end.toString(10);
if ((newStart.charAt(1)) === (newEnd.charAt(1))) {
return true;
};
};
};
return false
};
const checkIfShipPresent = (direction, start, end) => {
if (direction === 'horizontal') {
for (let i = start; i <= end; i++) {
if (gameBoard[i].id !== null) {
return true;
}
};
return false;
} else {
for (let i = start; i <= end; i += 10) {
if (gameBoard[i].id !== null) {
return true;
}
};
return false;
};
};
const placeShip = (id, direction, start, end) => {
if (!checkValidCoordinates(direction, start, end)) {
return;
};
if (checkIfShipPresent(direction, start, end)) {
return;
};
const newShip = [];
if (direction === 'horizontal') {
for (let i = start; i <= end; i++) {
gameBoard[i].id = id;
newShip.push(i);
};
} else {
for (let i = start; i <= end; i += 10) {
gameBoard[i].id = id;
newShip.push(i);
};
};
shipYard.push(shipFactory(id, newShip));
};
const receiveAttack = (cell) => {
console.log(cell)
if (gameBoard[cell].id !== null) {
const attackedShip = shipYard.filter((ship) => {
return ship.id === gameBoard[cell].id;
})[0];
if (!attackedShip.hits.includes(cell)) {
attackedShip.hits.push(cell);
};
} else {
gameBoard[cell].id = 'miss';
};
};
const checkIfAllShipsSunk = () => {
let allShipsSunk = true;
shipYard.forEach((ship) => {
if (ship.isSunk() === false) {
allShipsSunk = false;
};
});
return allShipsSunk;
};
if (gameBoard.length === 0) {
init();
};
return {
gameBoard,
placeShip,
receiveAttack,
shipYard,
checkIfAllShipsSunk
};
};
export default gameboardFactory;
I'm completely lost to what the issue could be and have tried countless things to rectify it. Any suggestions would be hugely appreciated.
Thank you!
You trying to add actual function call as listener here:
let aiGridCells = document.querySelectorAll(".ai-grid__game-cell");
aiGridCells.forEach(cell => {
cell.addEventListener("click", humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});
So on your event listener initialization you actually call your function instead of passing it as a listener.
You can pass it like this instead:
aiGridCells.forEach(cell => {
cell.addEventListener("click", () => humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});

Maintain counter of a global variable across function

I have a counter which is local to the scope of a parent function and being passed across multiple child functions and is incremented across multiple instances. I am having trouble maintaining the count
I have tried the following
var maxLimit = 150;
async function incrementCounter(counter) {
counter++;
console.log(counter);
return counter;
}
async function processRights() {
var counter = 0,
end = false;
var queryInput = [0, 1, 1, 0, 1];
for (var i = 0; i < queryInput.length; i++) {
var element = queryInput[i];
var thOutput = await processTitle(counter, element, 'th');
if (!thOutput) {
end = true;
return;
}
var nthOutput = await processTitle(counter, element, 'nth');
if (!nthOutput) {
end = true;
return;
};
}
if (!queryInput || !queryInput.length) {
end = true;
}
return;
}
async function processTitle(counter, element, type) {
var output = await callFunc(counter, element, type);
if (!output) {
return false;
}
return output;
}
async function callFunc(counter, element) {
var responses = [];
var counterValue1 = await incrementCounter(counter);
if (counterValue1 >= maxLimit) {
return false;
}
await callAnotherFunc();
if (1) {
var qryArr = [0, 1, 1, 0, 1];
for (let i = 0; i < qryArr.length; i++) {
var counterValue2 = await incrementCounter(counterValue1);
console.log("counterValue2 -- " + counterValue2);
if (counterValue2 >= maxLimit) {
return false;
}
await callAnotherFunc();
}
return responses;
}
}
async callAnotherFunc(){
return true;
}
processRights();
I would like the increment the counter and check against the maximum limit each time the callAnotherFunc function is called. I am a novice to JS. Pl help!
If the counter can be really a global variable, you can declare it side by side with the maxLimit variable, something like this:
var overallCounter = 0;
var maxLimit = 150;
...
async callAnotherFunc(){
return overallCounter++ <= maxLimit;
}
From the question and the code I think this is what you want to achieve, right?

How to find id in array object then edit and delete a object in oop javascript?

I coding demo student management . But have problem below, please help me resolve them.
The requirement
Manage student : add, edit, remove
Using console building data table. The table contain fiedls: id Name Age Point
Using console call function
List all student have point > = 5;
This is my code:
//
function Student(id, name, age, point) {
this.id = id;
this.name = name;
this.age = age;
this.point = point;
}
Student.prototype = {
setId: function (value) {
this.id = value;
},
getId: function () {
return this.id;
},
setName: function (value) {
this.name = value;
},
getName: function () {
return this.name;
},
setAge: function (value) {
this.age = value;
},
getAge: function () {
return this.age;
},
setPoint: function (value) {
this.point = value;
},
getPoint: function () {
return this.point;
},
};
function StudentController(student) {
this.listStudent = [];
this.id = 1;
}
StudentController.prototype = {
addNew: function (name, age, point) {
var student = new Student(this.id, name, age, point);
this.listStudent.push(student);
this.id += 1;
return student;
},
This is function find id . But it always return last id in array object.
findId: function (id) {
var student = null;
for (var i = 0; i < this.listStudent.length; i++) {
if (id = this.listStudent[i].getId()) {
student = this.listStudent[i];
}
}
return student;
},
This is function edit student. But it can't getId from function findId();
editStudent: function (student) {
var oldStudent = this.findId(student.getId());
console.log('oldStudent', oldStudent);
for (var x = 0; x < this.listStudent.length; x++) {
if (oldStudent = this.listStudent[x].getId()) {
this.listStudent[x] = student;
}
}
},
This function same too editStudent() function.
deleteStudent: function (student) {
var crrentStudent = this.findId(student.getId());
for (var y = 0; y < this.listStudent.length; y++) {
if (crrentStudent.getId() === this.listStudent[y]) {
this.listStudent.splice(y, 1);
}
}
},
This this function sort point of student > = 5. But look like not working :(
// find point student > = 5
findByPoint: function (point) {
var point = '';
for (var i = 0; i < this.listStudent.length; i++) {
if (this.listStudent[i].getPoint() >= point) {
return point;
}
}
},
showStudent: function () {
console.table(this.listStudent);
},
};
var studentController = new StudentController();
studentController.addNew("Hanh", 20, 8);
studentController.findId(1);
studentController.editStudent();
studentController.deleteStudent();
Please help me resolve and explanation. Thank you so much !!
The problem is pretty straight forward, for a comparison operator you need == in if condition, = is for assignment and adding
Change findId() function
findId: function (id) {
var student = null;
for (var i = 0; i < this.listStudent.length; i++) {
if (id == this.listStudent[i].getId()) { // change here
student = this.listStudent[i];
}
}
return student;
},
and in editStudent function
editStudent: function (student) {
var oldStudent = this.findId(student.getId());
console.log('oldStudent', oldStudent);
for (var x = 0; x < this.listStudent.length; x++) {
if (oldStudent == this.listStudent[x].getId()) { //change here
this.listStudent[x] = student;
}
}
},

How to stop executing Javascript function containing infinite loop after some time

Suppose I have following piece of code that contains an infinite loop:
function infiniteLoop() {
while(true) {
//do something, eg.
document.getElementById("someID").innerHTML = "Blah";
}
}
If we execute this code in an online compiler, browser will crash. I want to prevent that from happening. So I tried following code following this answer:
function willNotCrash() {
myVar = setInterval(infiniteLoop, 5000);
setTimeout(function(){
clearInterval(myVar);
}, 4000);
}
This code doesn't make the browser to crash, because I am stopping the execution before infiniteLoop() gets called by clearInterval(myVar).
My question is how do I stop executing such functions if they don't response within some period of time (eg. after 5 seconds or before the browser is crashed).
For example, if we copy paste following java code in https://www.compilejava.net/
public class HelloWorld {
public static void main(String[] args) {
while(true) {
System.out.println("Blah");
}
}
}
we get a nice output saying,
Script was taking longer than 5 seconds to execute so it was killed.
Here is my current code: http://js.do/code/106546
This is a bit tricky but perfectly doable. You need to tokenize the script and then rebuild it but insert a counter increment in every loop and function call. If the counter goes above some threshold, then bomb out. I did it here: https://littleminigames.com/
You can see the source at https://bitbucket.org/cskilbeck/littleminigames/src
The interesting bits are in wrapper.js (https://bitbucket.org/cskilbeck/littleminigames/src/ac29d0d0787abe93c75b88520050a6792c04d34d/public_html/static/js/wrapper.js?at=master&fileviewer=file-view-default)
Google escodegen, estraverse and esprima
I relied heavily on this: https://github.com/CodeCosmos/codecosmos/blob/master/www/js/sandbox.js
wrapper.js, as requested:
// Don't obfuscate this file! We depend on the toString() of functions!
// this was all nicked from https://github.com/CodeCosmos/codecosmos/blob/master/www/js/sandbox.js
(function(mainApp) {
'use strict';
var esprima = window.esprima,
estraverse = window.estraverse,
escodegen = window.escodegen,
errors = [],
eng,
Syntax = estraverse.Syntax;
// This implements the jankiest possible "source map", where we keep an array
// of [generatedLine, knownSourceLine]. Seems to essentially work.
function SourceNode(line, col, _sourceMap, generated) {
this.line = line;
this.col = col;
this.generated = generated;
}
SourceNode.prototype.toStringWithSourceMap = function toStringWithSourceMap() {
var code = [];
var mapLines = {};
var map = [];
// assumes that wrapCode adds two lines
var line = 3;
var lastMapLine = null;
function walk(node) {
if (typeof(node) === "string") {
if (node) {
code.push(node);
var matches = node.match(/\n/g);
if (matches !== null) {
line += matches.length;
}
}
} else if (node instanceof SourceNode) {
if (node.line !== null) {
if (!mapLines[line]) {
map.push([line, node.line]);
mapLines[line] = node.line;
}
}
walk(node.generated);
} else {
node.forEach(walk);
}
}
walk(this);
return {
code: code.join(''),
map: map
};
};
SourceNode.prototype.toString = function toString() {
return this.toStringWithSourceMap().code;
};
// This is used by escodegen
window.sourceMap = {
SourceNode: SourceNode
};
// TODO (chs): add in all the things that need to be masked
function runWrapper($userCode, __sys) {
var clear = __sys.clear,
setpixel = __sys.setpixel,
rectangle = __sys.rectangle,
box = __sys.box,
line = __sys.line,
getpixel = __sys.getpixel,
getpixeli = __sys.getpixeli,
keypress = __sys.keypress,
keyrelease = __sys.keyrelease,
keyheld = __sys.keyheld,
reset = __sys.reset;
__sys.userFunction = __sys.catchErrors($userCode);
}
function extractCode(fn) {
var code = fn.toString();
return code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));
}
function makeOneLine(code) {
return code.replace(/(\/\/[^\n]+|\n\s|\r\n\s*)/g, '');
}
var runTemplate = makeOneLine(extractCode(runWrapper));
function wrapCode(code, template, functionName, postCode) {
// avoid interpretation of the replacement string by using a fun.
// otherwise mo' $ mo problems.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter
return ("'use strict';" + template.replace(/\$userCode/, function() {
return 'function ' + functionName + '() {\n' + code + postCode + '\n}';
}));
}
var injectStatement = esprima.parse("if (++__sys.ctr >= __sys.maxctr) throw new Error('Script halted - infinite loop?');").body[0];
var injectElseStatement = esprima.parse("if (++__sys.ctr >= __sys.maxctr) throw new Error('Script halted - infinite loop?'); else ;").body[0];
function CallExpression(callee, args) {
this.callee = callee;
this.arguments = args;
}
CallExpression.prototype.type = Syntax.CallExpression;
function Identifier(name) {
this.name = name;
}
Identifier.prototype.type = Syntax.Identifier;
function BlockStatement(body) {
this.body = body;
}
BlockStatement.prototype.type = Syntax.BlockStatement;
function ReturnStatement(argument) {
this.argument = argument;
}
ReturnStatement.prototype.type = Syntax.ReturnStatement;
function FunctionExpression(id, params, body) {
this.id = id;
this.params = params;
this.body = body;
this.defaults = [];
this.expression = false;
this.generator = false;
this.rest = null;
}
FunctionExpression.prototype.type = Syntax.FunctionExpression;
function wrapId(node, defaultName) {
if (node.loc) {
var id = (node.id || {
name: null,
loc: null
});
var loc = id.loc || node.loc;
var name = id.name || defaultName;
return new Identifier(name + '$' + loc.start.line);
} else {
return node.id;
}
}
function instrumentAST(ast) {
var identifierStack = [];
function pushIdentifier(s) {
identifierStack[identifierStack.length - 1].push(s);
}
function popIdentifierStack() {
identifierStack.pop();
}
function pushIdentifierStack() {
identifierStack.push([]);
}
function peekLastIdentifier() {
var lastStackIdx = identifierStack.length - 1;
if (lastStackIdx >= 0) {
var stack = identifierStack[lastStackIdx];
if (stack.length) {
return stack[stack.length - 1];
}
}
return '';
}
pushIdentifierStack();
return estraverse.replace(ast, {
enter: function enterAST(node) {
switch (node.type) {
case Syntax.VariableDeclarator:
if (node.id.type === Syntax.Identifier) {
pushIdentifier(node.id.name);
}
break;
case Syntax.MemberExpression:
if (node.object.type === Syntax.Identifier) {
var id = node.object.name;
if (node.property.type === Syntax.Identifier) {
id += '__dot__' + node.property.name; // huh? why mangle these?
// console.log(id);
}
pushIdentifier(id);
} else if (node.property.type === Syntax.Identifier) {
pushIdentifier(node.property.name);
}
break;
case Syntax.FunctionDeclaration:
pushIdentifierStack();
break;
case Syntax.FunctionExpression:
pushIdentifierStack();
break;
default:
break;
}
return node;
},
leave: function leaveAST(node) {
switch (node.type) {
case Syntax.DoWhileStatement:
break;
case Syntax.ForStatement:
break;
case Syntax.FunctionDeclaration:
break;
case Syntax.FunctionExpression:
break;
case Syntax.WhileStatement:
break;
default:
return estraverse.SKIP;
}
// modify the BlockStatement in-place to inject the instruction counter
if(node.body.body === undefined) {
// they have used a non-block statement as the body of a function or loop construct
// not allowed for function declarations - should never get here
if(node.type === Syntax.FunctionDeclaration) {
errors.push({
message: "Missing {",
line: node.loc.start.line,
column: node.loc.start.column
});
}
else {
// otherwise insert the test
var newBody = angular.copy(injectElseStatement);
newBody.alternate = node.body;
node.body = newBody;
}
return estraverse.SKIP;
}
node.body.body.unshift(injectStatement);
if (node.type === Syntax.FunctionExpression) {
popIdentifierStack();
// __catchErrors(node)
node.id = wrapId(node, peekLastIdentifier());
return new CallExpression(
new Identifier("__sys.catchErrors"), [node]);
}
if (node.type === Syntax.FunctionDeclaration) {
popIdentifierStack();
// modify the BlockStatement in-place to be
// return __catchErrors(function id() { body });
var funBody = node.body;
node.body = new BlockStatement([
new ReturnStatement(
new CallExpression(
new CallExpression(
new Identifier("__sys.catchErrors"), [new FunctionExpression(
wrapId(node, peekLastIdentifier()), [],
funBody)]), []))
]);
}
return node;
}
});
}
// mainApp.sandbox('var a = 1; function update(frame) { clear(0); }').code
// give it the source code as a string
mainApp.sandbox = function(code) {
var rc = {};
this.errors = [];
try {
this.ast = instrumentAST(esprima.parse(code, { range: true, loc: true }));
this.map = escodegen.generate(this.ast, { sourceMap: true, sourceMapWithCode: true });
this.code = wrapCode(this.map.code, runTemplate, '', ';\n__sys.updateFunction = (typeof update === "function") ? update : null;');
}
catch(e) {
this.errors.push({
message: e.description,
line: e.lineNumber,
column: e.column
});
}
if(this.code) {
this.code = "eng.clientFunction = function(__sys) {" + this.code + "};";
}
};
mainApp.sandbox.prototype.searchMap = function(needle) {
// binary search
var lo = 0;
var hi = this.map.map.length;
var mid, here;
while (true) {
mid = lo + ((hi - lo) >> 1);
here = this.map.map[mid];
if (mid === lo || here[0] === needle) {
return here[1];
} else if (here[0] > needle) {
hi = mid;
} else {
lo = mid;
}
}
};
})(mainApp);
Typically all JavaScript runs in one thread, so it is impossible to run any JavaScript that could stop your loop while your loop is running. Using HTML5 web workers, you can run the infinite loop in a separate thread, and then you can terminate it:
var myWorker = new Worker( '/infinite.js ');
setTimeout( function ( ) {
myWorker.terminate( );
}, 5000 );
However your web worker won't have access to the DOM, so the contents of your infinite loop would need to be different that what you have in your question.
I found exactly what I was looking for in Bergi's comment,
Alternatively, place a if (Date.now() > dateAtStartOfExecution+5000) return; in every loop body.
So now my code looks like:
function infiniteLoop() {
dateAtStartOfExecution = Date.now();
while(true) {
//do something
document.getElementById("someID").innerHTML = "Blah";
if (Date.now() > dateAtStartOfExecution+5000) {
alert("Taking too much time. Killing.");
return;
}
}
}
If I run this code after 5 seconds I will get an alert and the execution will stop. Try this:
http://js.do/code/106565

Categories

Resources