i make quiz app
It displays the winner now if the number of questions expires and counts the points, but I want itin the event of the points being equal to display a message
self.showWinner = function () {
// who has the most points?
let team_data = {};
for (let team_name of self.teams)
team_data[team_name] = 0;
for (let action of self.actions) {
if (self.rounds[self.current.round].indexOf(action.category) === -1)
continue;
if (action.correct)
team_data[action.team] += action.difficulty;
else
team_data[action.team] -= action.difficulty;
}
let name;
let points = -99999;
for (let team_name in team_data) {
if (team_data[team_name] > points) {
points = team_data[team_name];
name = team_name;
}
}
// show screen
$("#overlay").show();
$("#overlay > *").hide();
$("#overlay #winner").show();
$("#winner .text").text(name + " (" + points + " pt)");
// reset game
self.teams = [];
};
if (self.current.round !== undefined && answered_questions === 4) {
setTimeout(self.showWinner, 10);
return;
}
Related
First of all, I hope you can understand my English. I'm not really good at English. I'd like to ask for help on giving 1 point to every unique user(a user that joined, a live stream to be exact) for every correct answer.
The issue I have in the current coding is that instead of giving points to each user respectively, it counts all users as one. What I mean by that is, for example: When user A wins, he gets(displays) 1 point. When user B wins, he gets(displays) 2 points. The points keep adding for every other user.
I'd like it to be like this: When user A wins, he gets(displays) 1 point. When user B wins he also gets(displays) 1 point too. That means each user has their own winning record.
I hope you can understand what I'm trying to explain.
Please ask if you need further explanation on things you don't understand.
I'd like to display the win count under/currently under the 'function addPhoto'.
Following is the current code:
Thanks in advance!
// DATA
let connection = new TikTokIOConnection(undefined);
let gameWords = [];
let gamegameSelectedWord = null;
let gameTimer = null;
let gameStatus = false;
let wins = 0;
// Config
let confComment = false;
let confLike = false;
let confShare = false;
let confJoin = false;
// START
$(document).ready(() => {
// Resize
function resizeContainer() {
let height = window.innerHeight;
let width = Math.round((9 / 16) * height);
$("#gameSize").html(width + 'x' + height);
$(".container").outerWidth(width);
$(".background").outerWidth(width);
$(".printer").outerWidth(width);
$(".animation").outerWidth(width);
// Paper
if (window.innerWidth >= 1366) {
var paperHeight = $("#paperContainer").outerHeight() - 20;
} else {
var paperHeight = $("#paperContainer").outerHeight() + 7;
}
$("#paper").outerHeight(paperHeight);
}
resizeContainer();
$(window).resize(function() {
resizeContainer();
});
// Connect
$("#targetConnect").click(function(e) {
// Check
if (gameStatus) {
let targetLive = $("#targetUsername").val();
connect(targetLive);
} else {
alert("Start game first!");
}
});
// Test
$("#btnPrepare").click(function(e) {
// Check sound
playSound(1);
playSound(2);
playSound(3);
playSound(4);
speakTTS(MSG_TEST);
// Populate dummy
for (let i = 0; i < 30; i++) {
addContent("<div style='text-align:center;'>Welcome ๐ฅณ๐ฅณ๐ฅณ</div>");
}
// Load game
loadGame();
// Setting
loadSetting();
// Set
gameStatus = true;
});
// Save config
$("#btnSave").click(function(e) {
loadSetting();
});
})
/*GAME PLAY
*/
function speakTTS(msg) {
speak(msg, {
amplitude: 100,
pitch: 70,
speed: 150,
wordgap: 5
});
}
/*function scramble( s ) {
return s.replace(
/\b([a-z])([a-z]+)([a-z])\b/gi,
function( t, a, b, c ) {
b = b.split( /\B/ );
for( var i = b.length, j, k; i; j = parseInt( Math.random() * i ),
k = b[--i], b[i] = b[j], b[j] = k ) {}
return a + b.join( '' ) + c;
}
);
}
document.forms.f.onsubmit = function() {
this.elements.t.value = scramble( this.elements.t.value );
return false;
};
document.forms.f.elements.t.value =
scramble( gettext( document.getElementsByTagName( 'p' )[0] ) );
*/
function censor(word) {
let censored = [];
let length = word.length;
let target = Math.ceil(length / 2);
let range_start = 2;
let range_end = target;
for (let i = 0; i < length; i++) {
let c = word.charAt(i);
if (i >= range_start && i <= range_end) {
if (c === " ") {
censored.push(" ");
} else {
censored.push("*");
}
} else {
censored.push(c);
}
}
return censored.join("");
}
function copyArray(a) {
let b = [];
for (i = 0; i < a.length; i++) {
b[i] = a[i];
}
return b;
}
function shuffle(a) {
let j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
return copyArray(a);
}
function countDown() {
// Counter
let timeleft = 60 * 4; // 4 Mins
// Clear
if (gameTimer != null) {
clearInterval(gameTimer);
}
// Start
gameTimer = setInterval(function() {
// Reset
if (timeleft <= 0){
clearInterval(gameTimer);
loadGame();
}
// Set
$("#gameTimeout").html(timeleft.toLocaleString() + "s");
timeleft -= 1;
}, 1000);
}
function loadGame() {
// Check
if (gameWords.length < 1) {
gameWords = shuffle(WORDS);
}
// Load
gameSelectedWord = gameWords.pop();
// Set remain words
$("#gameWords").html(gameWords.length);
// Check
if (typeof gameSelectedWord === 'string') {
// Normalize
splittedWord = gameSelectedWord.split("|");
gameSelectedWord = splittedWord[1];
// Set
$("#textGuess").html("<div style='font-size:70%;padding-bottom:5px;'>" + splittedWord[0] + "</div>" + censor(gameSelectedWord));
//$("#textGuess").html("<div style='font-size:70%;padding-bottom:5px;'>" + splittedWord[0] + "</div>" + scramble(gameSelectedWord));
// Timeout
countDown()
} else {
loadGame();
}
}
function checkWinner(data, msg) {
// Check type
if (typeof gameSelectedWord === 'string' && typeof msg === 'string') {
// Check answer
if (gameSelectedWord.trim().toLowerCase() == msg.trim().toLowerCase()) {
// Print Photo
addPhoto(data, "winner");
// Sound
playSound(4);
// Play TTS
let tssMsg = MSG_WINNER.replace("|username|", data.uniqueId);
speakTTS(tssMsg);
// Reload game
loadGame();
}
}
}
function loadSetting() {
// Load
confComment = $("#confComment").prop('checked');
confLike = $("#confLike").prop('checked');
confShare = $("#confShare").prop('checked');
confJoin = $("#confJoin").prop('checked');
}
/*LIVE TIKTOK
*/
function connect(targetLive) {
if (targetLive !== '') {
$('#stateText').text('Connecting...');
$("#usernameTarget").html("#"+targetLive);
connection.connect(targetLive, {
enableExtendedGiftInfo: true
}).then(state => {
$('#stateText').text(`Connected ${state.roomId}`);
}).catch(errorMessage => {
$('#stateText').text(errorMessage);
})
} else {
alert('Enter username first!');
}
}
function sanitize(text) {
return text.replace(/</g, '<')
}
function isPendingStreak(data) {
return data.giftType === 1 && !data.repeatEnd;
}
function playSound(mode) {
document.getElementById("sfx"+mode).play();
}
function addContent(payload) {
// Container
let content = $('#paper');
content.append("<div class='item'>" + payload + "</div>");
// Scroll top bottom
content.animate({ scrollTop: content.get(0).scrollHeight}, 333);
}
function addMessage(data, msg) {
// DATA
let userName = data.uniqueId;
let message = sanitize(msg);
// Check for voice
let command = message.split(" ")[0];
if (command == ":=say" || command == ":=cakap") {
// TTS
let cleanText = message.replace("=:say", "").replace("=:cakap", "");
speakTTS(cleanText);
} else {
// Check setting
if (confComment) {
// Add
addContent("<span style='font-weight: bold;'>" + userName + "</span>: " + message);
// Sound
playSound(1);
}
}
}
function addPhoto(data, mode) {
// DATA
let userName = data.uniqueId;
let userAvatar = data.profilePictureUrl;
let word = ['Nice going','Thatโs better than ever','Thatโs first class work','Iโm impressed','Nothing can stop you now','Well done','Good job','You did it','Thatโs the way','You rock','I knew you could do it','Keep up the good work','Thatโs clever','Way to go','Outstanding','Tremendous','Fantastic','You are amazing','No one can beat you','You are the chosen one'];
let words = word[Math.floor(Math.random()*word.length)];
// Add
if (mode == "winner")
{
wins++;
addContent(
`<div style="text-align:center;font-size: 1.25rem;">
<div style='padding-bottom:.25rem;color:#1881FF;'>๐๐ป๐๐ป `+words+`</div>
<div style='padding-bottom:.5rem;font-weight: bold;color:#20B601;'>`+userName+` โ</div>
<div>
<img src="`+userAvatar+`" style="width:135px;height:135px;border-radius: 15px;"/>
Wins: `+wins+`
</div>
</div>`
);
} else {
addContent(
`<div style="text-align:center;font-size: 1.25rem;">
<div style='padding-bottom:.25rem;'>๐๐๐Thanks๐๐๐</div>
<div style='padding-bottom:.5rem;font-weight: bold;color:#EA0C0C;'>`+userName+`</div>
<div>
<img src="`+userAvatar+`" style="width:135px;height:135px;border-radius: 15px;"/>
</div>
</div>`
);
}
// Sound
playSound(3);
}
function addGift(data) {
// DATA
let userName = data.uniqueId;
let giftPictureUrl = data.giftPictureUrl;
let giftName = data.giftName;
let giftRepeat = data.repeatCount;
let giftTotal = (data.diamondCount * data.repeatCount);
let word = ['Appreciate it','Thanks','Thank you very much','It means a lot','Youโre the best','Your gift helps me',];
let words = word[Math.floor(Math.random()*word.length)];
// Check
if (giftTotal >= 10) {
// Print Photo
addPhoto(data);
} else {
// Add
addContent(
`<div style="text-align:center;font-size: 1.25rem;"><div style='padding-bottom:.5rem;'>`+words+` <span style='font-weight: bold;color:#EA0C0C;'>`+userName+`!</span></div>
<div style='font-weight: bold;padding-bottom:.5rem;'><img src="`+giftPictureUrl+`" style="width:35px;height:35px;"/> Sent `+giftName+`</div>
x`+giftRepeat.toLocaleString()+` worth `+giftTotal.toLocaleString()+` coins!</div>`
);
// Sound
playSound(2);
// Play TTS
let tssMsg = MSG_GIFT.replace("|username|", userName);
speakTTS(tssMsg);
}
}
// New chat comment received
connection.on('chat', (data) => {
addMessage(data, data.comment);
checkWinner(data, data.comment);
})
// New gift received
connection.on('gift', (data) => {
if (!isPendingStreak(data) && data.diamondCount > 0) {
addGift(data);
}
})
// Like
connection.on('like', (data) => {
if (typeof data.totalLikeCount === 'number') {
// Check setting
if (confLike) {
// Print like
addMessage(data, data.label.replace('{0:user}', '').replace('likes', `${data.likeCount} likes`));
}
}
})
// Share, Follow
connection.on('social', (data) => {
// Check setting
if (confShare) {
// Print share
addMessage(data, data.label.replace('{0:user}', ''));
}
})
// Member join
let joinMsgDelay = 0;
connection.on('member', (data) => {
let addDelay = 250;
if (joinMsgDelay > 500) addDelay = 100;
if (joinMsgDelay > 1000) addDelay = 0;
joinMsgDelay += addDelay;
setTimeout(() => {
joinMsgDelay -= addDelay;
// Check setting
if (confJoin) {
// Print join
addMessage(data, "has entered");
}
}, joinMsgDelay);
})
// End
connection.on('streamEnd', () => {
$('#stateText').text('Stream ended.');
})
You should have an dictionary (object) of wins with keys the users, and values are the wins for each. Thus you save for each user its own wins.
So declare global:
let wins = {
// name: wins, name2: wins2
}
Then whenever need to increase wins, do so for the wins[playerName] value.
if (mode == "winner") {
wins[userName] = wins[userName] || 0
wins[userName]++
addContent(
`<div style="text-align:center;font-size: 1.25rem;">
<div style='padding-bottom:.25rem;color:#1881FF;'>๐๐ป๐๐ป `+ words + `</div>
<div style='padding-bottom:.5rem;font-weight: bold;color:#20B601;'>`+ userName + ` โ</div>
<div>
<img src="`+ userAvatar + `" style="width:135px;height:135px;border-radius: 15px;"/>
Wins: `+ wins[userName] + `
</div>
</div>`
);
}
The problem is in this function:
function minimax(newBoard,player, depth = 0) {
const availableSpots = emptySquares();
// console.log(availableSpots)
// console.log((checkGameStatus(newBoard,player) ? 'true' : 'false'))
let score = {};
if (checkGameStatus(newBoard, computerSymbol)) {
console.log("Are we hitting here? 1")
// console.log((onCheckWin(newBoard) === false))
// console.log(newBoard,"o")
score = { "score": (-100 + depth)}
return score
} else if (checkGameStatus(newBoard, playerSymbol)) {
console.log("Are we hitting here? 2")
// console.log(checkGameStatus(newBoard))
// console.log(newBoard,"o")
score = { "score": (100 - depth)}
return score
} else if (availableSpots.length === 0) {
console.log("Are we hitting here? 3")
score = { "score": 0 }
return score
}
let moves = [];
for (let i=0; i<availableSpots.length; i++) {
let move = {};
move.index = newBoard[availableSpots[i]];
newBoard[availableSpots[i]] = player;
console.table(newBoard)
if (player === computerSymbol) {
let result = minimax(newBoard, playerSymbol , depth++);
console.log(newBoard,"first");
console.log(result)
move.score = result["score"];
} else {
let result = minimax(newBoard, computerSymbol, (depth + 1));
console.log(newBoard,"first");
move.score = result.score;
}
console.log(moves,move)
newBoard[availableSpots[i]] = move.index;
moves.push(move);
console.log(moves,move)
} // end of for look
let bestMove;
if (player === computerSymbol) {
let bestScore = -10000;
for (let i=0; i<moves.length; i++) {
if (moves[i].score > bestScore) {
bestScore = moves[i].score;
bestMove = i;
// console.log(bestMove)
}
// end of for loop
}
if (player === playerSymbol) {
let bestScore = 10000;
for (let i=0; i<moves.length; i++) {
if (moves[i].score < bestScore) {
bestScore = moves[i].score;
bestMove = i;
// console.log(bestMove)
}
}
}
}
return moves[bestMove];
}
When I console.log the function inside of it, it returns an object, but when I set that function call to "result" like here:
if (player === computerSymbol) {
let result = minimax(newBoard, playerSymbol , depth++);
console.log(newBoard,"first");
console.log(result)
move.score = result["score"];
I get undefined as the result and that I can't access property of undefined. Also, I seem to have a problem with my minimax algorithm where it's just constantly choosing the next open square. This might be because I am only using an empty board at the moment. I can add more code if need be, but it seems to me that the problem exists inside the function and not from one of my external or helper functions
I am trying to make a game when you have to guess a number that is generated by the Math.random() function in JavaScript. But I realized that when I do that I have to rerun the function if they get the number wrong. Then the number regenerates when it reruns the function. Is there a way that I can make the variable stay until I want to change it. I was going to change it using the const function but I realized it would do the same thing. Here is my full code:
var tries = 5;
var howMany = 0;
var wrong = 0;
var player1 = 0;
var player2 = 0;
var triesmulti = 10;
var turn = 'player 1';
var number;
function start() {
var min = document.getElementById('min').value;
var max = document.getElementById('max').value;
number = Math.floor(Math.random() * (+max - +min)) + +min;
if (tries < 1) {
alert('You \don\'t have any more tries left. The number was \n' + number);
tries = 5;
wrong += 1;
document.getElementById('wrong').innerHTML = 'You have got the number wrong ' + wrong + ' times';
} else {
var guess = prompt();
if (guess == number) {
alert('You got the number right!\n' + number);
howMany += 1;
tries = 5;
document.getElementById('howMany').innerHTML = 'You have guessed the number ' + howMany + ' times';
document.getElementById('tries').innerHTML = 'You have 5 tries left';
} else {
alert('You got the number wrong.');
tries -= 1;
document.getElementById('tries').innerHTML = 'You have ' + tries + ' tries left';
setTimeout(start, 1000);
}
}
}
function multiplayer() {
var min = document.getElementById('minm').value;
var max = document.getElementById('maxm').value;
number = Math.floor(Math.random() * (+max - +min)) + +min;
if (triesmulti < 1) {
alert('You \don\'t have any more tries left\n' + number);
triesmulti = 10;
document.getElementById('triesmulti').innerHTML = 'You have 5 tries for each player';
} else {
var guess = prompt(turn);
if (turn == 'player 1') {
if (guess == number) {
alert('You got the number right!\n' + number);
player1 += 1;
triesmulti = 10;
document.getElementById('triesmulti').innerHTML = 'You have 5 tries for each player';
} else {
alert('You got the number wrong!');
turn = 'player 2';
setTimeout(multiplayer, 1000);
}
} else if (turn == 'player 2') {
if (guess == number) {
alert('You got the number right!\n' + number);
player2 += 1;
triesmulti = 10;
document.getElementById('triesmulti').innerHTML = 'You have 5 tries for each player';
} else {
alert('You got the number wrong!');
turn = 'player1';
setTimeout(multiplayer, 1000);
}
}
}
}
If you see there, in the setTimeout() it reruns the function.
You can create a stateful random number generator quite easily with an object or closure:
const rndRng = (lo, hi) => ~~(Math.random() * (hi - lo) + lo);
const intRng = (lo, hi) => {
let n = rndRng(lo, hi);
return {
next: () => (n = rndRng(lo, hi)),
get: () => n
};
};
const rng = intRng(10, 20);
console.log(rng.get());
console.log(rng.get());
rng.next();
console.log(rng.get());
console.log(rng.get());
But having to do this shouldn't really be necessary for your application. Currently, the application uses non-idempotent functions that rely on global state, repeated/duplicate logic and deeply nested conditionals, so it's become too encumbered to easily work with.
I'd start by storing state in an object. A game like this can be modeled well by a finite state machine.
The below code is a naive implementation of this with plenty of room for improvement, but hopefully demonstrates the idea. It works for any number of players and it's fairly easy to add features to.
However, string messages are baked into business logic so the class is overburdened. A good next step would be creating a separate view class to abstract business logic from display. However, although the message strings are baked into the game logic, the DOM is decoupled. This makes it fairly easy for the caller to use the class in other UIs such as substituting the DOM for alert/prompt.
The below solution is far from the only way to approach this design problem.
class GuessingGame {
constructor(players=1, guesses=5, lo=0, hi=10) {
this.players = Array(players).fill().map(() => ({
guesses: guesses, score: 0
}));
this.guesses = guesses;
this.lowerBound = lo;
this.upperBound = hi;
this.state = this.initialize;
}
initialize() {
const {lowerBound: lo, upperBound: hi} = this;
this.players = this.players.map(({score}) => ({
guesses: this.guesses,
score: score
}));
this.target = ~~(Math.random() * (hi - lo) + lo);
this.currentPlayer = ~~(Math.random() * this.players.length);
this.state = this.guess;
this.message = `guess a number between ${lo} and ${hi - 1} ` +
`(inclusive), player ${this.currentPlayer}:`;
}
handleCorrectGuess() {
this.state = this.initialize;
this.players[this.currentPlayer].score++;
this.message = `player ${this.currentPlayer} guessed ` +
`${this.target} correctly! press 'enter' to continue.`;
}
handleNoGuessesLeft(guess) {
this.state = this.initialize;
this.players[this.currentPlayer].score--;
this.flash = `${guess} was not the number, player ` +
`${this.currentPlayer}.`;
this.message = `player ${this.currentPlayer} ran out of ` +
`guesses. the secret number was ${this.target}. press ` +
`'enter' to continue.`;
}
handleIncorrectGuess(guess) {
this.flash = `${guess} was not the number, player ` +
`${this.currentPlayer}.`;
this.currentPlayer = (this.currentPlayer + 1) % this.players.length;
const {lowerBound: lo, upperBound: hi} = this;
this.message = `guess a number between ${lo} and ${hi - 1} ` +
`(inclusive), player ${this.currentPlayer}:`;
}
guess(guess) {
if (String(+guess) !== String(guess)) {
this.flash = `sorry, ${guess || "that"} ` +
`isn't a valid number. try something else.`;
return;
}
if (this.target === +guess) {
this.handleCorrectGuess();
}
else if (!--this.players[this.currentPlayer].guesses) {
this.handleNoGuessesLeft(+guess);
}
else {
this.handleIncorrectGuess(+guess);
}
}
nextState(...args) {
this.flash = "";
return this.state(...args);
}
scoreBoard() {
return game.players.map((e, i) =>
`player ${i}: {score: ${e.score}, guesses remaining: ` +
`${e.guesses}} ${game.currentPlayer === i ? "<--" : ""}`
).join("\n");
}
}
const msgElem = document.getElementById("message");
const responseElem = document.getElementById("response");
const scoresElem = document.getElementById("scoreboard");
const game = new GuessingGame(3);
game.nextState();
msgElem.innerText = game.message;
scoresElem.innerText = game.scoreBoard();
let timeout;
responseElem.addEventListener("keydown", e => {
if (timeout || e.code !== "Enter") {
return;
}
game.nextState(e.target.value);
e.target.value = "";
e.target.disabled = true;
msgElem.innerText = game.flash;
clearTimeout(timeout);
timeout = setTimeout(() => {
msgElem.innerText = game.message;
scoresElem.innerText = game.scoreBoard();
timeout = null;
e.target.disabled = false;
e.target.focus();
}, game.flash ? 1300 : 0);
});
* {
background: white;
font-family: monospace;
font-size: 1.03em;
}
input {
margin-bottom: 1em;
margin-top: 1em;
}
<div id="message"></div>
<input id="response">
<div id="scoreboard"></div>
Well, your code is not organised, have lot of duplicates, you could devide it into functions anyway, you can add a boolean variable to check against when you should change the number, I don't know about your HTML code or css but I just added those elements according to you selectors, you can change the multiplayer function too.
var tries = 5;
var howMany = 0;
var wrong = 0;
var player1 = 0;
var player2 = 0;
var triesmulti = 10;
var turn = 'player 1';
var number;
var isAlreadyPlaying = false;
function start() {
var min = document.getElementById('min').value;
var max = document.getElementById('max').value;
if(!isAlreadyPlaying) {
isAlreadyPlaying = true;
number = Math.floor(Math.random() * (+max - +min)) + +min;
}
if (tries < 1) {
alert('You \don\'t have any more tries left. The number was \n' + number);
tries = 5;
wrong += 1;
document.getElementById('wrong').innerHTML = 'You have got the number wrong ' + wrong + ' times';
isAlreadyPlaying = false;
} else {
var guess = prompt();
if (guess == number) {
alert('You got the number right!\n' + number);
howMany += 1;
tries = 5;
document.getElementById('howMany').innerHTML = 'You have guessed the number ' + howMany + ' times';
document.getElementById('tries').innerHTML = 'You have 5 tries left';
isAlreadyPlaying = false;
} else {
alert('You got the number wrong.');
tries -= 1;
document.getElementById('tries').innerHTML = 'You have ' + tries + ' tries left';
setTimeout(start, 1000);
}
}
}
Min <input type="number" id="min" value="1"><br>
Max<input type="number" id="max" value="10"><br>
<button onclick="start()">Play</button>
<p id="wrong"></p>
<p id="howMany"></p>
<p id="tries"></p>
I have a MultiSelectDropDown, that is, several RadComboBox controls are used in a combined way. For example, I can have a dropdown for regions, another for depots and another for user. The idea is to change the content of lower levels dynamically whenever items are selected or unselected on a higher level. The problem is that in the case when many items are selected, this becomes brutally slow due to some Telerik functions, but I do not understand why. This is a chunk from the client-side of the MultiSelectDropDown prototype:
changeLowerLevels: function (valueIndex, values, value) {
if (!this.canChange) return;
//Get selected values from combobox
var combo = $find(this.ddlIDs[valueIndex - 1]);
var cbItems = combo.get_checkedItems();
var selectedItems = [];
var change = null;
var counter = 0;
if (cbItems.length) this.filterString = "";
for (var i = 0; i < cbItems.length; i++) {
counter++;
if (this.filterString == "") this.filterString = cbItems[i].get_text();
selectedItems.push(cbItems[i].get_value());
}
if (counter > 1) this.filterString += " with " + (counter - 1) + " other" + ((counter > 2) ? "s" : "");
if (JSON.stringify(selectedItems) === JSON.stringify(this.selectedItems[valueIndex - 1]) || selectedItems == [])
return;
this.selectedItems[valueIndex - 1] = selectedItems;
var controlObject = this;
var combo = $find(this.ddlIDs[valueIndex]);
var comboItems = combo.get_items();
if(!this.disabled) combo.enable();
combo.clearItems();
if (valueIndex == 1) this.twoLevelCache = values;
var val = values;
//break if all items are found
var nrOfSelectedItems = this.selectedItems[valueIndex - 1].length;
var nrOfFoundItems = 0;
var index = 0;
var indexes = [];
var found = false;
while (nrOfFoundItems < nrOfSelectedItems && val[index] !== undefined) {
found = (this.selectedItems[valueIndex - 1].indexOf(val[index].Value) != -1);
if (!(found))
index++;
else {
indexes.push(index)
nrOfFoundItems++;
index++;
}
}
//separators from valuesIndex - 1 level
var controlObject = this;
for (var i = 0; i < indexes.length; i++) {
var separator = new Telerik.Web.UI.RadComboBoxItem();
separator.set_text("<span><a class=\"checkAll tt-multi-uncheck-icon\" index=\"" + index + "\">U</a>" + $find(this.ddlIDs[valueIndex - 1]).findItemByValue(val[indexes[i]].Value).get_text() + "</span>");
separator.set_value("");
separator.set_isSeparator(true);
comboItems.add(separator);
this.twoLevelCache.push(val[indexes[i]].Levels);
//valuesIndex level
var valuesArray = val;
var comboItem = new Telerik.Web.UI.RadComboBoxItem();
for (var depot in valuesArray[indexes[i]].Levels) {
comboItem = new Telerik.Web.UI.RadComboBoxItem();
comboItem.set_text(valuesArray[indexes[i]].Levels[depot].Name);
comboItem.set_value(valuesArray[indexes[i]].Levels[depot].Value);
comboItems.add(comboItem);
comboItem = null;
}
$('#' + this.ddlIDs[valueIndex] + '_DropDown a.checkAll').unbind().on("click", function () {
checkAllLowerItems(this, controlObject.ddlIDs[valueIndex]);
});
}
combo.set_emptyMessage(this.allText);
//$("#" + this.ddlIDs[valueIndex]).html(returnValue);
if (this.ddlIDs.length > valueIndex + 1) {
var paramToPass = (((val == undefined) || (val[index] === undefined)) ? ("") : (val[index]));
if (this.allText.length > 0)
this.changeLowerLevels(valueIndex + 1, paramToPass, "");
else {
if (paramToPass !== "")
paramToPass = paramToPass.Levels;
if ((val[index] == undefined) || (val[index].Levels[0] === undefined) || (val[index].Levels[0].Value === "")) {
this.changeLowerLevels(valueIndex + 1, paramToPass, "");
}
else {
this.changeLowerLevels(valueIndex + 1, paramToPass, val[index].Levels[0].Value);
}
}
}
else {
if (this.allText.length > 0)
this.selectedItems[valueIndex] = "";
else
if ((val[index] == undefined) || (val[index].Levels[0] === undefined) || (val[index].Levels[0].Value === "")) {
this.selectedItems[valueIndex] = "";
}
else {
this.selectedItems[valueIndex] = val[index].Levels[0].Value;
}
}
this.setText();
}
combo.clearItems() is extremeley slow. I have take a look on how it is implemented:
function (){var f=this._parent._getControl();?if(f._checkBoxes){f._checkedIndicesJson="[]";?f._checkedIndices=[];?var g=f.get_items();?for(var d=0,e=g.get_count();?d<e;?d++){var c=f.get_items().getItem(d);?c.set_checked(false);?}f.updateClientState();?}a.RadComboBoxItemCollection.callBaseMethod(this,"clear");?}
How can I make sure that this Javascript function speeds up?
I have finally solved the problem by rewriting Telerik client-side functionalities. It was a long and difficult debugging, but it yielded a large performance boost in the most difficult circumstances. From ~30 000 milliseconds, to ~300. Let's see the parts of the optimization:
The actual rewrite
/* Overriding Telerik functions Start */
var overridenTelerikControls = false;
function overrideTelerikFunctionalities() {
if (!overridenTelerikControls) {
overridenTelerikControls = true;
Telerik.Web.UI.RadComboBox.prototype.clearItems = function (isMultiSelectDropDown) {
this.get_items().clear(isMultiSelectDropDown);
this._itemData = null;
};
Telerik.Web.UI.RadComboBoxItemCollection.prototype.clear = function (isMultiSelectDropDown){
var f=this._parent._getControl();
if(f._checkBoxes){
f._checkedIndicesJson="[]";
f._checkedIndices=[];
var g = f.get_items();
for(var d=0,e=g.get_count();d<e;d++){
var c=f.get_items().getItem(d);
c.set_checked(false, isMultiSelectDropDown);
}
if (isMultiSelectDropDown) {
f._updateComboBoxText();
if (f._checkAllCheckBoxElement != null) {
f._updateCheckAllState();
}
}
f.updateClientState();
}
Telerik.Web.UI.RadComboBoxItemCollection.callBaseMethod(this, "clear");
};
Telerik.Web.UI.RadComboBoxItem.prototype.set_checked = function (d, isMultiSelectDropDown){
if(!this.get_enabled()){
return;
}
this._setChecked(d);
var c=this.get_comboBox();
if(c){
if(d){
c._registerCheckedIndex(this.get_index());
}else{
c._unregisterCheckedIndex(this.get_index());
}
if (!isMultiSelectDropDown) {
c._updateComboBoxText();
}
if((!isMultiSelectDropDown) && (c._checkAllCheckBoxElement!=null)){
c._updateCheckAllState();
}
}
};
}
}
/* Overriding Telerik functions End*/
My approach was to keep the old way of their working by default, but if an isMultiSelectDropDown parameter is passed, then work in the optimized manners. So we have a switch materialized as a parameter and we can turn it on/off. The main difference was that the old way was to change the label text showing the selected elements each time a checkbox is checked/unchecked. The main improvement was to do this change after all the checkboxes were checked/unchecked. This extremely simple idea was the driving force behind the boost of performance.
Actual usage
overrideTelerikFunctionalities();
combo.clearItems(true);
This was the functionalities were overriden if they were not already and the parameter was true, therefore the new approach was chosen.
Test, test, test
The following code is giving me an error in the chrome developer tools:
"uncaught syntax error- unexpected token"
Specifically, the Errors come up in the populate header function at
var notraw = JSON.parse(arrayraw)
and in the first if statement, at
parsed = JSON.parse(retrieved); //var test is now re-loaded!
These errors haven't come up in previous iterations. Does anyone know why?
// This statement should be ran on load, to populate the table
// if Statement to see whether to retrieve or create new
if (localStorage.getItem("Recipe") === null) { // if there was no array found
console.log("There was no array found. Setting new array...");
//Blank Unpopulated array
var blank = [
["Untitled Recipe", 0, 0]
];
// Insert Array into local storage
localStorage.setItem("Recipe", JSON.stringify(blank));
console.log(blank[0]);
} else {
console.log("The Item was retrieved from local storage.");
var retrieved = localStorage.getItem("Recipe"); // Retrieve the item from storage
// test is the storage entry name
parsed = JSON.parse(retrieved); //var test is now re-loaded!
// we had to parse it from json
console.log(parsed)
}
// delete from array and update
function deletefromarray(id) {
var arrayraw = localStorage.getItem("Recipe")
var notraw = JSON.parse(arrayraw)
console.log(notraw[id])
notraw.splice(id, 1);
localStorage.setItem("Recipe", JSON.stringify(notraw));
}
// This adds to local array, and then updates that array.
function addtoarray(ingredient, amount, unit) {
var arrayraw = localStorage.getItem("Recipe")
var notraw = JSON.parse(arrayraw)
notraw.push([ingredient, amount, unit]);
localStorage.setItem("Recipe", JSON.stringify(notraw));
var recipeid = notraw.length - 1
console.log("recipe id:" + recipeid)
}
//The calculation function, that gets the ingredients from the array, and calculates what ingredients are needed
function calculate(multiplier) {
alert("Calculate function was called")
var length = recipearray.length - 1;
console.log("There are " + length + " ingredients.");
for (i = 0; i < length; i++) {
console.log("raw = " + recipearray[i + 1][1] + recipearray[i + 1][2]);
console.log("multiplied = " + recipearray[i + 1][1] / recipearray[0][2] * multiplier + recipearray[i + 1][2]);
}
}
// The save function, This asks the user to input the name and how many people the recipe serves. This information is later passed onto the array.
function save() {
var verified = true;
while (verified) {
var name = prompt("What's the name of the recipe?")
var serves = prompt("How many people does it serve?")
if (serves === "" || name === "" || isNaN(serves) === true || serves === "null") {
alert("You have to enter a name, followed by the number of people it serves. Try again.")
verified = false;
} else {
alert("sucess!");
var element = document.getElementById("header");
element.innerHTML = name;
var header2 = document.getElementById("details");
header2.innerHTML = "Serves " + serves + " people"
calculate(serves)
var arrayraw = localStorage.getItem("Recipe")
var notraw = JSON.parse(arrayraw)
notraw.splice(0, 1, [name, serves, notraw.length])
localStorage.setItem("Recipe", JSON.stringify(notraw))
return;
}
}
}
// the recipe function processes the inputs for the different ingredients and amounts.
function recipe() {
// Declare all variables
var ingredient = document.getElementById("ingredient").value;
var amount = document.getElementById("number").value;
var unit = document.getElementById("unit").value;
var count = "Nothing";
console.log("Processing");
if (isNaN(amount)) {
alert("You have to enter a number in the amount field")
} else if (ingredient === "" || amount === "" || unit === "Select Unit") {
alert("You must fill in all fields.")
} else if (isNaN(ingredient) === false) {
alert("You must enter an ingredient NAME.")
} else {
console.log("hey!")
// console.log(recipearray[1][2] + recipearray[1][1])
var totalamount = amount + unit
edit(ingredient, amount, unit, false)
insRow(ingredient, totalamount) // post(0,*123456*,"Fish")
}
}
function deleteRow(specified) {
// Get the row that the delete button was clicked in, and delete it.
var inside = specified.parentNode.parentNode.rowIndex;
document.getElementById('table').deleteRow(inside);
var rowid = inside + 1 // account for the first one being 0
console.log("An ingredient was deleted by the user: " + rowid);
// Remove this from the array.
deletefromarray(-rowid);
}
function insRow(first, second) {
//var first = document.getElementById('string1').value;
//var second = document.getElementById('string2').value;
// This console.log("insRow: " + first)
// Thisconsole.log("insRow: " + second)
var x = document.getElementById('table').insertRow(0);
var y = x.insertCell(0);
var z = x.insertCell(1);
var a = x.insertCell(2);
y.innerHTML = first;
z.innerHTML = second;
a.innerHTML = '<input type="button" onclick="deleteRow(this)" value="Delete">';
}
function populateheader() {
// Populate the top fields with the name and how many it serves
var arrayraw = localStorage.getItem("Recipe")
var notraw = JSON.parse(arrayraw)
var element = document.getElementById("header");
element.innerHTML = notraw[0][0];
var header2 = document.getElementById("details");
// if statement ensures the header doesn't say '0' people, instead says no people
if (notraw[0][1] === 0) {
header2.innerHTML = "Serves no people"
} else {
header2.innerHTML = "Serves " + notraw[0][1] + " people"
}
console.log("Now populating Header, The Title was: " + notraw[0][0] + " And it served: " + notraw[0][1]);
}
function populatetable() {
console.log("Now populating the table")
// Here we're gonna populate the table with data that was in the loaded array.
var arrayraw = localStorage.getItem("Recipe")
var notraw = JSON.parse(arrayraw)
if (notraw.length === 0 || notraw.length === 1) {
console.log("Array was empty.")
} else {
var count = 1;
while (count < notraw.length) {
amount = notraw[count][1] + " " + notraw[count][2]
insRow(notraw[count][0], amount)
console.log("Inserted Ingredient: " + notraw[count][0] + notraw[count][1])
count++;
}
}
}