--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="180" height="180">
+ <!-- TODO -->
+</svg>
case "PlayCard":
this.chat.append(this.info_element(user_action.username, "Plays " + card_name(user_action.action.card)));
break;
+ case "Pass":
+ this.chat.append(this.info_element(user_action.username, "Passes"));
+ break;
+ case "Score":
+ this.chat.append(this.info_element(user_action.username, "Scores " + user_action.action.points + ": " + user_action.action.reason));
+ break;
case "CutCard":
this.chat.append(this.info_element(user_action.username, "Cuts " + card_name(user_action.action.card)));
break;
case "WinCall":
this.chat.append(this.info_element(user_action.username, "Wins the call"));
break;
+ case "PutInBox":
+ if (user_action.action.card === null) {
+ this.chat.append(this.info_element(user_action.username, "Puts a card in the box"));
+ } else {
+ this.chat.append(this.info_element(user_action.username, "Puts " + card_name(user_action.action.card) + " in the box"));
+ }
+ break;
case "WinHand":
this.chat.append(this.info_element(
user_action.username,
--- /dev/null
+import { create_svg_element } from "./svg.js";
+import { card_href } from "./card.js";
+import { GameWithChat } from "./gamechat.js";
+import { CongratulateWinner } from "./winner.js";
+
+export class Cribbage extends GameWithChat {
+ constructor(container, summary, actions, username, send, close) {
+ super(container, summary, actions, username, send, close);
+ this.svg.classList.add("cribbage");
+
+ this.user_icons = new Map();
+ this.hands = new Map();
+ this.played = new Map();
+ this.scores = new Map();
+ this.community = [];
+ this.box = [];
+
+ this.active = null;
+ this.dealer = null;
+
+ this.pass_control = create_svg_element(this.svg, "rect", ["pass-control", "active"], [["x", "400"], ["y", "400"], ["width", "120"], ["height", "50"], ["rx", "10"]]);
+ this.pass_control.onclick = () => {
+ this.send({type: "TakeAction", action: {action: "Pass"}});
+ };
+ this.pass_control_label = create_svg_element(this.svg, "text", ["pass-control-label"], [["x", "460"], ["y", "430"]]);
+ this.pass_control_text = document.createTextNode("Pass");
+ this.pass_control_label.append(this.pass_control_text);
+
+ this.take_initial_actions(actions);
+ }
+
+ redraw_players() {
+ const active_player = this.active;
+ for (const [username, [icon, score, active]] of this.user_icons) {
+ if (!this.seats.has(username)) {
+ this.svg.removeChild(icon);
+ this.svg.removeChild(score);
+ this.svg.removeChild(active);
+ this.user_icons.delete(username);
+ } else {
+ const seat = this.seats.get(username);
+ const angle = this.player_angle(seat);
+ const x = 240 - 180 * Math.sin(angle);
+ const y = 250 + 120 * Math.cos(angle) + 50 * Math.sign(Math.cos(angle));
+ icon.setAttribute("x", x);
+ icon.setAttribute("y", y);
+ score.childNodes[0].nodeValue = this.scores.get(username);
+ score.setAttribute("x", 240 - 120 * Math.sin(angle));
+ score.setAttribute("y", 250 + 70 * Math.cos(angle));
+ active.classList.toggle("active", active_player === username);
+ active.setAttribute("cx", x - 10);
+ active.setAttribute("cy", y - 5);
+ }
+ }
+ for (const [username, seat] of this.seats) {
+ if (!this.user_icons.has(username)) {
+ const angle = this.player_angle(seat);
+ const x = 240 - 180 * Math.sin(angle);
+ const y = 250 + 120 * Math.cos(angle) + 50 * (Math.sign(Math.cos(angle)) || 1);
+
+ const icon = create_svg_element(this.svg, "text", [], [["x", x], ["y", y]]);
+ icon.append(document.createTextNode(username));
+
+ const score = create_svg_element(this.svg, "text", [], [["x", 240 - 120 * Math.sin(angle)], ["y", 250 + 70 * Math.cos(angle)]]);
+ score.append(document.createTextNode(this.scores.get(username) || ""));
+
+ const active = create_svg_element(this.svg, "circle", ["active-indicator"], [["cx", x - 10], ["cy", y - 5], ["r", 5]]);
+ active.classList.toggle("active", active_player === username);
+
+ this.user_icons.set(username, [icon, score, active]);
+ }
+ }
+ }
+
+ card_image(username, card) {
+ const image = create_svg_element(this.svg, card === null ? "image" : "use", [], [["width", "45"], ["height", "70"], ["href", card_href(card)]]);
+ if (username === this.username) {
+ image.onclick = () => this.send({
+ type: "TakeAction",
+ action: {
+ action: this.community.length === 0 ? "PutInBox" : "PlayCard",
+ card: card
+ }
+ });
+ }
+ return image;
+ }
+
+ redraw_cards() {
+ for (const [username, cards] of this.hands) {
+ const seat = this.seats.get(username);
+ const angle = this.player_angle(seat);
+ const offset = cards.length * 10;
+ let x = 227.5 + offset - 180 * Math.sin(angle);
+ const y = 210 + 120 * Math.cos(angle);
+ for (const {card, image} of cards) {
+ image.classList.toggle("my-card", username === this.username);
+ image.setAttribute("x", x);
+ image.setAttribute("y", y);
+ x -= 20;
+ }
+ }
+ for (const [username, cards] of this.played) {
+ const seat = this.seats.get(username);
+ const angle = this.player_angle(seat);
+ const offset = cards.length * 10;
+ const x = 227.5 + offset - 90 * Math.sin(angle);
+ const y = 210 + 30 * Math.cos(angle);
+ let adjust = 0;
+ for (const {card, image} of cards) {
+ image.classList.toggle("my-card", username === this.username);
+ image.setAttribute("x", x + 10 * Math.sin(adjust));
+ image.setAttribute("y", y + 10 * Math.cos(adjust));
+ adjust += 77;
+ }
+ }
+ let x = 120.0;
+ for (const {card, image} of this.community) {
+ const y = 210;
+ image.setAttribute("x", x);
+ image.setAttribute("y", y);
+ x -= 20;
+ }
+ x = 400.0;
+ for (const {card, image} of this.box) {
+ const y = 210;
+ image.setAttribute("x", x);
+ image.setAttribute("y", y);
+ x -= 20;
+ }
+ }
+
+ remove_card(username, card) {
+ const hand = this.hands.get(username);
+ const card_index = hand.findIndex(c => c.card !== null && c.card.suit === card.suit && c.card.rank === card.rank);
+ const null_index = hand.findIndex(c => c.card === null);
+ if (card_index !== -1) {
+ return hand.splice(card_index, 1)[0];
+ } else if (null_index !== -1) {
+ return hand.splice(null_index, 1)[0];
+ } else {
+ return undefined;
+ }
+ }
+
+ reveal_card(username, card) {
+ const hand = this.hands.get(username);
+ const found_card = hand.find(c => c.card !== null && c.card.suit === card.suit && c.card.rank === card.rank) || hand.find(c => c.card === null);
+ const found_card_box = this.box.find(c => c.card !== null && c.card.suit === card.suit && c.card.rank === card.rank) || this.box.find(c => c.card === null);
+ if (found_card !== undefined) {
+ this.svg.removeChild(found_card.image);
+ found_card.card = card;
+ found_card.image = this.card_image(username, card);
+ } else if (found_card_box !== undefined) {
+ this.svg.removeChild(found_card_box.image);
+ found_card_box.card = card;
+ found_card_box.image = this.card_image(null, card);
+ }
+ }
+
+ take_action(user_action) {
+ super.take_action(user_action);
+ switch (user_action.action.action) {
+ case "Message":
+ break;
+ case "Join":
+ this.hands.set(user_action.username, []);
+ this.scores.set(user_action.username, 0);
+ this.redraw_players();
+ break;
+ case "Leave":
+ this.hands.delete(user_action.username);
+ this.scores.delete(user_action.username);
+ this.redraw_players();
+ break;
+ case "NextToDeal":
+ this.dealer = user_action.username;
+ for (const card of this.community) {
+ this.svg.removeChild(card.image);
+ }
+ for (const card of this.box) {
+ this.svg.removeChild(card.image);
+ }
+ this.community = [];
+ this.box = [];
+ for (const [username, hand] of this.hands) {
+ for (const card of hand) {
+ this.svg.removeChild(card.image);
+ }
+ }
+ this.hands.clear();
+ this.active = this.player_after(user_action.username);
+ this.redraw_players();
+ break;
+ case "ReceiveCard":
+ const card = {
+ card: user_action.action.card,
+ image: this.card_image(user_action.username, user_action.action.card),
+ };
+ if (!this.hands.has(user_action.username)) {
+ this.hands.set(user_action.username, []);
+ }
+ this.hands.get(user_action.username).push(card);
+ this.redraw_cards();
+ break;
+ case "Score":
+ if (!this.scores.has(user_action.username)) {
+ this.scores.set(user_action.username, 0);
+ }
+ this.set_info_text(user_action.username + " scores " + user_action.action.points + ": " + user_action.action.reason);
+ this.scores.set(user_action.username, this.scores.get(user_action.username) + user_action.action.points);
+ if ([...this.hands.values()].every(hand => hand.length === 0)) {
+ this.hands = this.played;
+ this.played = new Map();
+ for (const [username, hand] of this.hands) {
+ for (const card of hand) {
+ this.svg.removeChild(card.image);
+ card.card = null;
+ card.image = this.card_image(username, null);
+ }
+ }
+ this.redraw_cards();
+ }
+ this.redraw_players();
+ break;
+ case "PlayCard":
+ const played = this.remove_card(user_action.username, user_action.action.card);
+ if (played !== undefined) {
+ this.svg.removeChild(played.image);
+ if (!this.played.has(user_action.username)) {
+ this.played.set(user_action.username, []);
+ }
+ this.played.get(user_action.username).push({
+ card: user_action.action.card,
+ image:this.card_image(user_action.username, user_action.action.card),
+ });
+ }
+ /* fallthrough */
+ case "Pass":
+ this.active = user_action.username;
+ do {
+ this.active = this.player_after(this.active);
+ } while (this.active !== user_action.username && this.hands.has(this.active) && this.hands.get(this.active).length === 0);
+ this.redraw_cards();
+ this.redraw_players();
+ break;
+ case "RevealCard":
+ this.reveal_card(user_action.username, user_action.action.card);
+ this.redraw_cards();
+ break;
+ case "PutInBox":
+ const removed = this.remove_card(user_action.username, user_action.action.card);
+ if (removed !== undefined) {
+ this.svg.removeChild(removed.image);
+ this.box.push({card: null, image: this.card_image(null, null)});
+ }
+ this.redraw_cards();
+ break;
+ case "CommunityCard":
+ const turnup = {
+ card: user_action.action.card,
+ image: this.card_image(null, user_action.action.card),
+ };
+ this.community.push(turnup);
+ this.active = this.player_after(this.dealer);
+ this.redraw_cards();
+ this.redraw_players();
+ break;
+ case "EndDeal":
+ this.active = user_action.username;
+ this.redraw_players();
+ break;
+ case "WinGame":
+ this.active = null;
+ this.redraw_cards();
+ this.redraw_players();
+ new CongratulateWinner(this.svg, user_action.username);
+ break;
+ default:
+ console.error("Unhandled action for cribbage", user_action);
+ this.set_error_text("Unhandled action for cribbage: " + user_action.action.action);
+ break;
+ }
+ }
+}
--- /dev/null
+export class Game {
+ constructor(container, summary, actions, username, send, close) {
+ this.container = container;
+ this.summary = summary;
+ this.username = username;
+ this.send = send;
+ this.seats = new Map();
+ }
+
+ take_initial_actions(actions) {
+ for (const user_action of actions) {
+ this.take_action(user_action);
+ }
+
+ if (!this.seats.has(this.username)) {
+ this.send({type: "TakeAction", action: {action: "Join", seat: 0, chips: 0}});
+ }
+ }
+
+ player_angle(seat) {
+ const my_seat = this.seats.get(this.username) || 0;
+ const num_seats = Math.max(...this.seats.values()) + 1;
+ const rel_seat = my_seat > seat ? num_seats + seat - my_seat : seat - my_seat;
+ if (num_seats % 4 === 0) {
+ if (rel_seat < num_seats / 4) {
+ return rel_seat * 2 * Math.PI / (num_seats + 2);
+ } else if (rel_seat >= num_seats / 4 && rel_seat <= 3 * num_seats / 4) {
+ return (rel_seat + 1) * 2 * Math.PI / (num_seats + 2);
+ } else {
+ return (rel_seat + 2) * 2 * Math.PI / (num_seats + 2);
+ }
+ } else {
+ return rel_seat * 2 * Math.PI / num_seats;
+ }
+ }
+
+ player_after(username) {
+ const in_seat = this.seats.get(username);
+ let [player_after, player_after_seat] = [username, null];
+ for (const [username, seat] of this.seats) {
+ if (seat > in_seat && (player_after_seat === null || seat < player_after_seat)) {
+ player_after = username;
+ player_after_seat = seat;
+ }
+ }
+ if (player_after_seat === null) for (const [username, seat] of this.seats) {
+ if (player_after_seat === null || seat < player_after_seat) {
+ player_after = username;
+ player_after_seat = seat;
+ }
+ }
+ return player_after;
+ }
+
+
+ take_action(user_action) {
+ switch (user_action.action.action) {
+ case "Join":
+ this.seats.set(user_action.username, user_action.action.seat);
+ break;
+ case "Leave":
+ this.seats.delete(user_action.username);
+ break;
+ default:
+ break;
+ }
+ }
+}
--- /dev/null
+import { create_svg_element } from "./svg.js";
+import { Game } from "./game.js";
+import { Chatroom } from "./chatroom.js";
+
+export class GameWithChat extends Game {
+ constructor(container, summary, actions, username, send, close) {
+ super(container, summary, actions, username, send, close);
+
+ this.svg = create_svg_element(container, "svg", [], [["viewBox", "0 0 500 500"]]);
+ this.background = create_svg_element(this.svg, "rect", [], [["width", "500"], ["height", "500"], ["fill", "#404040"]]);
+
+ this.error_text_timeout = null;
+ this.error = create_svg_element(this.svg, "text", ["game-error"], [["x", "20"], ["y", "450"]]);
+ this.error_lines = [];
+ this.error_text = [];
+ const num_error_lines = 2;
+ for (let i = 0; i < num_error_lines; i++) {
+ const line = create_svg_element(this.error, "tspan", [], [["x", "20"], ["dy", "20"]]);
+ const text = document.createTextNode("");
+ line.append(text);
+ this.error_lines.push(line);
+ this.error_text.push(text);
+ }
+
+ this.close_control = create_svg_element(this.svg, "rect", ["close-control"], [["x", "460"], ["y", "5"], ["width", "35"], ["height", "35"], ["rx", "2"]])
+ this.close_control.onclick = close;
+ this.close_image = create_svg_element(this.svg, "image", ["close-image"], [["x", "465"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/close.svg"]]);
+
+ this.chat_control = create_svg_element(this.svg, "rect", ["chat-control"], [["x", "420"], ["y", "5"], ["width", "35"], ["height", "35"], ["rx", "2"]])
+ this.chat_control.onclick = () => this.chatroom_container.classList.toggle("hidden");
+ this.chat_image = create_svg_element(this.svg, "image", ["chat-image"], [["x", "425"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/chat.svg"]]);
+
+ this.chatroom_container = document.createElement("div");
+ this.chatroom_container.classList.add("embedded-chatroom");
+ const send_chat_messages_only = message => {
+ if (message.type === "TakeAction" && message.action.action === "Message") {
+ send(message);
+ }
+ };
+ this.chatroom = new Chatroom(this.chatroom_container, summary, [], username, send_chat_messages_only, null);
+ this.container.append(this.chatroom_container);
+ }
+
+ set_text(style, text) {
+ this.error.setAttribute("class", style);
+ const lines = text.split("\n", this.error_text.length);
+ for (const text of this.error_text) {
+ text.textContent = "";
+ }
+ for (let i = 0; i < lines.length; i++) {
+ this.error_text[i + this.error_text.length - lines.length].textContent = lines[i];
+ }
+ if (this.error_text_timeout !== null) clearTimeout(this.error_text_timeout);
+ this.error_text_timeout = setTimeout(() => {
+ for (const text of this.error_text) {
+ text.textContent = "";
+ }
+ this.error_text_timeout = null;
+ }, 5000);
+ }
+
+ set_info_text(text) {
+ this.set_text("game-info", text);
+ }
+
+ set_error_text(text) {
+ this.set_text("game-error", text);
+ }
+
+ take_action(user_action) {
+ super.take_action(user_action);
+ this.chatroom.take_action(user_action);
+ }
+}
format_row.append(format_title);
const format_value = document.createElement("td");
const format_input = document.createElement("select");
- for (const format of ["KnockOutWhist", "TexasHoldEm", "Chatroom"]) {
+ for (const format of ["KnockOutWhist", "Cribbage", "TexasHoldEm", "Chatroom"]) {
const game_format = document.createElement("option");
game_format.setAttribute("value", format);
game_format.innerText = format;
table.append(format_row);
const fields = [
- { name: "start_time", display: "Start Time", value: "", type: "datetime", formats: ["TexasHoldEm", "KnockOutWhist"] },
- { name: "max_players", display: "Max Players", value: 2, type: "number", formats: ["TexasHoldEm", "KnockOutWhist"] },
+ { name: "start_time", display: "Start Time", value: "", type: "datetime", formats: ["TexasHoldEm", "KnockOutWhist", "Cribbage"] },
+ { name: "max_players", display: "Max Players", value: 2, type: "number", formats: ["TexasHoldEm", "KnockOutWhist", "Cribbage"] },
{ name: "small_blind", display: "Small Blind", value: 25, type: "number", formats: ["TexasHoldEm"] },
{ name: "starting_stack", display: "Starting Stack", value: 5000, step: 1000, type: "number", formats: ["TexasHoldEm"] },
{ name: "round_length", display: "Round Length", value: "", type: "duration", formats: ["TexasHoldEm"] },
{ name: "tournament_length", display: "Tournament Length", value: "", type: "duration", formats: ["TexasHoldEm"] },
{ name: "action_timeout", display: "Action Timeout", value: "", type: "duration", formats: ["TexasHoldEm"] },
+ { name: "target_score", display: "Target Score", value: 121, type: "number", formats: ["Cribbage"] },
];
for (const {name, display, value, type, step, formats} of fields) {
knock_out_whist.onclick = () => this.send({type: "JoinLobby", filter: "format: KnockOutWhist and created_in_last: 2:00:00"});
menu_container.append(knock_out_whist);
+ const cribbage = document.createElement("div");
+ cribbage.classList.add("game-format-icon");
+ const cribbage_img = document.createElement("img");
+ cribbage_img.setAttribute("src", "img/cribbage.svg");
+ const cribbage_text = document.createElement("p");
+ cribbage_text.innerText = "Cribbage";
+ cribbage.append(cribbage_img);
+ cribbage.append(cribbage_text);
+ cribbage.onclick = () => this.send({type: "JoinLobby", filter: "format: Cribbage and created_in_last: 2:00:00"});
+ menu_container.append(cribbage);
+
const chatroom = document.createElement("div");
chatroom.classList.add("game-format-icon");
const chatroom_img = document.createElement("img");
-const svgns = "http://www.w3.org/2000/svg";
-
+import { svgns, create_svg_element } from "./svg.js";
import { card_href, suit_href } from "./card.js";
import { Chatroom } from "./chatroom.js";
import { CongratulateWinner } from "./winner.js";
-function set_element_attributes(element, attributes) {
- for (const [attr, value] of attributes) {
- element.setAttribute(attr, value);
- }
-}
-
-function create_svg_element(parent, name, class_list, attributes) {
- const element = document.createElementNS(svgns, name);
- set_element_attributes(element, attributes);
- for (const cls of class_list) {
- element.classList.add(cls);
- }
- parent.append(element);
- return element;
-}
-
export class BetControls {
constructor(game) {
this.game = game
if (chips !== null && !isNaN(Number(chips))) {
game.send({type: "TakeAction", action: {action: "Bet", chips: Number(chips)}});
}
- }
+ };
this.bet_control_label = create_svg_element(this.game.svg, "text", ["bet-control-label"], [["x", "380"], ["y", "566"]]);
this.bet_control_text = document.createTextNode("Bet");
this.close_control = create_svg_element(this.svg, "rect", ["close-control"], [["x", "460"], ["y", "5"], ["width", "35"], ["height", "35"], ["rx", "2"]])
this.close_control.onclick = close;
- this.close_image = create_svg_element(this.svg, "image", ["close-image"], [["x", "465"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/close.svg"]])
+ this.close_image = create_svg_element(this.svg, "image", ["close-image"], [["x", "465"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/close.svg"]]);
this.chat_control = create_svg_element(this.svg, "rect", ["chat-control"], [["x", "420"], ["y", "5"], ["width", "35"], ["height", "35"], ["rx", "2"]])
this.chat_control.onclick = () => this.chatroom_container.classList.toggle("hidden");
- this.chat_image = create_svg_element(this.svg, "image", ["chat-image"], [["x", "425"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/chat.svg"]])
+ this.chat_image = create_svg_element(this.svg, "image", ["chat-image"], [["x", "425"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/chat.svg"]]);
this.chatroom_container = document.createElement("div");
this.chatroom_container.classList.add("embedded-chatroom");
-const svgns = "http://www.w3.org/2000/svg";
-
import { Auth } from "./auth.js";
import { MainMenu } from "./mainmenu.js";
import { GameList } from "./gamelist.js";
import { Chatroom } from "./chatroom.js";
+import { Cribbage } from "./cribbage.js";
import { KnockOutWhist } from "./whist.js";
import { TexasHoldEm } from "./poker.js";
this.send({type: "TakeAction", action: {action: "Join", seat: message.action.action.seat + 1, chips: message.action.action.chips}});
} else {
console.error("Taking action failed: " + message.reason, message.action);
+ console.error("This.game: ", this.game);
this.game.set_error_text(message.reason);
}
break;
return 1000;
case "RevealCard":
return 1000;
+ case "Score":
case "CutCard":
return 3000;
case "PlayCard":
let Format;
switch (this.game.summary.settings.format) {
case "Chatroom": Format = Chatroom; break;
+ case "Cribbage": Format = Cribbage; break;
case "KnockOutWhist": Format = KnockOutWhist; break;
case "TexasHoldEm": Format = TexasHoldEm; break;
default:
--- /dev/null
+export const svgns = "http://www.w3.org/2000/svg";
+
+function set_element_attributes(element, attributes) {
+ for (const [attr, value] of attributes) {
+ element.setAttribute(attr, value);
+ }
+}
+
+export function create_svg_element(parent, name, class_list, attributes) {
+ const element = document.createElementNS(svgns, name);
+ set_element_attributes(element, attributes);
+ for (const cls of class_list) {
+ element.classList.add(cls);
+ }
+ parent.append(element);
+ return element;
+}
-const svgns = "http://www.w3.org/2000/svg";
-
+import { svgns, create_svg_element } from "./svg.js";
import { card_href, suit_href } from "./card.js";
import { Chatroom } from "./chatroom.js";
import { CongratulateWinner } from "./winner.js";
-const svgns = "http://www.w3.org/2000/svg";
-
+import { svgns, create_svg_element } from "./svg.js";
import { card_href, FIFTY_TWO_CARD_DECK } from "./card.js";
import { random_int } from "./random.js";
@import url("style/game-list.css");
@import url("style/game.css");
@import url("style/chatroom.css");
+@import url("style/cribbage.css");
@import url("style/poker.css");
@import url("style/whist.css");
@import url("style/winner.css");
--- /dev/null
+.pass-control {
+ fill: #808080;
+ stroke: black;
+ stroke-width: 1px;
+ transition: fill 0.5s;
+ pointer-events: none;
+ cursor: pointer;
+}
+
+.pass-control.active {
+ fill: #8080ff;
+ pointer-events: auto;
+}
+
+.pass-control.active:hover {
+ fill: #a0a0ff;
+}
+
+.pass-control-label {
+ pointer-events: none;
+ text-anchor: middle;
+}
display: table-row;
}
+.game-popup > table.Cribbage tr.game-field {
+ display: none;
+}
+
+.game-popup > table.Cribbage tr.game-field.Cribbage {
+ display: table-row;
+}
+
.game-popup input, .game-popup select {
width: 100%;
height: 100%;