Iterating over a multilevel array - javascript

I have an array of values that can be cycled through using the next/prev buttons:
Example:
var sav = [
"first item",
"second item",
"third item"
];
var box = document.getElementById('box');
var i = -1;
function next() {
i = i >= sav.length - 1 ? 0 : i + 1;
box.innerHTML = sav[i];
}
function prev() {
i = i > 0 ? i - 1 : sav.length - 1;
box.innerHTML = sav[i];
}
Previous
<div id="box"></div>
Next
Tell me please how to iterate over if the array is multidimensional?
I managed to make an example based on a one-dimensional array, but it does not work in the case of a multidimensional one

The next() and prev() functions just need a little logic to cycle two, dependent variables. The snippet below explains further:
var sav = [
["0-a", "0-b", "0-c"],
["1-a", "1-b", "1-c"],
["2-a", "2-b", "2-c"],
];
const box = document.getElementById('box');
let row = 0, col = 0;
box.innerHTML = sav[row][col];
function next() {
if (col === sav[row].length-1) {
// if the col is at its limit, reset...
col = 0;
// ...and advance the row. if we're at its limit, reset it, too
row = row === sav.length-1 ? 0 : row+1;
} else { // otherwise, just advance the col
col = col+1
}
box.innerHTML = sav[row][col]
}
function prev() {
// same as above, except 0 is the limit, and we subtract to "advance"
if (col === 0) {
col = sav[row].length-1;
row = row === 0 ? sav.length-1 : row-1;
} else {
col = col-1
}
box.innerHTML = sav[row][col]
}
Previous
<div id="box"></div>
Next

Related

JavaScript Custom Star Rating System

I want to create a star rating system that has 5 stars. You can not select a half star, only a whole one. I want to achieve the following: If the user clicks on the star, the cilcked one and the other before it should be activated, and if the user clicks on a lower star deactivate all the stars after the selected one.
Here is what I got so far: The user can select 4 stars out of five (on the fifth click I have a bug which should be solved).
PS: I am working with SVG images but it would be way too ugly to insert in so the [ ] are the empty stars (the default), and the [X] are the selected (active) stars.
Heres my code:
for (let i = 1; i <= 5; i++) { document.getElementById("w__stars").innerHTML += `<span class="r__icon">[ ]</span>`; }
var icon = document.getElementsByClassName("r__icon");
for (let i = 0; i < icon.length; i++) {
icon[i].addEventListener("click", function (e) { console.log("--");
for (let j = 0; j < i+1; j++) {
console.log("i: " +i); console.log("j: "+(j+1)); console.log("Rest: "+ (j+(5-(i+1))));
icon[j].innerHTML = `[X]`;
icon[i+(5-(i+1))].innerHTML = `[ ]`;
}
});
}
<div class="flex flex-row product-star-con" id="w__stars"></div>
Your method just needs a different approach. For instance that inner loop is unnecessary if you are to place this in there icon[j].innerHTML = '[X]'.. which can be placed just within the outer loop.
Also the unnecessary calculations are making the task seem harder than it actually is. And since this is a loop, the i variable will always have the highest value within the loop, since there is no break statement in there
The method below targets the next elements and previous elements relative to the one being clicked at the moment and applies the appropriate 'innerHTML' to them
// Function to get previous and next siblings of the target element
function getSiblings(element, type){
var arraySib = [];
if ( type == 'prev' ){
while ( element = element.previousSibling ){
arraySib.push(element);
}
} else if ( type == 'next' ) {
while ( element = element.nextSibling ){
arraySib.push(element);
}
}
return arraySib;
}
for (var i = 1; i <= 5; i++) { document.getElementById("w__stars").innerHTML += `<span class="r__icon">[ ]</span>`; }
var icon = document.getElementsByClassName("r__icon");
for (var i = 0; i < icon.length; i++) {
icon[i].addEventListener("click", function (e){
this.innerHTML = `[X]`;
var prev = getSiblings(this, 'prev')
var next = getSiblings(this, 'next')
// populate previous siblings
for(p = 0; p < prev.length; p++){
prev[p].innerHTML = `[X]`
}
// clear next siblings
for(n = 0; n < next.length; n++){
next[n].innerHTML = `[]`
}
});
}
<div class="flex flex-row product-star-con" id="w__stars"></div>
Another approach:
// Setting stars
const stars = [];
for (let i = 0; i <= 4; i++) {
stars.push({
active: false,
index: i
});
}
const renderStars = (parentElement, stars, activeContent, notActiveContent) => {
parentElement.innerHTML = '';
stars.forEach(({ active, index }) => {
parentElement.innerHTML += `
<span class="r__icon">${active ? activeContent : notActiveContent}</span>`;
});
Array.from(parentElement.querySelectorAll('.r__icon')).forEach((item, itemIndex) => {
const star = stars.find(({ index }) => index === itemIndex);
stars[star.index].element = item;
item.addEventListener('click', (e) => {
const itemElement = e.target;
const starIndex = stars.findIndex(({ element }) => element === itemElement);
if (starIndex === -1) {
return;
}
const toActive = stars[starIndex].active !== true;
stars = stars.map(star => {
if (toActive) {
// Set items before index to true, and after to false
if (star.index <= starIndex) {
return {
...star,
active: true
};
}
return {
...star,
active: false
};
} else {
// Set items after index to false, and before to true
if (star.index >= starIndex) {
return {
...star,
active: false
};
}
return {
...star,
active: true
};
}
});
renderStars(parentElement, stars, activeContent, notActiveContent);
});
});
};
const setupStars = (stars, activeContent, notActiveContent) => {
const parentElement = document.getElementById("w__stars");
if (!parentElement) {
return;
}
renderStars(parentElement, stars, activeContent, notActiveContent);
};
setupStars(stars, '[X]', '[ ]');
<div class="flex flex-row product-star-con" id="w__stars"></div>

How can I do actions when multiple divs are clicked ? (Javascript)

I'll try to explain : I have a lot of divs that I am using as buttons (they're switching to red when you click on it).
It's a ranking system (for a videogame). Each rank has three step (1-2-3).
The goal of this is that you can select your rank first, then select the desire rank.
After choosing this, the price is displayed. Im trying to do this function.
For example, you select the lowest rank possible, then your landing (1-2-3).
Then, you choose what is the rank that you want, so in total, there is 4 divs clicked.
Finally, the goal of it all is to show the price.
But im having troubles. If you guys have some ideas.
I know that this is not the good way to do it. The code below is an example (Silver 1 to Silver 2).
I'm sure that I can condense this but I don't know how (Because there is a lot of ranks and doing 50 functions is not a good idea I think).
I hope to have been clear and precise, thanks for your help.
//First part
var fer1 = 0;
var bronze1 = 0;
var argent1 = 0;
var or1 = 0;
var platine1 = 0;
var diamant1 = 0;
var immortel1 = 0;
var un1 = 0;
var deux1 = 0;
var trois1 = 0;
//Second part
var fer2 = 0;
var bronze2 = 0;
var argent2 = 0;
var or2 = 0;
var platine2 = 0;
var diamant2 = 0;
var immortel2 = 0;
var un2 = 0;
var deux2 = 0;
var trois2 = 0;
function P1() {
fer1 = 1;
bronze1 = 0;
argent1 = 0;
or1 = 0;
platine1 = 0;
diamant1 = 0;
immortel1 = 0;
if ((fer1 == 1 && fer2 == 1 && un1 == 1 && deux2 == 1)) {
document.getElementById('total').innerHTML = "100€"
} else {
document.getElementById('total').innerHTML = "0€"
}
}
function P8() {
un1 = 1;
deux1 = 0;
trois1 = 0;
if ((fer1 == 1 && fer2 == 1 && un1 == 1 && deux2 == 1)) {
document.getElementById('total').innerHTML = "100€"
} else {
document.getElementById('total').innerHTML = "0€"
}
}
function P11() {
fer2 = 1;
bronze2 = 0;
argent2 = 0;
or2 = 0;
platine2 = 0;
diamant2 = 0;
immortel2 = 0;
if ((fer1 == 1 && fer2 == 1 && un1 == 1 && deux2 == 1)) {
document.getElementById('total').innerHTML = "100€"
} else {
document.getElementById('total').innerHTML = "0€"
}
}
function P99() {
un2 = 0;
deux2 = 1;
trois2 = 0;
if ((fer1 == 1 && fer2 == 1 && un1 == 1 && deux2 == 1)) {
document.getElementById('total').innerHTML = "100€"
} else {
document.getElementById('total').innerHTML = "0€"
}
}
HTML :
<div class="selection2">
<h1 class="unselectable policeOA policeRS" style="background-color:black; color:white;"> VOTRE RANG SOUHAITÉ</h1>
<div class="SousSelection1">
<div class="contenu-grille">
<div class="foc">
<div class="PetitCarre customClass" id="pc11">
<img class="rankss unselectable" src="http://localhost:8080/fullvaloAZ\FullValoCreated\HTML\esports\build\assets\images\Rank\Silver.png"/>
<img id="s1" class="unselectable slash1" src="http://localhost:8080/fullvaloAZ\FullValoCreated\HTML\esports\build\assets\images\All\Slashwhite.png"/>
</div>
</div>
<div class="foc">
<div class="PetitCarre" id="pc22">
<img class="rankss unselectable" src="http://localhost:8080/fullvaloAZ\FullValoCreated\HTML\esports\build\assets\images\Rank\Bronze.png"/>
<img id="s2" class="unselectable slash2" src="http://localhost:8080/fullvaloAZ\FullValoCreated\HTML\esports\build\assets\images\All\Slashwhite.png"/>
</div>
</div>
<div class="foc">
<div class="PetitCarre" id="pc33">
<img class="rankss unselectable" src="http://localhost:8080/fullvaloAZ\FullValoCreated\HTML\esports\build\assets\images\Rank\Argent.png"/>
<img id="s3" class="unselectable slash3" src="http://localhost:8080/fullvaloAZ\FullValoCreated\HTML\esports\build\assets\images\All\Slashwhite.png"/>
</div>
I think you should give the divs the same class name the select them using querySelectorAll then create forEach loop on them add click event listener for them inside the event check for the current rank and div rank then return the required price also insted of using fer1 fer2 use [fer1,fer2] it would be much better. if you could provide the html maybe I could help more
UPDATE
you can modify this as it fit your project
// ranks variable will hold all the ranks
// currentRank will hold the user current rank if user have current rank
const ranks = document.querySelectorAll(".foc");
const currentRank = document.getElementById("");
const total = document.getElementById("total");
let upgradePrice = 0;
// this will loop over every rank div you have
ranks.forEach(rank=>{
// then we will add event listener for mouse clicks
rank.addEventListener("click",event=>{
// here you will write the upgrade logic like checking the clicked div id
// so you can know which rank is this and if you need to do any
// validation you can do it here then after you finish you can
// add the upgrade cost to upgradePrice variable so you show it to user
// and be able to use it in another part in your code if needed
// also if you want to get id or class of any element in the rank div
// or the parent element of the div you can use `event`
// you can search for the propertys you can use with `event.target` in google
// you can get the id of the div or the id of any element inside it use event argument
// const rankId = event.target.id <- this the div id
upgradePrice+=100
// after you finish you can change the div style to using `event.target.style`
// event.target.style.background = "red"; <- to mark the div as selected
total.innerHTML = `${upgradePrice}€`
})
})

Why does arrow key navigation returns the wrong value when reversing direction?

I am trying to create a list navigation controlled by the arrow keys, using the keydown event. Selection works as expected until the user reverses direction. For example, if the user is on item 3 and hits the down arrow, it goes to item 4. However, if the user then hits the up arrow, instead of going back to item 3, it proceeds to item 5. Subsequent hits of the up arrow behave as expected. Only when changing direction does the first key press produces incorrect results.
Can anyone tell me why this is?
function keyListener() {
"use strict";
let index = 1;
document
.querySelector(".select__element")
.addEventListener("keydown", (e) => {
let keyValue = e.key;
// ****************************** ARROW UP
if (keyValue === "ArrowUp") {
console.log("Arrow Up Pressed");
const selectItem = document.querySelectorAll(".select__item");
const length = selectItem.length - 1;
// dropdown the list
document
.querySelector(".select__list")
.classList.add("select__list--visible");
let indexPrev = index - 1;
if (indexPrev < 0) {
indexPrev = length;
}
let indexNext = index + 1;
if (indexNext > length) {
indexNext = 0;
}
// test the bounds
if (index < 0) {
index = length;
indexPrev = length - 1;
indexNext = 0;
}
if (index > length) {
index = 0;
indexPrev = index + 1;
indexNext = length;
}
console.log(
"Up Arrow: ",
indexPrev,
index,
indexNext,
"Values should decrease"
);
// select the item
selectItem[index].classList.add("select__item--selected");
selectItem[indexPrev].classList.remove("select__item--selected");
selectItem[indexNext].classList.remove("select__item--selected");
index--;
}
// ****************************** ARROW DOWN
if (keyValue === "ArrowDown") {
console.log("Arrow Down Pressed");
const selectItem = document.querySelectorAll(".select__item");
const length = selectItem.length - 1;
// dropdown the list
document
.querySelector(".select__list")
.classList.add("select__list--visible");
let indexPrev = index - 1;
if (indexPrev < 0) {
indexPrev = length;
}
let indexNext = index + 1;
if (indexNext > length) {
indexNext = 0;
}
// test the bounds
if (index < 0) {
index = length;
indexPrev = 0;
indexNext = length - 1;
}
if (index > length) {
index = 0;
indexPrev = length;
indexNext = index + 1;
}
console.log(
"Down Arrow: ",
indexPrev,
index,
indexNext,
"Values should increase"
);
//select the item
selectItem[index].classList.add("select__item--selected");
selectItem[indexPrev].classList.remove("select__item--selected");
selectItem[indexNext].classList.remove("select__item--selected");
index++;
}
});
}
keyListener();
.select__item--selected,
.select__item:hover {
background: #25A0DA;
color: #fff;
}
.select__root {
background-color: lightpink;
}
<div class="m-wrapper">
<div id="Select-Pages" class="select">
<div class="select__element" tabindex="0">
<div class="select__root">Select an item...</div>
<ul class="select__list">
<li class="select__item select__item--selected">Item-1</li>
<li class="select__item">Item-2</li>
<li class="select__item">Item-3</li>
<li class="select__item">Item-4</li>
<li class="select__item">Item-5</li>
<li class="select__item">Item-6</li>
</ul>
</div>
</div>
The index variable is incremented and decremented after class lists are modified. As a result, highlighting appears out of sync with the selected index. I suggest updating the variable before the class lists, as shown below.
Also, since selectItem array indices start with zero, I recommend defining index = 0.
function keyListener() {
"use strict";
const selectItem = document.querySelectorAll(".select__item");
const length = selectItem.length;
let index = 0;
document
.querySelector(".select__element")
.addEventListener("keydown", (e) => {
let keyValue = e.key;
// store last index
let lastIndex = index;
// decrement / increment index
if (keyValue === "ArrowUp") {
index--;
}
if (keyValue === "ArrowDown") {
index++;
}
// keep index within range
index = (index + length) % length;
// deselect last item
selectItem[lastIndex].classList.remove("select__item--selected");
// select current item
selectItem[index].classList.add("select__item--selected");
});
}
keyListener();
.select__item--selected,
.select__item:hover {
background: #25A0DA;
color: #fff;
}
.select__root {
background-color: lightpink;
}
<div id="Select-Pages" class="select">
<div class="select__element" tabindex="0">
<div class="select__root">Select an item...</div>
<ul class="select__list">
<li class="select__item select__item--selected">Item-1</li>
<li class="select__item">Item-2</li>
<li class="select__item">Item-3</li>
<li class="select__item">Item-4</li>
<li class="select__item">Item-5</li>
<li class="select__item">Item-6</li>
</ul>
</div>
</div>

Detecting 5 in a row

I am making a game of tic tac toe 5 in a row. I have the grid where whenever you click on a square, it records a "coordinate" of [row,column] in the certain color of the dot. I'm currently not sure how to use the 'coordinates' to detect a five in a row of either color and just prints out a message.
Note: If 5 in a row gets tedious with the copy and pasting of previous code or such, a 3 in a row will also work for me and I will just modify it into a 5 in a row. Also when viewing the code snippet below, use the full screen mode.
Code I have so far:
var white=true;
function generateGrid( rows, cols ) {
var grid = "<table>";
for ( row = 1; row <= rows; row++ ) {
grid += "<tr>";
for ( col = 1; col <= cols; col++ ) {
grid += "<td></td>";
}
grid += "</tr>";
}
return grid;
}
$( "#tableContainer" ).append( generateGrid( 10, 10) );
$( "td" ).click(function() {
$(this).css('cursor','default');
var index = $( "td" ).index( this );
var row = Math.floor( ( index ) / 10) + 1;
var col = ( index % 10) + 1;
var $td = $(this);
if ($td.data('clicked'))
return;
if (white===true){
var whi=[row,col];
console.log("white coord is "+whi);
} else {
var bla=[row,col];
console.log("black coord is "+bla);
}
$td.data('clicked', true);
$td.css('background-color', white ? 'white' : 'black');
white = !white;
});
html{
background-color:#7189ea;
}
td {
border: 1px solid;
width: 25px;
height: 25px;
border-radius:100%;
}
table {
border-collapse: collapse;
}
<link type="text/css" rel="stylesheet" href="stylesheet.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div id="tableContainer"></div>
I've written a function that checks whether the last move wins the game. It basically loops the squares in every direction (and backwards) and looks for 5 in a row (the required length of a line).
var board = new Array();
var boardSize = 5;
var requiredLineLength = 5;
for (var r = 0; r < boardSize; r++) {
board[r] = new Array();
for (var c = 0; c < boardSize; c++) {
board[r][c] = 0;
}
}
var lineDirections = [
[0, 1], //horizontal
[1, 0], //vertical
[1, -1], //diagonal 1
[1, 1] //diagonal 2
];
//example usage:
board[0][0] = 1;
board[1][0] = 1;
board[2][0] = 1;
board[3][0] = 1;
board[4][0] = 1;
console.log(checkWin(1, [0, 0]));
// an empty square is marked with 0
// the players are marked with 1 and 2
// pl is the id of the player: either 1 or 2
// lastMove is an array of size 2, with the coordinates of the last move played, for example: [3, 1]
function checkWin(pl, lastMove) {
var boolWon = false;
for (var i = 0; i < lineDirections.length && !boolWon; i++) {
var shift = lineDirections[i];
var currentSquare = [lastMove[0] + shift[0], lastMove[1] + shift[1]];
var lineLength = 1;
while (lineLength < requiredLineLength && legalSquare(currentSquare) && board[currentSquare[0]][currentSquare[1]] === pl) {
lineLength++;
currentSquare[0] += shift[0];
currentSquare[1] += shift[1];
}
currentSquare = [lastMove[0] - shift[0], lastMove[1] - shift[1]];
while (lineLength < requiredLineLength && legalSquare(currentSquare) && board[currentSquare[0]][currentSquare[1]] === pl) {
lineLength++;
currentSquare[0] -= shift[0];
currentSquare[1] -= shift[1];
}
if (lineLength >= requiredLineLength)
boolWon = true;
}
return boolWon;
}
function legalSquare(square) {
return square[0] < boardSize && square[1] < boardSize && square[0] >= 0 && square[1] >= 0;
}
It's not fully tested so let me know if you encounter any problems or if you need any clarification on how this works.

BookYourSeat: Automatic Seat Selection on Click using AngularJS [GIF]

BookyourSeat:
It is an angularJs app that helps you book your seats for a movie show similar to bookmyshow .
]
What the User Can Do (User Cases)?
Select and Deselect the Seats with respect to the selectedVal, i.e if the
selectedVal = 4 then the user can select only 4 seats in total.
if the SelectedVal is less than 1 then the user should not be able
to select the seat anymore unless the user deselect any of the
previously selected seats and select again.
Booked Seats Case: If the check value of a seat is true, then the
user should not be able to select or deselect that seat(a.blocked
CSS rule is Added for that purpose) since it is already selected by
another user(Lets assume).
Automatic Seat Selection Cases
As shown in the GIF
If the user selects 3 seats and click on the first seat in the first row it should automatically select 2 and 3 on the same row.
If the user Selects 3 seats and clicks on the second last seat in the row then last two seats should be filled and the remaining seat should be filled where ever the user clicks.
If the user selects 3 seats and clicks on only the last seat then only that seat should be filled.
In case of 4 seats.
Problem:
I am able to achieve the automatic selection process using angular.forEach() but cannot all the logics correctly.
$scope.execute = function(i, j, itemVal, itemLetter) {
angular.forEach($scope.obj, function(v, k) {
if (v[i].val == itemVal && v[i].letter == itemLetter) {
if (v[i].seat == true || ($scope.isDisabled && v[i].check == false)) {
return;
}
v[i].check = !v[i].check;
if (v[i].check)
$scope.selectedVal -= 1;
else
$scope.selectedVal += 1;
//seatSelection
var m = i;
for (var l = 0; l < $scope.selectedVal; l++)
v[m++].check = true;
//seatSelectionEnd
console.log(itemVal + " " + itemLetter);
if ($scope.selectedVal < 1) {
$scope.isDisabled = true;
} else {
$scope.isDisabled = false;
}
}
});
};
}])
Working Fiddle: https://jsfiddle.net/rittamdebnath/5vqxgtq3/11/
Your looping logic is a bit difficult to follow, but more critically, unnecessary. Instead of a click function that loops through the whole collection, have it just interact directly with the object bound to the element being clicked.
Change your anchor element binding to this: ng-click="clickSeat(item)", and use a function on the controller like this:
$scope.clickSeat = function(seat) {
if (!seat.seat && !$scope.isDisabled) {
if (seat.check) {
seat.check = false;
$scope.selectedSeatCount--;
} else if ($scope.selectedSeatCount < $scope.selectedVal) {
seat.check = true;
$scope.selectedSeatCount++;
}
}
}
Here's an updated fiddle: https://jsfiddle.net/5vqxgtq3/12/
I don't know if this captures all the functionality you're looking for, but hopefully effectively demonstrates how the logic is easier to reason when it's no longer relying on a loop, and you can expand upon it from there.
I've changed your code with the following points to implement the logic:
changed model to 2d-Array style. seats = [ [Obj0, Obj1, Obj2], [Obj3, Obj4, Obj5] ]
calculated distance to border = how many seats to select for current row. Then we can calculate the rest of the available seats for current count.
added rang property to create the sections for different row groups.
I think I've implemented all your use cases.
Please have a look at the demo below or at this fiddle.
My code is pretty complicated as well but I think the loops are required because of the updating of the other selections.
angular.module('bookYourSeatApp', [])
.factory('seats', SeatsFactory)
.controller('mainCtrl', MainCtrl);
function SeatsFactory($rootScope, $timeout) {
var seatProps = {
id: 0,
caption: 0,
checked: false,
booked: false
};
var seats = {
'firstRang': {
// col0 1 2 3 4 5
// row 0 seat 0 1 2 3 4 5
// row 1 seat 6 7 8 9 10 11
seats: createSeats(2, 6) // rows, cols
},
'secondRang': {
seats: createSeats(3, 6)
}
};
function createSeats(rows, cols) {
var arr = [[]];
var seatIndex = 0;
for (var row = 0; row < rows; row++) {
arr[row] = [];
for(var col=0; col < cols; col++) {
var seat = angular.extend({}, seatProps, {
id: seatIndex,
caption: seatIndex,
booked: seatIndex < 5 // 0 to 5 booked
});
arr[row][col] = seat;
seatIndex++;
}
}
return arr;
}
function checkSelected(newCount) {
// selected fewer or more than persons in select.
// --> uncheck all
var checkedCount=0, keys = Object.keys(seats);
for (var rang=0; rang < keys.length; rang++) {
var key = keys[rang];
var curSeats = seats[key].seats;
for (var row=0; row < curSeats.length; row++) {
for (var col=0; col < curSeats[row].length; col++) {
if ( curSeats[row][col].checked ) {
checkedCount++;
}
}
}
//console.log('new count', newCount, checkedCount);
// we can have more or less selections after selection change
// --> more inc availCount
if (checkedCount === 0) {
// nothing selected
factory.availCount = angular.copy(newCount);
}
else if (newCount.val > checkedCount) {
//console.log('add delta', newCount, checkedCount)
factory.availCount.val = (newCount.val - checkedCount);
} else {
removeAllCheck();
}
}
}
function removeCheck(rang) {
// later pass user to this function (for now remove all checked)
/*var curSeats = seats[rang].seats
for (var row=0; row < curSeats.length; row++) {
for (var col=0; col < curSeats[row].length; col++) {
curSeats[row][col].checked = false;
}
}*/
keys = Object.keys(seats);
for (var rang=0; rang < keys.length; rang++) {
var key = keys[rang];
var curSeats = seats[key].seats;
for (var row=0; row < curSeats.length; row++) {
for (var col=0; col < curSeats[row].length; col++) {
curSeats[row][col].checked = false;
}
}
}
}
function removeAllCheck() {
keys = Object.keys(seats);
for (var rang=0; rang < keys.length; rang++) {
var key = keys[rang];
var curSeats = seats[key].seats;
for (var row=0; row < curSeats.length; row++) {
for (var col=0; col < curSeats[row].length; col++) {
curSeats[row][col].checked = false;
}
}
}
}
function selectSeats(selection, count) {
// todo:
// check distance to border, keep the rest as clickable
// selection = {rang, row, seat}
console.log(selection);
var row = selection.row,
seat = selection.seat;
if ( !seat.booked ) {
//console.log('availCount', factory.availCount);
if ( factory.availCount.val == 0 ) {
//console.log('new selection');
factory.availCount = angular.copy(count);
removeCheck(); //selection.rang);
}
var borderDistance = row.length - row.indexOf(seat),
rest = borderDistance > count.val ? 0: count.val - borderDistance;
if ( factory.availCount.val === count.val) {
// first click
var lastIndex = rest > 0 ? row.length: row.indexOf(seat) + count.val;
for ( var seatIndex = row.indexOf(seat); seatIndex < lastIndex; seatIndex++) {
row[seatIndex].checked = true;
}
factory.availCount.val = rest; // update available seats
}
else {
// second click dec. availCounter
// single change of seats
/*if ( factory.availCount.val < 0 ) {
row[row.indexOf(seat)].checked = false; // remove check
factory.availCount.val++;
}
else {*/
if ( !row[row.indexOf(seat)].checked ) {
// only if not already checked
row[row.indexOf(seat)].checked = true;
if ( factory.availCount.val > 0 ) {
factory.availCount.val--;
}
}
//}
}
}
}
var factory = {
map: seats,
select: selectSeats,
availCount: {},
setAvailCount: function(count) {
console.log('avail', count);
checkSelected(count);
}
};
return factory
}
function MainCtrl(seats) {
var vm = this;
angular.extend(vm, {
seats: seats,
selectionCount: [//[0,1,2,3,4],[
{id: 0, val: 0}, // object for two-way binding
{id: 1, val: 1},
{id: 2, val: 2},
{id: 3, val: 3},
{id: 4, val: 4},
],
selectedCount: 0
});
vm.selectedCount = vm.selectionCount[2];
seats.setAvailCount(vm.selectedCount);
}
table {
border: 1px solid black;
padding: 0.5em;
}
td {
padding: 1em;
border: 2px solid gray;
}
td:hover {
cursor: default;
background-color: gray;
}
.active {
border: 2px solid lightgreen;
border-radius: 5px;
}
.booked {
background-color: lightgray;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="bookYourSeatApp" ng-controller="mainCtrl as ctrl">
<label>Persons <select ng-model="ctrl.selectedCount" ng-change="ctrl.seats.setAvailCount(ctrl.selectedCount)" ng-options="count as count.val for count in ctrl.selectionCount"></select></label>
Seats left: {{ctrl.seats.availCount.val}}<br/>
<table ng-repeat="(key, rang) in ctrl.seats.map">
<tr ng-repeat="row in rang.seats">
<td ng-repeat="seat in row" ng-class="{'active': seat.checked, 'booked': seat.booked}" ng-click="ctrl.seats.select({rang:key, row:row, seat: seat}, ctrl.selectedCount)">
{{seat.caption}}
</td>
</tr>
</table>
<pre>{{ctrl.seats.map | json : 2}}</pre>
</div>

Categories